GNOME Bugzilla – Bug 788101
gtk_text_view_scroll_to_iter() does nothing
Last modified: 2018-05-02 19:08:39 UTC
code fragment: gtk_text_buffer_select_range(textBuff,&iter1,&iter2); gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(widget),&iter2,0,0,1,1); The select range works correctly but the selected text is below the bottom edge of a scrolling text widget. The scroll request does nothing. I have played with the four numerical args to no avail. I also tried making a GtkTextMark and scrolling to that, but that also does nothing. No errors are produced. The text widget scrolls OK using the mouse wheel or scroll bars. What can be behind this? I looked for a forum but there seems to be nothing. If there is, please tell me.
Could you provide a minimal self-contained testcase?
I wrote the test program which is copied below. Here is what I found out: 1. If the container hierarchy is scroll-window, frame, text-view, then scrolling with the mouse wheel or scroll bars works, but programmatic scrolling (scroll to iter or mark) does not work. 2. If the container hierarchy is scroll-window, text-view (no frame), then everything works OK. Test program and make file follow: ztest.cc -------------------------------------------------------------------------------- // test gtk_text_view for program-controlled scrolling #include <stdlib.h> #include <stdio.h> #include <string.h> #include <gtk/gtk.h> int eventfunc(GtkWidget *widget, GdkEvent *event); void textview_append(GtkWidget *widget, const char *text); char * textview_get_line(GtkWidget *widget, int line); int currline = 0; int Nlines = 100; int main(int argc, char *argv[]) { GtkWidget *reportwin, *scrollwin, *frame, *textview; char buff[100]; setenv("GDK_BACKEND","x11",1); setenv("GTK_THEME","default",0); gtk_init(&argc,&argv); reportwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(reportwin),"report window"); gtk_window_set_default_size(GTK_WINDOW(reportwin),500,300); gtk_window_set_position(GTK_WINDOW(reportwin),GTK_WIN_POS_CENTER_ON_PARENT); scrollwin = gtk_scrolled_window_new(0,0); gtk_container_add(GTK_CONTAINER(reportwin),scrollwin); //------------------------------------------------------------------------------- // frame = gtk_frame_new(0); // gtk_container_add(GTK_CONTAINER(scrollwin),frame); // option A FAILS // textview = gtk_text_view_new(); // gtk_container_add(GTK_CONTAINER(frame),textview); //------------------------------------------------------------------------------- textview = gtk_text_view_new(); // option B OK gtk_container_add(GTK_CONTAINER(scrollwin),textview); //------------------------------------------------------------------------------- gtk_text_view_set_editable(GTK_TEXT_VIEW(textview),0); gtk_widget_show_all(reportwin); g_signal_connect(G_OBJECT(reportwin),"destroy",gtk_main_quit,0); g_signal_connect(G_OBJECT(reportwin),"delete-event",gtk_main_quit,0); gtk_widget_add_events(textview,GDK_BUTTON_PRESS_MASK); gtk_widget_add_events(textview,GDK_KEY_PRESS_MASK); gtk_widget_add_events(textview,GDK_ENTER_NOTIFY_MASK); g_signal_connect(G_OBJECT(textview),"key-press-event",G_CALLBACK(eventfunc),0); g_signal_connect(G_OBJECT(textview),"button-press-event",G_CALLBACK(eventfunc),0); g_signal_connect(G_OBJECT(textview),"enter-notify-event",G_CALLBACK(eventfunc),0); for (int line = 0; line < Nlines; line++) { snprintf(buff,100,"%03d The quick brown fox jumps over the lazy dog.\n",line); textview_append(textview,buff); } gtk_main(); return 0; } int eventfunc(GtkWidget *widget, GdkEvent *event) { GdkDisplay *display; GdkWindow *gdkwin; static GdkCursor *arrowcursor = 0; GtkTextIter iter1; int mpx, mpy, tbx, tby, line, pos, kbkey; char *textline; #define TEXT GTK_TEXT_WINDOW_TEXT #define VIEW GTK_TEXT_VIEW #define ARROW GDK_TOP_LEFT_ARROW #define appfontsize 12 if (! arrowcursor) { gdkwin = gtk_widget_get_window(widget); display = gdk_window_get_display(gdkwin); arrowcursor = gdk_cursor_new_for_display(display,ARROW); } gdkwin = gtk_text_view_get_window(VIEW(widget),TEXT); if (gdkwin) gdk_window_set_cursor(gdkwin,arrowcursor); if (event->type == GDK_KEY_PRESS) { kbkey = ((GdkEventKey *) event)->keyval; printf("keyboard key %d \n",kbkey); if (kbkey == GDK_KEY_Up) currline--; if (kbkey == GDK_KEY_Down) currline++; if (currline < 0) currline = 0; if (currline > Nlines-1) currline = Nlines - 1; textline = textview_get_line(widget,currline); return 1; } if (event->type == GDK_BUTTON_PRESS) { mpx = int(((GdkEventButton *) event)->x); mpy = int(((GdkEventButton *) event)->y); mpx -= appfontsize / 2; if (mpx < 0) mpx = 0; gtk_text_view_window_to_buffer_coords(VIEW(widget),TEXT,mpx,mpy,&tbx,&tby); gtk_text_view_get_iter_at_location(VIEW(widget),&iter1,tbx,tby); line = gtk_text_iter_get_line(&iter1); pos = gtk_text_iter_get_line_offset(&iter1); printf("click on line %d position %d \n",line,pos); currline = line; textline = textview_get_line(widget,currline); if (textline) free(textline); return 1; } return 0; } void textview_append(GtkWidget *widget, const char *text) { GtkTextBuffer *textBuff; GtkTextIter enditer; GtkTextMark *endmark; GtkTextTag *fontag; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_get_end_iter(textBuff,&enditer); endmark = gtk_text_buffer_create_mark(textBuff,"endmark",&enditer,0); fontag = gtk_text_buffer_create_tag(textBuff,0,"font","mono 12",0); gtk_text_buffer_insert_with_tags(textBuff,&enditer,text,-1,fontag,NULL); gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(widget),endmark,0,0,1,1); while (gtk_events_pending()) gtk_main_iteration_do(0); return; } char * textview_get_line(GtkWidget *widget, int line) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int nlines; char *textline; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); nlines = gtk_text_buffer_get_line_count(textBuff); if (line < 0 || line >= nlines) return 0; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); iter2 = iter1; gtk_text_iter_forward_line(&iter2); textline = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); if (! textline) return 0; gtk_text_buffer_select_range(textBuff,&iter1,&iter2); gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(widget),&iter1,0,0,1,1); gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(widget),&iter2,0,0,1,1); return textline; } Makefile: -------------------------------------------------------------------------- CFLAGS = $(CXXFLAGS) -Wall -O0 -c `pkg-config --cflags gtk+-3.0` ztest: ztest.o $(CXX) $(LDFLAGS) -o ztest ztest.o `pkg-config --libs gtk+-3.0` ztest.o: ztest.cc $(CXX) $(CFLAGS) -o ztest.o ztest.cc
I'm pretty sure this is just because ScrolledWindow won't traverse >1 child to get scroll adjustments to sync to. When adding the TextView, ScrolledWindow.add() finds that it's a GtkScrollable, so the SW syncs its h/v adjustments with the TextView. Otherwise, when adding the Frame, or any other non-Scrollable, that gets wrapped in the autogenerated ViewPort, then the ScrolledWindow syncs its adjustments with those of the ViewPort, not the nested TextView. In this case, I can't see why you wouldn't just use Frame > ScrolledWindow > TextView anyway.
-- GitLab Migration Automatic Message -- This bug has been migrated to GNOME's GitLab instance and has been closed from further activity. You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/gtk/issues/918.