6

I am writing a small GUI application using Haskell's gtk2hs library and am currently working with the multiline text boxes within it. I have a function which I want to run when the user makes changes to the text within the text box, but don't want them to have to click a button to activate it.

Furthermore, because it is a rather intrusive and processing intensive function (It draws graphics, loads files etc.), I would like it to fire not whenever a user makes any change (which could probably be done with the bufferChanged signal in text buffer I'm guessing?) but when they stop for a few seconds in between changes.

Basically I am wondering if there is something in gtk which is analogous to the way range widgets can have their update policy set to continuous or delayed, but for text boxes

Craig Innes
  • 1,573
  • 11
  • 23
  • 2
    One way I did a similar thing in wxHaskell was to use a Timer object set at a (configurable) 1 second delay, and enable it every time the user types, which had the effect of delaying by a second after typing has finished. I gave up on gtk, so I'm unfamiliar with it, but maybe you can do something similar using `timeoutAdd` or `timeoutAddFull` and `timeoutRemove` from [Graphics.UI.Gtk.General.General](http://hackage.haskell.org/packages/archive/gtk/0.12.3/doc/html/Graphics-UI-Gtk-General-General.html). – AndrewC Jan 11 '13 at 13:51
  • Don't know what it is called in the Haskell port, but this is what `g_idle_add()` is for. – ptomato Jan 12 '13 at 15:05
  • Alternatively (to @AndrewC's suggestion), record the time of last user input, and check against that in a periodic timeout to decide when to run the heavy code. – ergosys Jan 13 '13 at 05:12

1 Answers1

1

I don't know anything of the Haskell bindings but in plain C it is quite easy to implement by leveraging a timeout GSource.

#include <gtk/gtk.h>

static guint source_id = 0;

static gboolean do_stuff(gpointer user_data)
{
    g_print("doing stuff...\n");
    return FALSE;
}

static void postpone(void)
{
    if (source_id > 0)
        g_source_remove(source_id);
    source_id = g_timeout_add(1000, do_stuff, NULL);
}

int main(int argc, char **argv)
{
    GtkWidget *window, *text_view;
    GtkTextBuffer *text_buffer;

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);

    text_view = gtk_text_view_new();
    gtk_container_add(GTK_CONTAINER(window), text_view);

    text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
    g_signal_connect(text_buffer, "changed", G_CALLBACK(postpone), NULL);

    gtk_widget_show_all(window);
    gtk_main();
    return 0;
}

The issue of quitting the TextView before the timeout has elapsed is still open though.

ntd
  • 7,372
  • 1
  • 27
  • 44