Reputation: 1512
I'm building a GUI which has a scrollable element, which holds an Textview. I'm using a Textbuffer to add Text to the textview; it works good but when the content gets to big, the scrollbar is staying in its place. I created my GUI with glade.
I read the docs but I couldn't find a method like scrollToBottom()
or anything else.
So how can I make sure that the scrollable is always at the bottom when new text is outputted?
Upvotes: 1
Views: 1796
Reputation: 958
To scroll in GtkTextView you have to use markers in GtkTextBuffer. GtkTextMark objects are auto-updating, so just create the marker once when you create the TextBuffer.
Here's how to do it:
C
GtkTextView *text_view;
GtkTextMark *text_mark_end;
void create_text_view () {
text_view = gtk_text_view_new ();
/* get the text buffer */
GtkTextBuffer *text_buffer;
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
/* create an auto-updating 'always at end' marker to scroll */
GtkTextIter text_iter_end;
gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end);
text_mark_end = gtk_text_buffer_create_mark (text_buffer,
NULL,
&text_iter_end,
FALSE);
}
void append_text(const gchar *text) {
if (text) {
/* get the text buffer */
GtkTextBuffer *text_buffer;
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
/* get an end iter */
GtkTextIter text_iter_end;
gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end);
/* append text */
gtk_text_buffer_insert (text_buffer, &text_iter_end);
/* now scroll to the end using marker */
gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view),
text_mark_end,
0., FALSE, 0., 0.);
}
}
void destroy_text_view () {
g_object_unref (text_mark_end);
gtk_widget_destroy (text_view);
}
Python
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class TextView(Gtk.TextView):
def __init__(self):
Gtk.TextView.__init__(self)
# create mark for scrolling
text_buffer = self.get_buffer()
text_iter_end = text_buffer.get_end_iter()
self.text_mark_end = text_buffer.create_mark("", text_iter_end, False)
def append_text(self, text):
# append text
text_buffer = self.get_buffer()
text_iter_end = text_buffer.get_end_iter()
text_buffer.insert(text_iter_end, text)
# now scroll using mark
self.scroll_to_mark(self.text_mark_end, 0, False, 0, 0)
class Window(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.set_title('Test GtkTextView scrolling')
self.set_default_size(400, 300)
self.grid = Gtk.Grid()
self.scrolled_win = Gtk.ScrolledWindow()
self.text_view = TextView()
self.button = Gtk.Button(label='Append text')
self.scrolled_win.set_hexpand(True)
self.scrolled_win.set_vexpand(True)
self.scrolled_win.add(self.text_view)
self.grid.add(self.scrolled_win)
self.grid.add(self.button)
self.add(self.grid)
self.connect('destroy', Gtk.main_quit)
self.button.connect('clicked', self.on_button_clicked)
self.show_all()
def on_button_clicked(self, widget):
self.text_view.append_text('Hello\n' * 5);
if __name__ == '__main__':
win = Window()
Gtk.main()
Note:
There is also a gtk_text_view_scroll_to_iter function. This is not really useful because GtkTextView uses an animated scrolling (the animated scrolling can be seen for example with gedit with PAGE up/down). Unlike marker based scrolling, iter based scrolling does not play well with the animated scrolling capability of GtkTextView:
https://developer.gnome.org/gtk3/stable/GtkTextView.html#gtk-text-view-scroll-to-iter
Note that this function uses the currently-computed height of the lines in the text buffer. Line heights are computed in an idle handler; so this function may not have the desired effect if it’s called before the height computations. To avoid oddness, consider using gtk_text_view_scroll_to_mark() which saves a point to be scrolled to after line validation.
Upvotes: 3