diff options
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | demos/gtk-demo/Makefile.am | 1 | ||||
-rw-r--r-- | demos/gtk-demo/textscroll.c | 202 | ||||
-rw-r--r-- | docs/reference/ChangeLog | 5 | ||||
-rw-r--r-- | docs/reference/gtk/question_index.sgml | 19 |
5 files changed, 223 insertions, 8 deletions
@@ -1,5 +1,9 @@ 2006-08-25 Matthias Clasen <mclasen@redhat.com> + * demos/gtk-demo/Makefile.am: + * demos/gtk-demo/textscroll.c: Add an example of automatic + scrolling, thanks to Yevgen Muntyan. (#351206) + * gtk/gtkmodules.c (find_module): Use local binding when loading modules. (#351868) diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index e1adb57b63..f043ee0f4e 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -31,6 +31,7 @@ demos = \ sizegroup.c \ stock_browser.c \ textview.c \ + textscroll.c \ tree_store.c \ ui_manager.c diff --git a/demos/gtk-demo/textscroll.c b/demos/gtk-demo/textscroll.c new file mode 100644 index 0000000000..063322141c --- /dev/null +++ b/demos/gtk-demo/textscroll.c @@ -0,0 +1,202 @@ +/* Text Widget/Automatic scrolling + * + * This example demonstrates how to use the gravity of + * GtkTextMarks to keep a text view scrolled to the bottom + * while appending text. + */ + +#include <gtk/gtk.h> +#include "demo-common.h" + +/* Scroll to the end of the buffer. + */ +static gboolean +scroll_to_end (GtkTextView *textview) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkTextMark *mark; + char *spaces; + static int count; + + buffer = gtk_text_view_get_buffer (textview); + + /* Get "end" mark. It's located at the end of buffer because + * of right gravity + */ + mark = gtk_text_buffer_get_mark (buffer, "end"); + gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark); + + /* and insert some text at its position, the iter will be + * revalidated after insertion to point to the end of inserted text + */ + spaces = g_strnfill (count++, ' '); + gtk_text_buffer_insert (buffer, &iter, "\n", -1); + gtk_text_buffer_insert (buffer, &iter, spaces, -1); + gtk_text_buffer_insert (buffer, &iter, + "Scroll to end scroll to end scroll " + "to end scroll to end ", + -1); + g_free (spaces); + + /* Now scroll the end mark onscreen. + */ + gtk_text_view_scroll_mark_onscreen (textview, mark); + + /* Emulate typewriter behavior, shift to the left if we + * are far enough to the right. + */ + if (count > 150) + count = 0; + + return TRUE; +} + +/* Scroll to the bottom of the buffer. + */ +static gboolean +scroll_to_bottom (GtkTextView *textview) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkTextMark *mark; + char *spaces; + static int count; + + buffer = gtk_text_view_get_buffer (textview); + + /* Get end iterator */ + gtk_text_buffer_get_end_iter (buffer, &iter); + + /* and insert some text at it, the iter will be revalidated + * after insertion to point to the end of inserted text + */ + spaces = g_strnfill (count++, ' '); + gtk_text_buffer_insert (buffer, &iter, "\n", -1); + gtk_text_buffer_insert (buffer, &iter, spaces, -1); + gtk_text_buffer_insert (buffer, &iter, + "Scroll to bottom scroll to bottom scroll " + "to bottom scroll to bottom", + -1); + g_free (spaces); + + /* Move the iterator to the beginning of line, so we don't scroll + * in horizontal direction + */ + gtk_text_iter_set_line_offset (&iter, 0); + + /* and place the mark at iter. the mark will stay there after we + * insert some text at the end because it has right gravity. + */ + mark = gtk_text_buffer_get_mark (buffer, "scroll"); + gtk_text_buffer_move_mark (buffer, mark, &iter); + + /* Scroll the mark onscreen. + */ + gtk_text_view_scroll_mark_onscreen (textview, mark); + + /* Shift text back if we got enough to the right. + */ + if (count > 40) + count = 0; + + return TRUE; +} + +static guint +setup_scroll (GtkTextView *textview, + gboolean to_end) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkTextMark *mark; + + buffer = gtk_text_view_get_buffer (textview); + gtk_text_buffer_get_end_iter (buffer, &iter); + + if (to_end) + { + /* If we want to scroll to the end, including horizontal scrolling, + * then we just create a mark with right gravity at the end of the + * buffer. It will stay at the end unless explicitely moved with + * gtk_text_buffer_move_mark. + */ + gtk_text_buffer_create_mark (buffer, "end", &iter, FALSE); + + /* Add scrolling timeout. */ + return g_timeout_add (50, (GSourceFunc) scroll_to_end, textview); + } + else + { + /* If we want to scroll to the bottom, but not scroll horizontally, + * then an end mark won't do the job. Just create a mark so we can + * use it with gtk_text_view_scroll_mark_onscreen, we'll position it + * explicitely when needed. Use left gravity so the mark stays where + * we put it after inserting new text. + */ + gtk_text_buffer_create_mark (buffer, "scroll", &iter, TRUE); + + /* Add scrolling timeout. */ + return g_timeout_add (100, (GSourceFunc) scroll_to_bottom, textview); + } +} + +static void +remove_timeout (GtkWidget *window, + gpointer timeout) +{ + g_source_remove (GPOINTER_TO_UINT (timeout)); +} + +static void +create_text_view (GtkWidget *hbox, + gboolean to_end) +{ + GtkWidget *swindow; + GtkWidget *textview; + guint timeout; + + swindow = gtk_scrolled_window_new (NULL, NULL); + gtk_box_pack_start_defaults (GTK_BOX (hbox), swindow); + textview = gtk_text_view_new (); + gtk_container_add (GTK_CONTAINER (swindow), textview); + + timeout = setup_scroll (GTK_TEXT_VIEW (textview), to_end); + + /* Remove the timeout in destroy handler, so we don't try to + * scroll destroyed widget. + */ + g_signal_connect (textview, "destroy", + G_CALLBACK (remove_timeout), + GUINT_TO_POINTER (timeout)); +} + +GtkWidget * +do_textscroll (GtkWidget *do_widget) +{ + static GtkWidget *window = NULL; + + if (!window) + { + GtkWidget *hbox; + GtkWidget *swindow; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_widget_destroyed), &window); + gtk_window_set_default_size (GTK_WINDOW (window), 600, 400); + + hbox = gtk_hbox_new (TRUE, 6); + gtk_container_add (GTK_CONTAINER (window), hbox); + + create_text_view (hbox, TRUE); + create_text_view (hbox, FALSE); + } + + if (!GTK_WIDGET_VISIBLE (window)) + gtk_widget_show_all (window); + else + gtk_widget_destroy (window); + + return window; +} diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 045b49170e..0a9adf6075 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,8 @@ +2006-08-25 Matthias Clasen <mclasen@redhat.com> + + * gtk/question_index.sgml: Rewrite the answer for + automatic scrolling. + 2006-08-17 Matthias Clasen <mclasen@redhat.com> * === Released 2.10.2 === diff --git a/docs/reference/gtk/question_index.sgml b/docs/reference/gtk/question_index.sgml index 441def5a5a..6c1f32fff3 100644 --- a/docs/reference/gtk/question_index.sgml +++ b/docs/reference/gtk/question_index.sgml @@ -757,18 +757,21 @@ How do I make a text view scroll to the end of the buffer automatically ? <answer> <para> -The "insert" <link linkend="GtkTextMark">mark</link> marks the insertion point -where gtk_text_buffer_insert() inserts new text into the buffer. The text is inserted -<emphasis>before</emphasis> the "insert" mark, so that it generally stays -at the end of the buffer. If it gets explicitly moved to some other position, -e.g. when the user selects some text, use gtk_text_buffer_move_mark() to set it to -the desired location before inserting more text. The "insert" mark of a buffer can be -obtained with gtk_text_buffer_get_insert(). +A good way to keep a text buffer scrolled to the end is to place a +<link linkend="GtkTextMark">mark</link> at the end of the buffer, and +give it right gravity. The gravity has the effect that text inserted +at the mark gets inserted <emphasis>before</emphasis>, keeping the mark +at the end. </para> <para> To ensure that the end of the buffer remains visible, use -gtk_text_view_scroll_to_mark() to scroll to the "insert" mark after inserting new text. +gtk_text_view_scroll_to_mark() to scroll to the mark after +inserting new text. +</para> + +<para> +The gtk-demo application contains an example of this technique. </para> </answer> </qandaentry> |