#include #define DRAG_THRESHOLD 20 typedef struct _appcontext AppContext; struct _appcontext { GtkWidget *window; GtkWidget *swin; GtkWidget *view; GtkWidget *vbox; GtkAdjustment *vadj; gboolean in_drag; gdouble start_drag_y; gdouble start_drag_vadj; }; /* record current scrolled window position / mouse coordinate on click */ gboolean button_drag_begin (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { AppContext *context = (AppContext *) user_data; context->in_drag = FALSE; context->start_drag_y = event->y_root; context->start_drag_vadj = gtk_adjustment_get_value (context->vadj); return FALSE; } /* * on motion with button pressed (GDK_BUTTON_MOTION_MASK) check if we started * scrolling (y-distance > DRAG_THRESHOLD) and update vadj accordingly */ gboolean button_drag_motion (GtkWidget *widget, GdkEventMotion *event, gpointer user_data) { AppContext *context = (AppContext *) user_data; gdouble cur_val; gdouble cur_y; cur_y = event->y_root; if (ABS (context->start_drag_y - cur_y) > DRAG_THRESHOLD) context->in_drag = TRUE; if (!context->in_drag) return FALSE; cur_val = gtk_adjustment_get_value (context->vadj); gtk_adjustment_set_value (context->vadj, CLAMP (context->start_drag_vadj + context->start_drag_y - cur_y, context->vadj->lower, context->vadj->upper - context->vadj->page_size)); return FALSE; } /* for buttons we might need to stop the "clicked" signal emission to * avoid further handlers to run. After scrolling we don't want to activate * the button */ void button_drag_end (GtkWidget *widget, gpointer user_data) { AppContext *context = (AppContext *) user_data; if (context->in_drag) g_signal_stop_emission_by_name (widget, "clicked"); context->in_drag = FALSE; } /* just some sample callback for the buttons. Note that these need to be * connected *after* the previous callback */ void button_activity (GtkWidget *widget, gpointer user_data) { AppContext *context = (AppContext *) user_data; GtkWidget *dlg; dlg = gtk_dialog_new_with_buttons ("pressed!\n", GTK_WINDOW (context->window), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); gtk_widget_new (gtk_label_get_type (), "label", gtk_button_get_label (GTK_BUTTON (widget)), "visible", TRUE, "parent", gtk_dialog_get_content_area (GTK_DIALOG (dlg)), NULL); gtk_dialog_run (GTK_DIALOG (dlg)); gtk_widget_destroy (dlg); } int main (int argc, char *argv[]) { AppContext *context; GtkWidget *w; int i; gtk_init (&argc, &argv); context = g_new0 (AppContext, 1); /* top level window */ context->window = GTK_WIDGET (gtk_window_new (GTK_WINDOW_TOPLEVEL)); gtk_window_set_default_size (GTK_WINDOW (context->window), 400, 400); g_signal_connect (G_OBJECT (context->window), "delete-event", G_CALLBACK (gtk_main_quit), NULL); /* a scrolled window with a viewport for the button-vbox */ context->swin = gtk_widget_new (gtk_scrolled_window_get_type(), "vscrollbar-policy", GTK_POLICY_ALWAYS, "hscrollbar-policy", GTK_POLICY_NEVER, "parent", context->window, NULL); context->view = gtk_widget_new (gtk_viewport_get_type(), "parent", context->swin, NULL); /* motion/press handlers for the viewport */ g_signal_connect (G_OBJECT (context->view), "button-press-event", G_CALLBACK (button_drag_begin), context); g_signal_connect (G_OBJECT (context->view), "motion-notify-event", G_CALLBACK (button_drag_motion), context); gtk_widget_add_events (GTK_WIDGET (context->view), GDK_BUTTON_MOTION_MASK); /* the vbox containing a lot of buttons */ context->vbox = gtk_widget_new (gtk_vbox_get_type(), "spacing", 10, "parent", context->view, NULL); context->vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (context->swin)); for (i = 0; i < 50; i++) { gchar *label; label = g_strdup_printf ("Button No. %d", i+1); w = gtk_widget_new (gtk_button_get_type(), "label", label, "parent", context->vbox, NULL); /* first connect the signal handlers for the scroll handling */ g_signal_connect (G_OBJECT (w), "button-press-event", G_CALLBACK (button_drag_begin), context); g_signal_connect (G_OBJECT (w), "motion-notify-event", G_CALLBACK (button_drag_motion), context); g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (button_drag_end), context); gtk_widget_add_events (GTK_WIDGET (w), GDK_BUTTON_MOTION_MASK); /* *then* connect the GUI functionality */ g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (button_activity), context); g_free (label); } gtk_widget_show_all (GTK_WIDGET (context->window)); gtk_main (); return 0; }