From 8773b0d45efbace287c21fe196e8ee1d4704d0d8 Mon Sep 17 00:00:00 2001 From: Roberto Kok Date: Thu, 3 Oct 2024 19:37:48 +0800 Subject: [PATCH 1/4] Close tabs when middle-clicked --- gnucash/gnome-utils/gnc-main-window.cpp | 63 ++++++++++++++++++++----- gnucash/gnome-utils/gnc-main-window.h | 19 ++++++++ 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index 09e59c3a41d..d0420fbd94b 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -3209,7 +3209,7 @@ gnc_main_window_open_page (GncMainWindow *window, GncPluginPage *page) { GncMainWindowPrivate *priv; - GtkWidget *tab_hbox; + GtkWidget *tab_container, *tab_clickable_area; GtkWidget *label, *entry; const gchar *icon, *text, *color_string, *lab_text; GtkWidget *image; @@ -3257,6 +3257,15 @@ gnc_main_window_open_page (GncMainWindow *window, /* * The page tab. + * Component structure: + * + * tab_container (GtkBox) + * ├── tab_clickable_area (GtkEventBox) + * │ └── tab_content (GtkBox) + * │ ├── image (GtkImage, optional) + * │ ├── label (GtkLabel) + * │ └── entry (GtkEntry, hidden) + * └── close_button (GtkButton, if not immutable) */ icon = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon; lab_text = gnc_plugin_page_get_page_name(page); @@ -3269,34 +3278,44 @@ gnc_main_window_open_page (GncMainWindow *window, gtk_widget_show (label); - tab_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + tab_container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); if (g_strcmp0 (gnc_plugin_page_get_plugin_name (page), "GncPluginPageAccountTree") == 0) - gtk_widget_set_name (GTK_WIDGET(tab_hbox), "gnc-id-account-page-tab-box"); + gtk_widget_set_name (GTK_WIDGET(tab_container), "gnc-id-account-page-tab-box"); + + gtk_box_set_homogeneous (GTK_BOX (tab_container), FALSE); + gtk_widget_show (tab_container); - gtk_box_set_homogeneous (GTK_BOX (tab_hbox), FALSE); - gtk_widget_show (tab_hbox); + // Create a custom clickable area for the tab to support middle-clicking + tab_clickable_area = gtk_event_box_new(); + gtk_widget_show(tab_clickable_area); + gtk_box_pack_start (GTK_BOX (tab_container), tab_clickable_area, TRUE, TRUE, 0); + + // Create a box for the tab's content + GtkWidget *tab_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_container_add(GTK_CONTAINER(tab_clickable_area), tab_content); + gtk_widget_show(tab_content); if (icon != nullptr) { image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU); gtk_widget_show (image); - gtk_box_pack_start (GTK_BOX (tab_hbox), image, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab_content), image, FALSE, FALSE, 0); gtk_widget_set_margin_start (GTK_WIDGET(image), 5); - gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (tab_content), label, TRUE, TRUE, 0); } else - gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (tab_content), label, TRUE, TRUE, 0); text = gnc_plugin_page_get_page_long_name(page); if (text) { - gtk_widget_set_tooltip_text(tab_hbox, text); + gtk_widget_set_tooltip_text(tab_clickable_area, text); } entry = gtk_entry_new(); gtk_widget_hide (entry); - gtk_box_pack_start (GTK_BOX (tab_hbox), entry, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (tab_content), entry, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(gnc_main_window_tab_entry_activate), page); g_signal_connect(G_OBJECT(entry), "focus-out-event", @@ -3328,10 +3347,14 @@ gnc_main_window_open_page (GncMainWindow *window, else gtk_widget_hide (close_button); + // Custom handler to close on middle-clicks + g_signal_connect(G_OBJECT(tab_clickable_area), "button-press-event", + G_CALLBACK(gnc_tab_clicked_cb), page); + g_signal_connect_swapped (G_OBJECT (close_button), "clicked", G_CALLBACK(gnc_main_window_close_page), page); - gtk_box_pack_start (GTK_BOX (tab_hbox), close_button, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab_container), close_button, FALSE, FALSE, 0); gtk_widget_set_margin_end (GTK_WIDGET(close_button), 5); g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON, close_button); } @@ -3344,7 +3367,7 @@ gnc_main_window_open_page (GncMainWindow *window, /* * Now install it all in the window. */ - gnc_main_window_connect(window, page, tab_hbox, label); + gnc_main_window_connect(window, page, tab_container, label); color_string = gnc_plugin_page_get_page_color(page); main_window_update_page_color (page, color_string); @@ -5643,6 +5666,22 @@ gnc_main_window_button_press_cb (GtkWidget *whatever, return FALSE; } +/* Callback function invoked when the user clicks on a GtkNotebook tab. + * + * This function is needed to make it possible to close a tab + * when it's clicked using the middle mouse button; + * there does not seem to be a way to do this with GtkNotebook natively. + */ +gboolean +gnc_tab_clicked_cb(GtkWidget *widget, GdkEventButton *event, GncPluginPage *page) { + if (event->type == GDK_BUTTON_PRESS && event->button == 2) + { + gnc_main_window_close_page(page); + return TRUE; + } + return FALSE; +} + void gnc_main_window_all_action_set_sensitive (const gchar *action_name, gboolean sensitive) diff --git a/gnucash/gnome-utils/gnc-main-window.h b/gnucash/gnome-utils/gnc-main-window.h index 63bd933d6dc..3376b4e6b1b 100644 --- a/gnucash/gnome-utils/gnc-main-window.h +++ b/gnucash/gnome-utils/gnc-main-window.h @@ -415,6 +415,25 @@ gboolean gnc_main_window_button_press_cb (GtkWidget *whatever, GdkEventButton *event, GncPluginPage *page); +/* Callback function invoked when the user clicks on a GtkNotebook tab. + * + * This function is needed to make it possible to close a tab + * when it's clicked using the middle mouse button; + * there does not seem to be a way to do this with GtkNotebook natively. + * + * @param widget The event box in the tab, which was clicked. + * + * @param event The event parameter describing where on the screen + * the mouse was pointing when clicked, type of click, modifiers, + * etc. + * + * @param page This is the GncPluginPage corresponding to the tab. + * + * @return Returns TRUE if this was a middle-click, meaning Gnucash + * handled the click. + */ +gboolean +gnc_tab_clicked_cb(GtkWidget *widget, GdkEventButton *event, GncPluginPage *page); /** Callback function invoked when the user requests that Gnucash * popup the contextual menu via the keyboard context-menu request From 56fafe05e615c6a54458b0c8a121c746c2510a77 Mon Sep 17 00:00:00 2001 From: Roberto Kok Date: Mon, 7 Oct 2024 13:48:58 +0800 Subject: [PATCH 2/4] Make gnc_tab_clicked_cb static --- gnucash/gnome-utils/gnc-main-window.cpp | 43 ++++++++++++++++--------- gnucash/gnome-utils/gnc-main-window.h | 20 ------------ 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index d0420fbd94b..0c1825a7d3e 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -1607,6 +1607,33 @@ gnc_main_window_update_all_titles (void) nullptr); } +/* Callback function invoked when the user clicks on a GtkNotebook tab. + * + * This function is needed to make it possible to close a tab + * when it's clicked using the middle mouse button; + * there does not seem to be a way to do this with GtkNotebook natively. + * + * @param widget The event box in the tab, which was clicked. + * + * @param event The event parameter describing where on the screen + * the mouse was pointing when clicked, type of click, modifiers, + * etc. + * + * @param page This is the GncPluginPage corresponding to the tab. + * + * @return Returns TRUE if this was a middle-click, meaning Gnucash + * handled the click. + */ +static gboolean +gnc_tab_clicked_cb(GtkWidget *widget, GdkEventButton *event, GncPluginPage *page) { + if (event->type == GDK_BUTTON_PRESS && event->button == 2) + { + gnc_main_window_close_page(page); + return TRUE; + } + return FALSE; +} + static void gnc_main_window_book_dirty_cb (QofBook *book, gboolean dirty, @@ -5666,22 +5693,6 @@ gnc_main_window_button_press_cb (GtkWidget *whatever, return FALSE; } -/* Callback function invoked when the user clicks on a GtkNotebook tab. - * - * This function is needed to make it possible to close a tab - * when it's clicked using the middle mouse button; - * there does not seem to be a way to do this with GtkNotebook natively. - */ -gboolean -gnc_tab_clicked_cb(GtkWidget *widget, GdkEventButton *event, GncPluginPage *page) { - if (event->type == GDK_BUTTON_PRESS && event->button == 2) - { - gnc_main_window_close_page(page); - return TRUE; - } - return FALSE; -} - void gnc_main_window_all_action_set_sensitive (const gchar *action_name, gboolean sensitive) diff --git a/gnucash/gnome-utils/gnc-main-window.h b/gnucash/gnome-utils/gnc-main-window.h index 3376b4e6b1b..5366ca9d3ae 100644 --- a/gnucash/gnome-utils/gnc-main-window.h +++ b/gnucash/gnome-utils/gnc-main-window.h @@ -415,26 +415,6 @@ gboolean gnc_main_window_button_press_cb (GtkWidget *whatever, GdkEventButton *event, GncPluginPage *page); -/* Callback function invoked when the user clicks on a GtkNotebook tab. - * - * This function is needed to make it possible to close a tab - * when it's clicked using the middle mouse button; - * there does not seem to be a way to do this with GtkNotebook natively. - * - * @param widget The event box in the tab, which was clicked. - * - * @param event The event parameter describing where on the screen - * the mouse was pointing when clicked, type of click, modifiers, - * etc. - * - * @param page This is the GncPluginPage corresponding to the tab. - * - * @return Returns TRUE if this was a middle-click, meaning Gnucash - * handled the click. - */ -gboolean -gnc_tab_clicked_cb(GtkWidget *widget, GdkEventButton *event, GncPluginPage *page); - /** Callback function invoked when the user requests that Gnucash * popup the contextual menu via the keyboard context-menu request * key combination (Shift-F10 by default). From 7f0c41c1c7f857e4175acea2ba64ffa3dbac834d Mon Sep 17 00:00:00 2001 From: Roberto Kok Date: Wed, 20 Nov 2024 01:46:25 +0800 Subject: [PATCH 3/4] Update main_window_find_tab_items for new page tab structure --- gnucash/gnome-utils/gnc-main-window.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index 0c1825a7d3e..2fb40ec92e4 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -2279,11 +2279,19 @@ main_window_find_tab_items (GncMainWindow *window, tab_widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook), page->notebook_page); - if (GTK_IS_EVENT_BOX (tab_widget)) - tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget)); - else if (GTK_IS_BOX (tab_widget)) - tab_hbox = tab_widget; - else + + // Walk through children to find the box containing label+entry + tab_hbox = tab_widget; + while (tab_hbox) { + if (g_strcmp0(gtk_widget_get_name(tab_hbox), "tab-content") == 0) { + break; + } + GList* _children = gtk_container_get_children(GTK_CONTAINER(tab_hbox)); + tab_hbox = _children ? GTK_WIDGET(_children->data) : nullptr; + g_list_free(_children); + } + + if (!GTK_IS_BOX(tab_hbox)) { PWARN ("Unknown widget for tab label %p", tab_widget); return FALSE; @@ -3319,7 +3327,9 @@ gnc_main_window_open_page (GncMainWindow *window, gtk_box_pack_start (GTK_BOX (tab_container), tab_clickable_area, TRUE, TRUE, 0); // Create a box for the tab's content + // Give it a name so we can find it later (see main_window_find_tab_items) GtkWidget *tab_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_set_name(tab_content, "tab-content"); gtk_container_add(GTK_CONTAINER(tab_clickable_area), tab_content); gtk_widget_show(tab_content); From a2edf9d3cd506416500e5f548aa396f0ebe38ba0 Mon Sep 17 00:00:00 2001 From: Roberto Kok Date: Wed, 20 Nov 2024 01:56:04 +0800 Subject: [PATCH 4/4] Set tooltip text on the outer tab container again So it can be updated by main_window_update_page_long_name --- gnucash/gnome-utils/gnc-main-window.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index 2fb40ec92e4..b77f41851a0 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -3315,6 +3315,12 @@ gnc_main_window_open_page (GncMainWindow *window, tab_container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + text = gnc_plugin_page_get_page_long_name(page); + if (text) + { + gtk_widget_set_tooltip_text(tab_container, text); + } + if (g_strcmp0 (gnc_plugin_page_get_plugin_name (page), "GncPluginPageAccountTree") == 0) gtk_widget_set_name (GTK_WIDGET(tab_container), "gnc-id-account-page-tab-box"); @@ -3344,12 +3350,6 @@ gnc_main_window_open_page (GncMainWindow *window, else gtk_box_pack_start (GTK_BOX (tab_content), label, TRUE, TRUE, 0); - text = gnc_plugin_page_get_page_long_name(page); - if (text) - { - gtk_widget_set_tooltip_text(tab_clickable_area, text); - } - entry = gtk_entry_new(); gtk_widget_hide (entry); gtk_box_pack_start (GTK_BOX (tab_content), entry, TRUE, TRUE, 0);