diff options
-rw-r--r-- | TODO | 4 | ||||
-rw-r--r-- | src/interface-gtk/gtk-info-popup.c | 14 | ||||
-rw-r--r-- | src/interface-gtk/interface.c | 46 |
3 files changed, 61 insertions, 3 deletions
@@ -345,9 +345,7 @@ Features: Perhaps blinking or invisible? The problem is, this won't work so easily once we use a Scintilla minibuffer everywhere. - Gtk could at the very least use the hourglass cursor. - * Gtk: Change the cursor when hovering over popup entries. - The text area should have the "I" beam unless we're busy. + Gtk already sets the "wait" cursor when busy. * Command to free Q-Register (remove from table). e.g. FQ (free Q). :FQ could free by QRegister prefix name for the common use case of Q-Register subtables and lists. diff --git a/src/interface-gtk/gtk-info-popup.c b/src/interface-gtk/gtk-info-popup.c index b9ca41c..aaa0a65 100644 --- a/src/interface-gtk/gtk-info-popup.c +++ b/src/interface-gtk/gtk-info-popup.c @@ -47,6 +47,7 @@ struct _TecoGtkInfoPopup { GtkAdjustment *hadjustment, *vadjustment; GtkWidget *flow_box; + GdkCursor *cursor; /*< pointer/hand cursor */ GStringChunk *chunk; teco_stailq_head_t list; guint idle_id; @@ -74,6 +75,9 @@ teco_gtk_info_popup_finalize(GObject *obj_self) while ((entry = teco_stailq_remove_head(&self->list))) g_free(entry); + if (self->cursor) + g_object_unref(self->cursor); + /* chain up to parent class */ G_OBJECT_CLASS(teco_gtk_info_popup_parent_class)->finalize(obj_self); } @@ -354,6 +358,16 @@ teco_gtk_info_popup_idle_add(TecoGtkInfoPopup *self, teco_popup_entry_type_t typ gtk_widget_show_all(hbox); gtk_container_add(GTK_CONTAINER(self->flow_box), hbox); + + GtkWidget *flow_box_child = gtk_widget_get_parent(hbox); + g_assert(GTK_IS_FLOW_BOX_CHILD(flow_box_child)); + GdkWindow *window = gtk_widget_get_window(flow_box_child); + g_assert(window != NULL); + + if (G_UNLIKELY(!self->cursor)) + /* we only initialize it now after guaranteed widget realization */ + self->cursor = gdk_cursor_new_from_name(gdk_window_get_display(window), "pointer"); + gdk_window_set_cursor(window, self->cursor); } static gboolean diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index a41890d..5330bfa 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -60,6 +60,8 @@ //#define DEBUG +static gboolean teco_interface_busy_timeout_cb(gpointer user_data); +static void teco_interface_event_box_realized_cb(GtkWidget *widget, gpointer user_data); static void teco_interface_cmdline_size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data); @@ -312,6 +314,8 @@ teco_interface_init(void) gtk_box_pack_start(GTK_BOX(overlay_vbox), teco_interface.event_box_widget, TRUE, TRUE, 0); + g_signal_connect(teco_interface.event_box_widget, "realize", + G_CALLBACK(teco_interface_event_box_realized_cb), NULL); g_signal_connect(teco_interface.event_box_widget, "size-allocate", G_CALLBACK(teco_interface_size_allocate_cb), NULL); @@ -402,6 +406,17 @@ teco_interface_init(void) teco_interface_cmdline_update(&empty_cmdline); } +static void +teco_interface_set_cursor(GtkWidget *widget, const gchar *name) +{ + GdkWindow *window = gtk_widget_get_window(widget); + g_assert(window != NULL); + GdkDisplay *display = gdk_window_get_display(window); + + g_autoptr(GdkCursor) cursor = name ? gdk_cursor_new_from_name(display, name) : NULL; + gdk_window_set_cursor(window, cursor); +} + GOptionGroup * teco_interface_get_options(void) { @@ -1332,6 +1347,29 @@ teco_interface_cleanup(void) */ /** + * Called some time after processing an input event in order to show + * business. + * + * The delay avoids cursor flickering during normal typing. + * + * @fixme It would be nicer to set the cursor for the entire window, + * but that would apparently require another GtkEventBox, spanning everything. + */ +static gboolean +teco_interface_busy_timeout_cb(gpointer user_data) +{ + teco_interface_set_cursor(teco_interface.event_box_widget, "wait"); + return G_SOURCE_REMOVE; +} + +static void +teco_interface_event_box_realized_cb(GtkWidget *widget, gpointer user_data) +{ + /* It's only now safe to get the GdkWindow. */ + teco_interface_set_cursor(widget, "text"); +} + +/** * Called when the commandline widget is resized. * This should ensure that the caret jumps to the middle of the command line, * imitating the behaviour of the current Curses command line. @@ -1398,6 +1436,10 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) recursed = TRUE; + GSource *busy_timeout = g_timeout_source_new(500); /* ms */ + g_source_set_callback(busy_timeout, teco_interface_busy_timeout_cb, NULL, NULL); + g_source_attach(busy_timeout, NULL); + teco_memory_start_limiting(); g_queue_push_tail(teco_interface.event_queue, gdk_event_copy(event)); @@ -1471,6 +1513,10 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) teco_memory_stop_limiting(); + g_source_destroy(busy_timeout); + g_source_unref(busy_timeout); + teco_interface_set_cursor(teco_interface.event_box_widget, "text"); + recursed = FALSE; return TRUE; } |