From f01a9d35bf0d9c30c361970266b072dd0e424fbd Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Fri, 19 Jun 2026 12:45:13 +0100 Subject: [PATCH 1/3] Use button controller in Tree --- src/Widgets/SourceList/SourceList.vala | 44 ++++++++++++-------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/Widgets/SourceList/SourceList.vala b/src/Widgets/SourceList/SourceList.vala index 99444ad82..f365f75f7 100644 --- a/src/Widgets/SourceList/SourceList.vala +++ b/src/Widgets/SourceList/SourceList.vala @@ -1572,6 +1572,7 @@ public class SourceList : Gtk.ScrolledWindow { private Gtk.Entry? editable_entry; private Gtk.CellRendererText text_cell; private Gtk.EventControllerKey key_controller; + private Gtk.GestureMultiPress button_controller; private CellRendererIcon icon_cell; private CellRendererIcon activatable_cell; private CellRendererBadge badge_cell; @@ -1729,6 +1730,12 @@ public class SourceList : Gtk.ScrolledWindow { key_controller = new Gtk.EventControllerKey (this); key_controller.key_released.connect (on_key_released); + + button_controller = new Gtk.GestureMultiPress (this) { + propagation_phase = CAPTURE, + button = 0 + }; + button_controller.pressed.connect (on_button_pressed); } ~Tree () { @@ -2280,13 +2287,10 @@ public class SourceList : Gtk.ScrolledWindow { return base.button_release_event (event); } - public override bool button_press_event (Gdk.EventButton event) { - if (event.window != get_bin_window ()) - return base.button_press_event (event); - + private void on_button_pressed (int n_press, double dx, double dy) { Gtk.TreePath path; Gtk.TreeViewColumn column; - int x = (int) event.x, y = (int) event.y, cell_x, cell_y; + int x = (int) dx, y = (int) dy, cell_x, cell_y; if (get_path_at_pos (x, y, out path, out column, out cell_x, out cell_y)) { var item = data_model.get_item_from_path (path); @@ -2300,15 +2304,14 @@ public class SourceList : Gtk.ScrolledWindow { // Cancel any editing operation going on stop_editing (); - if (event.button == Gdk.BUTTON_SECONDARY) { - popup_context_menu (item, event); - return true; - } else if (event.button == Gdk.BUTTON_PRIMARY) { + var button = button_controller.get_current_button (); + if (button == Gdk.BUTTON_SECONDARY) { + popup_context_menu (item, button_controller.get_last_event (null)); + } else if (button == Gdk.BUTTON_PRIMARY) { // Check whether an expander (or an equivalent area) was clicked. bool is_expandable = item is ExpandableItem; bool is_category = is_expandable && data_model.is_category (item, null, path); - - if (event.type == Gdk.EventType.BUTTON_PRESS) { + if (n_press == 1) { if (is_expandable) { // Checking for secondary_expander_cell is not necessary because the entire row // serves for this purpose when the item is a category or when the item is a @@ -2318,27 +2321,23 @@ public class SourceList : Gtk.ScrolledWindow { unselectable_item_clicked = is_category || (!item.selectable && !over_cell (column, path, activatable_cell, cell_x)); - if (!unselectable_item_clicked - && over_primary_expander (column, path, cell_x) - && toggle_expansion (item as ExpandableItem)) - return true; + if (!unselectable_item_clicked && over_primary_expander (column, path, cell_x)) { + toggle_expansion (item as ExpandableItem); + } } } else if ( - event.type == Gdk.EventType.2BUTTON_PRESS + n_press == 2 && !is_category // Main categories are *not* editable && item.editable && item.selectable && over_cell (column, path, text_cell, cell_x) - && start_editing_item (item) ) { - // The user double-clicked over the text cell, and editing started successfully. - return true; + // Start editing after native event handlers finished else fails + Idle.add (() => { start_editing_item (item); return Source.REMOVE; }); } } } } - - return base.button_press_event (event); } private bool over_primary_expander (Gtk.TreeViewColumn col, Gtk.TreePath path, int x) { @@ -2405,7 +2404,7 @@ public class SourceList : Gtk.ScrolledWindow { return popup_context_menu (null, null); } - private bool popup_context_menu (Item? item, Gdk.EventButton? event) { + private bool popup_context_menu (Item? item, Gdk.Event? event) { if (item == null) item = selected_item; @@ -2669,7 +2668,6 @@ public class SourceList : Gtk.ScrolledWindow { private Tree tree; private DataModel data_model = new DataModel (); - /** * Creates a new {@link Code.Widgets.SourceList}. * From d6caeb606302c37de8f06b5e539f4d11af093b40 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Fri, 19 Jun 2026 13:10:33 +0100 Subject: [PATCH 2/3] Gtk.Menu -> Gtk.PopoverMenu --- src/Widgets/SourceList/SourceList.vala | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Widgets/SourceList/SourceList.vala b/src/Widgets/SourceList/SourceList.vala index f365f75f7..e6ec4e192 100644 --- a/src/Widgets/SourceList/SourceList.vala +++ b/src/Widgets/SourceList/SourceList.vala @@ -2306,7 +2306,7 @@ public class SourceList : Gtk.ScrolledWindow { var button = button_controller.get_current_button (); if (button == Gdk.BUTTON_SECONDARY) { - popup_context_menu (item, button_controller.get_last_event (null)); + popup_context_menu (item, (int) dx, (int) dy); } else if (button == Gdk.BUTTON_PRIMARY) { // Check whether an expander (or an equivalent area) was clicked. bool is_expandable = item is ExpandableItem; @@ -2401,26 +2401,26 @@ public class SourceList : Gtk.ScrolledWindow { } public override bool popup_menu () { - return popup_context_menu (null, null); + return popup_context_menu (); } - private bool popup_context_menu (Item? item, Gdk.Event? event) { - if (item == null) + private bool popup_context_menu (Item? item = null, int px = 0, int py = 0) { + if (item == null) { item = selected_item; + } if (item != null) { - var menu = item.get_context_menu (); - if (menu != null) { - var gtk_menu = new Gtk.Menu.from_model (menu) { - attach_widget = this + var menu_model = item.get_context_menu (); + if (menu_model != null) { + var menu = new Gtk.PopoverMenu () { + modal = true, + relative_to = this, + position = RIGHT }; - gtk_menu.popup_at_pointer (event); - if (event == null) { - gtk_menu.select_first (false); - } - - return true; + menu.bind_model (menu_model, null); + menu.pointing_to = Gdk.Rectangle () { x = px, y = py }; + menu.popup (); } } From 36ef9042f6be47d037d7f1306afe711d25eed235 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Mon, 22 Jun 2026 14:44:56 +0100 Subject: [PATCH 3/3] Handle controller released signal instead of button release event --- src/Widgets/SourceList/SourceList.vala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Widgets/SourceList/SourceList.vala b/src/Widgets/SourceList/SourceList.vala index e6ec4e192..8180c4532 100644 --- a/src/Widgets/SourceList/SourceList.vala +++ b/src/Widgets/SourceList/SourceList.vala @@ -1736,6 +1736,7 @@ public class SourceList : Gtk.ScrolledWindow { button = 0 }; button_controller.pressed.connect (on_button_pressed); + button_controller.released.connect (on_button_released); } ~Tree () { @@ -2266,13 +2267,13 @@ public class SourceList : Gtk.ScrolledWindow { } } - public override bool button_release_event (Gdk.EventButton event) { - if (unselectable_item_clicked && event.window == get_bin_window ()) { + private void on_button_released (int n_press, double px, double py) { + if (unselectable_item_clicked) { unselectable_item_clicked = false; Gtk.TreePath path; Gtk.TreeViewColumn column; - int x = (int) event.x, y = (int) event.y, cell_x, cell_y; + int x = (int) px, y = (int) py, cell_x, cell_y; if (get_path_at_pos (x, y, out path, out column, out cell_x, out cell_y)) { var item = data_model.get_item_from_path (path) as ExpandableItem; @@ -2283,8 +2284,6 @@ public class SourceList : Gtk.ScrolledWindow { } } } - - return base.button_release_event (event); } private void on_button_pressed (int n_press, double dx, double dy) {