diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-02-15 01:32:05 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-02-23 04:52:39 +0300 |
commit | 428dafa568923d5632101c716fb20a3de35d27be (patch) | |
tree | 475b270fc384d5040e4711e155e47d580c52b1a3 /src/interface-gtk | |
parent | 980dcdaa138a42830af4e2533b7e970d7f5fa3cf (diff) | |
download | sciteco-428dafa568923d5632101c716fb20a3de35d27be.tar.gz |
support mouse interaction with popup windows
* Curses allows scrolling with the scroll wheel at least
if mouse support is enabled via ED flags.
Gtk always supported that.
* Allow clicking on popup entries to fully autocomplete them.
Since this behavior - just like auto completions - is parser state-dependant,
I introduced a new state method (insert_completion_cb).
All the implementations are currently in cmdline.c since there is some overlap
with the process_edit_cmd_cb implementations.
* Fixed pressing undefined function keys while showing the popup.
The popup area is no longer redrawn/replaced with the Scintilla view.
Instead, continue to show the popup.
Diffstat (limited to 'src/interface-gtk')
-rw-r--r-- | src/interface-gtk/gtk-info-popup.c | 41 | ||||
-rw-r--r-- | src/interface-gtk/gtk-label.c | 6 | ||||
-rw-r--r-- | src/interface-gtk/gtk-label.h | 1 | ||||
-rw-r--r-- | src/interface-gtk/interface.c | 44 |
4 files changed, 76 insertions, 16 deletions
diff --git a/src/interface-gtk/gtk-info-popup.c b/src/interface-gtk/gtk-info-popup.c index 29a1fa9..b9ca41c 100644 --- a/src/interface-gtk/gtk-info-popup.c +++ b/src/interface-gtk/gtk-info-popup.c @@ -53,6 +53,8 @@ struct _TecoGtkInfoPopup { gboolean frozen; }; +static guint teco_gtk_info_popup_clicked_signal; + static gboolean teco_gtk_info_popup_scroll_event(GtkWidget *widget, GdkEventScroll *event); static void teco_gtk_info_popup_show(GtkWidget *widget); static void teco_gtk_info_popup_vadjustment_changed(GtkAdjustment *vadjustment, GtkWidget *scrollbar); @@ -82,6 +84,31 @@ teco_gtk_info_popup_class_init(TecoGtkInfoPopupClass *klass) GTK_WIDGET_CLASS(klass)->scroll_event = teco_gtk_info_popup_scroll_event; GTK_WIDGET_CLASS(klass)->show = teco_gtk_info_popup_show; G_OBJECT_CLASS(klass)->finalize = teco_gtk_info_popup_finalize; + + teco_gtk_info_popup_clicked_signal = + g_signal_new("clicked", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_ULONG); +} + +static void +teco_gtk_info_popup_activated_cb(GtkFlowBox *box, GtkFlowBoxChild *child, gpointer user_data) +{ + TecoGtkInfoPopup *popup = TECO_GTK_INFO_POPUP(user_data); + + /* + * Find the TecoGtkLabel in the flow box child. + */ + GtkWidget *hbox = gtk_bin_get_child(GTK_BIN(child)); + g_autoptr(GList) child_list = gtk_container_get_children(GTK_CONTAINER(hbox)); + GList *entry; + for (entry = child_list; entry != NULL && !TECO_IS_GTK_LABEL(entry->data); entry = g_list_next(entry)); + g_assert(entry != NULL); + const teco_string_t *str = teco_gtk_label_get_text(TECO_GTK_LABEL(entry->data)); + + g_signal_emit(popup, teco_gtk_info_popup_clicked_signal, 0, + str->data, (gulong)str->len); } static void @@ -106,6 +133,8 @@ teco_gtk_info_popup_init(TecoGtkInfoPopup *self) G_CALLBACK(teco_gtk_info_popup_vadjustment_changed), scrollbar); self->flow_box = gtk_flow_box_new(); + g_signal_connect(self->flow_box, "child-activated", + G_CALLBACK(teco_gtk_info_popup_activated_cb), self); /* take as little height as necessary */ gtk_orientable_set_orientation(GTK_ORIENTABLE(self->flow_box), GTK_ORIENTATION_HORIZONTAL); @@ -311,12 +340,6 @@ teco_gtk_info_popup_idle_add(TecoGtkInfoPopup *self, teco_popup_entry_type_t typ gtk_widget_set_halign(label, GTK_ALIGN_START); gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - /* - * FIXME: This makes little sense once we've got mouse support. - * But for the time being, it's a useful setting. - */ - gtk_label_set_selectable(GTK_LABEL(label), TRUE); - switch (type) { case TECO_POPUP_PLAIN: gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_START); @@ -417,12 +440,10 @@ teco_gtk_info_popup_scroll_page(TecoGtkInfoPopup *self) * Adjust this so only complete entries are shown. * Effectively, this rounds down to the line height. */ - GList *child_list = gtk_container_get_children(GTK_CONTAINER(self->flow_box)); - if (child_list) { + g_autoptr(GList) child_list = gtk_container_get_children(GTK_CONTAINER(self->flow_box)); + if (child_list) new_value -= (gint)new_value % gtk_widget_get_allocated_height(GTK_WIDGET(child_list->data)); - g_list_free(child_list); - } /* clip to the maximum possible value */ new_value = MIN(new_value, gtk_adjustment_get_upper(adj)); diff --git a/src/interface-gtk/gtk-label.c b/src/interface-gtk/gtk-label.c index ebbb40e..ef370a2 100644 --- a/src/interface-gtk/gtk-label.c +++ b/src/interface-gtk/gtk-label.c @@ -269,3 +269,9 @@ teco_gtk_label_set_text(TecoGtkLabel *self, const gchar *str, gssize len) gtk_label_set_text(GTK_LABEL(self), plaintext); } + +const teco_string_t * +teco_gtk_label_get_text(TecoGtkLabel *self) +{ + return &self->string; +} diff --git a/src/interface-gtk/gtk-label.h b/src/interface-gtk/gtk-label.h index e852104..c52d073 100644 --- a/src/interface-gtk/gtk-label.h +++ b/src/interface-gtk/gtk-label.h @@ -27,6 +27,7 @@ G_DECLARE_FINAL_TYPE(TecoGtkLabel, teco_gtk_label, TECO, GTK_LABEL, GtkLabel) GtkWidget *teco_gtk_label_new(const gchar *str, gssize len); void teco_gtk_label_set_text(TecoGtkLabel *self, const gchar *str, gssize len); +const teco_string_t *teco_gtk_label_get_text(TecoGtkLabel *self); void teco_gtk_label_parse_string(const gchar *str, gssize len, PangoColor *fg, guint16 fg_alpha, diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index ed9271b..9c740a6 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -70,6 +70,8 @@ static void teco_interface_size_allocate_cb(GtkWidget *widget, gpointer user_data); static gboolean teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data); +static void teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong len, + gpointer user_data); static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer user_data); static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED; @@ -194,6 +196,7 @@ static struct { GtkIMContext *input_method; GtkWidget *popup_widget; + gsize popup_prefix_len; GtkWidget *current_view_widget; @@ -381,6 +384,8 @@ teco_interface_init(void) */ teco_interface.popup_widget = teco_gtk_info_popup_new(); gtk_widget_set_name(teco_interface.popup_widget, "sciteco-info-popup"); + g_signal_connect(teco_interface.popup_widget, "clicked", + G_CALLBACK(teco_interface_popup_clicked_cb), NULL); gtk_overlay_add_overlay(GTK_OVERLAY(overlay_widget), teco_interface.popup_widget); g_signal_connect(overlay_widget, "get-child-position", G_CALLBACK(teco_gtk_info_popup_get_position_in_overlay), NULL); @@ -754,12 +759,16 @@ teco_interface_popup_add(teco_popup_entry_type_t type, const gchar *name, gsize } void -teco_interface_popup_show(void) +teco_interface_popup_show(gsize prefix_len) +{ + teco_interface.popup_prefix_len = prefix_len; + gtk_widget_show(teco_interface.popup_widget); +} + +void +teco_interface_popup_scroll(void) { - if (gtk_widget_get_visible(teco_interface.popup_widget)) - teco_gtk_info_popup_scroll_page(TECO_GTK_INFO_POPUP(teco_interface.popup_widget)); - else - gtk_widget_show(teco_interface.popup_widget); + teco_gtk_info_popup_scroll_page(TECO_GTK_INFO_POPUP(teco_interface.popup_widget)); } gboolean @@ -1349,7 +1358,6 @@ static gboolean teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) { static gboolean recursed = FALSE; - g_autoptr(GError) error = NULL; #ifdef DEBUG if (event->type == GDK_KEY_PRESS) @@ -1393,6 +1401,8 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) GdkWindow *top_window = gdk_window_get_toplevel(gtk_widget_get_window(teco_interface.window)); do { + g_autoptr(GError) error = NULL; + /* * The event queue might be filled when pressing keys when SciTECO * is busy executing code. @@ -1461,6 +1471,28 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) return TRUE; } +static void +teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong len, gpointer user_data) +{ + g_assert(len >= teco_interface.popup_prefix_len); + const teco_string_t insert = {str+teco_interface.popup_prefix_len, len-teco_interface.popup_prefix_len}; + teco_machine_t *machine = &teco_cmdline.machine.parent; + + const teco_view_t *last_view = teco_interface_current_view; + + /* + * NOTE: It shouldn't really be necessary to catch TECO_ERROR_QUIT here. + * A auto completion should never result in program termination. + */ + if (machine->current->insert_completion_cb && + !machine->current->insert_completion_cb(machine, &insert, NULL)) + return; + teco_interface_popup_clear(); + teco_interface_cmdline_update(&teco_cmdline); + + teco_interface_refresh(teco_interface_current_view != last_view); +} + static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer user_data) { |