aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--TODO4
-rw-r--r--src/interface-gtk/gtk-info-popup.c14
-rw-r--r--src/interface-gtk/interface.c46
3 files changed, 61 insertions, 3 deletions
diff --git a/TODO b/TODO
index 289e724..d297e1b 100644
--- a/TODO
+++ b/TODO
@@ -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;
}