[shotwell/wip/gtk4] Fix zoomed panning and context menus



commit 31d8d584a84c81fa8ee2ca69d507e7c5ef8011db
Author: Jens Georg <mail jensge org>
Date:   Sun Apr 10 00:17:47 2022 +0200

    Fix zoomed panning and context menus

 src/CheckerboardPage.vala       |  22 ++++-----
 src/MediaPage.vala              |  10 ++--
 src/Page.vala                   |  99 ++++++++++++++------------------------
 src/PhotoPage.vala              | 104 ++++++++++++++++++++++------------------
 src/SinglePhotoPage.vala        |  10 +---
 src/direct/DirectPhotoPage.vala |  15 ++----
 src/sidebar/Tree.vala           |   4 +-
 src/tags/TagsBranch.vala        |  16 +------
 src/util/ui.vala                |  17 +++++++
 9 files changed, 131 insertions(+), 166 deletions(-)
---
diff --git a/src/CheckerboardPage.vala b/src/CheckerboardPage.vala
index a1750580..2bb3e5f9 100644
--- a/src/CheckerboardPage.vala
+++ b/src/CheckerboardPage.vala
@@ -89,9 +89,7 @@ public abstract class CheckerboardPage : Page {
     private Gtk.PopoverMenu item_context_menu;
     public virtual Gtk.PopoverMenu? get_item_context_menu() {
         if (item_context_menu == null) {
-            var model = this.builder.get_object (item_context_menu_path)
-                as GLib.MenuModel;
-            item_context_menu = new Gtk.PopoverMenu.from_model (model);
+            item_context_menu = get_popover_menu_from_builder (this.builder, item_context_menu_path, this);
         }
 
         return item_context_menu;
@@ -103,9 +101,7 @@ public abstract class CheckerboardPage : Page {
             return null;
 
         if (page_context_menu == null) {
-            var model = this.builder.get_object (page_context_menu_path)
-                as GLib.MenuModel;
-            page_context_menu = new Gtk.PopoverMenu.from_model (model);
+            page_context_menu = get_popover_menu_from_builder (this.builder, page_context_menu_path, this);
         }
 
         return page_context_menu;
@@ -473,17 +469,16 @@ public abstract class CheckerboardPage : Page {
         return true;
     }
 
-#if 0
-    protected override bool on_right_click(Gdk.EventButton event) {
+    protected override bool on_right_click(Gtk.EventController event, int press, double x, double y) {
         // only interested in single-clicks for now
-        if (event.type != Gdk.EventType.BUTTON_PRESS)
+        if (press != 1)
             return false;
 
         // get what's right-clicked upon
-        CheckerboardItem item = get_item_at_pixel(event.x, event.y);
+        CheckerboardItem item = get_item_at_pixel(x, y);
         if (item != null) {
             // mask out the modifiers we're interested in
-            switch (event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK)) {
+            switch (event.get_current_event_state() & (Gdk.ModifierType.CONTROL_MASK | 
Gdk.ModifierType.SHIFT_MASK)) {
                 case Gdk.ModifierType.CONTROL_MASK:
                     // chosen item is toggled
                     Marker marker = get_view().mark(item);
@@ -514,10 +509,9 @@ public abstract class CheckerboardPage : Page {
             get_view().unselect_all();
         }
 
-        Gtk.Menu context_menu = get_context_menu();
-        return popup_context_menu(context_menu, event);
+        Gtk.PopoverMenu context_menu = get_context_menu();
+        return popup_context_menu(context_menu, x, y);
     }
-    #endif
 
     protected virtual bool on_mouse_over(CheckerboardItem? item, int x, int y, Gdk.ModifierType mask) {
         if (item != null)
diff --git a/src/MediaPage.vala b/src/MediaPage.vala
index 37e1c792..04c65acb 100644
--- a/src/MediaPage.vala
+++ b/src/MediaPage.vala
@@ -379,9 +379,8 @@ public abstract class MediaPage : CheckerboardPage {
         return new ZoomSliderAssembly();
     }
 
-#if 0
-    protected override bool on_mousewheel_up(Gdk.EventScroll event) {
-        if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
+    protected override bool on_mousewheel_up(Gtk.EventControllerScroll event) {
+        if ((event.get_current_event_state() & Gdk.ModifierType.CONTROL_MASK) != 0) {
             increase_zoom_level();
             return true;
         } else {
@@ -389,15 +388,14 @@ public abstract class MediaPage : CheckerboardPage {
         }
     }
 
-    protected override bool on_mousewheel_down(Gdk.EventScroll event) {
-        if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
+    protected override bool on_mousewheel_down(Gtk.EventControllerScroll event) {
+        if ((event.get_current_event_state() & Gdk.ModifierType.CONTROL_MASK) != 0) {
             decrease_zoom_level();
             return true;
         } else {
             return base.on_mousewheel_down(event);
         }
     }
-    #endif
     
     private void on_send_to() {
         DesktopIntegration.send_to((Gee.Collection<MediaSource>) get_view().get_selected_sources());
diff --git a/src/Page.vala b/src/Page.vala
index 7dce6bad..cef3c56f 100644
--- a/src/Page.vala
+++ b/src/Page.vala
@@ -90,6 +90,7 @@ public abstract class Page : Gtk.Box {
     // Event controllers
     private Gtk.GestureClick clicks;
     private Gtk.EventControllerMotion motion;
+    private Gtk.EventControllerScroll scroll;
     
     protected Page(string page_name) {
         Object (orientation: Gtk.Orientation.HORIZONTAL);
@@ -185,6 +186,8 @@ public abstract class Page : Gtk.Box {
         clicks.set_name ("CheckerboardPage click source");
         clicks.set_button (0); // Listen to all buttons
         clicks.set_exclusive (true); // TODO: Need to be true or false?
+        clicks.pressed.connect (on_button_pressed_internal);
+        clicks.released.connect (on_button_released_internal);
         event_source.add_controller (clicks);
 
         motion = new Gtk.EventControllerMotion ();
@@ -193,8 +196,10 @@ public abstract class Page : Gtk.Box {
         motion.leave.connect(on_leave_notify_event);
         event_source.add_controller (motion);
 
-        clicks.pressed.connect (on_button_pressed_internal);
-        clicks.released.connect (on_button_released_internal);
+        scroll = new Gtk.EventControllerScroll(Gtk.EventControllerScrollFlags.BOTH_AXES
+            | Gtk.EventControllerScrollFlags.DISCRETE);
+        scroll.scroll.connect(on_mousewheel_internal);
+        event_source.add_controller(scroll);
 
 #if 0
         // interested in mouse button and motion events on the event source
@@ -202,11 +207,6 @@ public abstract class Page : Gtk.Box {
             | Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_HINT_MASK
             | Gdk.EventMask.BUTTON_MOTION_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK
             | Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK);
-        event_source.button_press_event.connect(on_button_pressed_internal);
-        event_source.button_release_event.connect(on_button_released_internal);
-        event_source.motion_notify_event.connect(on_motion_internal);
-        event_source.leave_notify_event.connect(on_leave_notify_event);
-        event_source.scroll_event.connect(on_mousewheel_internal);
         event_source.realize.connect(on_event_source_realize);
         #endif 
     }
@@ -219,18 +219,15 @@ public abstract class Page : Gtk.Box {
         clicks = null;
         event_source.remove_controller (motion);
         motion = null;
+        event_source.remove_controller (scroll);
+        scroll = null;
         
     #if 0
-        event_source.button_press_event.disconnect(on_button_pressed_internal);
-        event_source.button_release_event.disconnect(on_button_released_internal);
-        event_source.motion_notify_event.disconnect(on_motion_internal);
-        event_source.leave_notify_event.disconnect(on_leave_notify_event);
-        event_source.scroll_event.disconnect(on_mousewheel_internal);
         
         disable_drag_source();
         
-        event_source = null;
         #endif
+        event_source = null;
     }
     
     public Gtk.Widget? get_event_source() {
@@ -983,16 +980,19 @@ public abstract class Page : Gtk.Box {
     
     protected virtual void on_move_finished(Gdk.Rectangle rect) {
     }
+    #endif    
     
     protected virtual void on_resize(Gdk.Rectangle rect) {
     }
     
     protected virtual void on_resize_start(Gdk.Rectangle rect) {
     }
-    
+
     protected virtual void on_resize_finished(Gdk.Rectangle rect) {
     }
-    
+
+    #if 0
+
     protected virtual bool on_configure(Gdk.EventConfigure event, Gdk.Rectangle rect) {
         return false;
     }
@@ -1047,10 +1047,10 @@ public abstract class Page : Gtk.Box {
         #if 0
         if (report_move_finished)
             on_move_finished((Gdk.Rectangle) allocation);
+            #endif
         
         if (report_resize_finished)
             on_resize_finished((Gdk.Rectangle) allocation);
-            #endif
         
         last_configure_ms = 0;
         report_move_finished = false;
@@ -1074,87 +1074,58 @@ public abstract class Page : Gtk.Box {
         // todo: stop propagation?
     }
 
-    #if 0
-    private bool on_mousewheel_internal(Gdk.EventScroll event) {
-        switch (event.direction) {
-            case Gdk.ScrollDirection.UP:
-                return on_mousewheel_up(event);
-
-            case Gdk.ScrollDirection.DOWN:
-                return on_mousewheel_down(event);
-            
-            case Gdk.ScrollDirection.LEFT:
-                return on_mousewheel_left(event);
-
-            case Gdk.ScrollDirection.RIGHT:
-                return on_mousewheel_right(event);
-
-            case Gdk.ScrollDirection.SMOOTH:
-                {
-                    double dx, dy;
-                    event.get_scroll_deltas(out dx, out dy);
-
-                    if (dy <= -1.0)
-                        return on_mousewheel_up(event);
-                    else if (dy >= 1.0)
-                        return on_mousewheel_down(event);
-                    else if (dx <= -1.0)
-                        return on_mousewheel_left(event);
-                    else if (dx >= 1.0)
-                        return on_mousewheel_right(event);
-                    else
-                        return true;
-                }
-           
-            default:
-                return false;
-        }
+    private bool on_mousewheel_internal(Gtk.EventControllerScroll event, double dx, double dy) {
+        if (dy <= -1.0)
+            return on_mousewheel_up(event);
+        else if (dy >= 1.0)
+            return on_mousewheel_down(event);
+        else if (dx <= -1.0)
+            return on_mousewheel_left(event);
+        else if (dx >= 1.0)
+            return on_mousewheel_right(event);
+        else
+            return false;
     }
     
-    protected virtual bool on_mousewheel_up(Gdk.EventScroll event) {
+    protected virtual bool on_mousewheel_up(Gtk.EventControllerScroll event) {
         return false;
     }
     
-    protected virtual bool on_mousewheel_down(Gdk.EventScroll event) {
+    protected virtual bool on_mousewheel_down(Gtk.EventControllerScroll event) {
         return false;
     }
     
-    protected virtual bool on_mousewheel_left(Gdk.EventScroll event) {
+    protected virtual bool on_mousewheel_left(Gtk.EventControllerScroll event) {
         return false;
     }
     
-    protected virtual bool on_mousewheel_right(Gdk.EventScroll event) {
+    protected virtual bool on_mousewheel_right(Gtk.EventControllerScroll event) {
         return false;
     }
-    #endif
     
     protected virtual bool on_context_keypress() {
         return false;
     }
     
-    #if 0
-    protected virtual bool on_context_buttonpress(Gdk.EventButton event) {
+    protected virtual bool on_context_buttonpress(Gtk.EventController event, double x, double y) {
         return false;
     }
-    #endif
     
 
     protected virtual bool on_context_invoked() {
         return true;
     }
 
-#if 0
-    protected bool popup_context_menu(Gtk.Menu? context_menu,
-        Gdk.EventButton? event = null) {
+    protected bool popup_context_menu(Gtk.PopoverMenu? context_menu, double x, double y) {
 
         if (context_menu == null || !on_context_invoked())
             return false;
 
-        context_menu.popup_at_pointer(event);
+        context_menu.set_pointing_to ({(int)x, (int)y, 1, 1});
+        context_menu.popup();
 
         return true;
     }
-    #endif
 
 
 #if 0
diff --git a/src/PhotoPage.vala b/src/PhotoPage.vala
index c0da8cff..d2eb7d96 100644
--- a/src/PhotoPage.vala
+++ b/src/PhotoPage.vala
@@ -608,11 +608,13 @@ public abstract class EditingHostPage : SinglePhotoPage {
     }
     #endif
 
-# if 0
-    private Gdk.Point get_cursor_wrt_viewport(Gdk.EventScroll event) {
+    private Gdk.Point get_cursor_wrt_viewport(Gtk.EventControllerScroll event) {
         Gdk.Point cursor_wrt_canvas = {0};
-        cursor_wrt_canvas.x = (int) event.x;
-        cursor_wrt_canvas.y = (int) event.y;
+        double x;
+        double y;
+        event.get_current_event().get_position(out x, out y);
+        cursor_wrt_canvas.x = (int) x;
+        cursor_wrt_canvas.y = (int) y;
 
         Gdk.Rectangle viewport_wrt_canvas = get_zoom_state().get_viewing_rectangle_wrt_screen();
         Gdk.Point result = {0};
@@ -624,7 +626,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
         return result;
     }
 
-    private Gdk.Point get_cursor_wrt_viewport_center(Gdk.EventScroll event) {
+    private Gdk.Point get_cursor_wrt_viewport_center(Gtk.EventControllerScroll event) {
         Gdk.Point cursor_wrt_viewport = get_cursor_wrt_viewport(event);
         Gdk.Rectangle viewport_wrt_canvas = get_zoom_state().get_viewing_rectangle_wrt_screen();
         
@@ -635,7 +637,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
         return subtract_points(cursor_wrt_viewport, viewport_center);
     }
 
-    private Gdk.Point get_iso_pixel_under_cursor(Gdk.EventScroll event) {
+    private Gdk.Point get_iso_pixel_under_cursor(Gtk.EventControllerScroll event) {
         Gdk.Point viewport_center_iso = scale_point(get_zoom_state().get_viewport_center(),
             1.0 / get_zoom_state().get_zoom_factor());
 
@@ -644,7 +646,6 @@ public abstract class EditingHostPage : SinglePhotoPage {
 
         return add_points(viewport_center_iso, cursor_wrt_center_iso);
     }
-    #endif
 
     private double snap_interpolation_factor(double interp) {
         if (interp < 0.03)
@@ -659,8 +660,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
         return snap_interpolation_factor(get_zoom_state().get_interpolation_factor() + adjustment);
     }
 
-#if 0
-    private void zoom_about_event_cursor_point(Gdk.EventScroll event, double zoom_increment) {
+    private void zoom_about_event_cursor_point(Gtk.EventControllerScroll event, double zoom_increment) {
         if (photo_missing)
             return;
 
@@ -693,7 +693,6 @@ public abstract class EditingHostPage : SinglePhotoPage {
 
         update_cursor_for_zoom_context();
     }
-    #endif
 
     protected void snap_zoom_to_min() {
         zoom_slider.set_value(0.0);
@@ -753,8 +752,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
         return zoom_buffer;
     }
     
-    #if 0
-    protected override bool on_mousewheel_up(Gdk.EventScroll event) {
+    protected override bool on_mousewheel_up(Gtk.EventControllerScroll event) {
         if (get_zoom_state().is_max() || !zoom_slider.get_sensitive())
             return false;
 
@@ -762,14 +760,13 @@ public abstract class EditingHostPage : SinglePhotoPage {
         return true;
     }
     
-    protected override bool on_mousewheel_down(Gdk.EventScroll event) {
+    protected override bool on_mousewheel_down(Gtk.EventControllerScroll event) {
         if (get_zoom_state().is_min() || !zoom_slider.get_sensitive())
             return false;
         
         zoom_about_event_cursor_point(event, -ZOOM_INCREMENT_SIZE);
         return true;
     }
-    #endif
 
     protected override void restore_zoom_state() {
         base.restore_zoom_state();
@@ -1322,11 +1319,11 @@ public abstract class EditingHostPage : SinglePhotoPage {
         
         return false;
     }
-  #if 0  
+
     protected override void on_resize(Gdk.Rectangle rect) {
         base.on_resize(rect);
 
-        track_tool_window();
+        //track_tool_window();
     }
     
     protected override void on_resize_finished(Gdk.Rectangle rect) {
@@ -1338,7 +1335,6 @@ public abstract class EditingHostPage : SinglePhotoPage {
         
         update_pixbuf();
     }
-    #endif
     
     private void on_viewport_resized() {
         // this means the viewport (the display area) has changed, but not necessarily the
@@ -1553,33 +1549,38 @@ public abstract class EditingHostPage : SinglePhotoPage {
         if (command != null)
             get_command_manager().execute(command);
     }
+    #endif
     
     // This virtual method is called only when the user double-clicks on the page and no tool
     // is active
-    protected virtual bool on_double_click(Gdk.EventButton event) {
+    protected virtual bool on_double_click(Gtk.EventController event, double x, double y) {
         return false;
     }
     
     // Return true to block the DnD handler from activating a drag
-    protected override bool on_left_click(Gdk.EventButton event) {
+    protected override bool on_left_click(Gtk.EventController event, int press, double x, double y) {
         // report double-click if no tool is active, otherwise all double-clicks are eaten
+        #if 0
         if (event.type == Gdk.EventType.2BUTTON_PRESS)
             return (current_tool == null) ? on_double_click(event) : false;
+        #else
+            if (press == 2) {
+                on_double_click (event, x, y);
+            }
+        #endif
         
-        int x = (int) event.x;
-        int y = (int) event.y;
-
         // if no editing tool, then determine whether we should start a pan operation over the
         // zoomed photo or fall through to the default DnD behavior if we're not zoomed
-        if ((current_tool == null) && (zoom_slider.get_value() != 0.0)) {
-            zoom_pan_start_point.x = (int) event.x;
-            zoom_pan_start_point.y = (int) event.y;
+        if (/*(current_tool == null) && */(zoom_slider.get_value() != 0.0)) {
+            zoom_pan_start_point.x = (int) x;
+            zoom_pan_start_point.y = (int) y;
             is_pan_in_progress = true;
             suspend_cursor_hiding();
 
             return true;
         }
 
+#if 0
         // default behavior when photo isn't zoomed -- return false to start DnD operation
         if (current_tool == null) {
             return false;
@@ -1594,13 +1595,16 @@ public abstract class EditingHostPage : SinglePhotoPage {
         
         // block DnD handlers if tool is enabled
         return true;
+#else
+        return false;
+#endif
     }
     
-    protected override bool on_left_released(Gdk.EventButton event) {
+    protected override bool on_left_released(Gtk.EventController event, int press, double x, double y) {
         if (is_pan_in_progress) {
             Gdk.Point viewport_center = get_zoom_state().get_viewport_center();
-            int delta_x = ((int) event.x) - zoom_pan_start_point.x;
-            int delta_y = ((int) event.y) - zoom_pan_start_point.y;
+            int delta_x = ((int) x) - zoom_pan_start_point.x;
+            int delta_y = ((int) y) - zoom_pan_start_point.y;
             viewport_center.x -= delta_x;
             viewport_center.y -= delta_y;
 
@@ -1612,6 +1616,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
             restore_cursor_hiding();
         }
 
+#if 0
         // report all releases, as it's possible the user click and dragged from inside the
         // pixbuf to the gutters
         if (current_tool == null)
@@ -1621,14 +1626,20 @@ public abstract class EditingHostPage : SinglePhotoPage {
 
         if (current_tool.get_tool_window() != null)
             current_tool.get_tool_window().present();
+#endif
         
         return false;
     }
     
-    protected override bool on_right_click(Gdk.EventButton event) {
-        return on_context_buttonpress(event);
+    protected override bool on_right_click(Gtk.EventController event, int press, double x, double y) {
+        if (press != 1) return false;
+        var sequence = ((Gtk.GestureSingle)event).get_current_sequence();
+        var last_event = ((Gtk.Gesture)event).get_last_event(sequence);
+
+        if (!last_event.triggers_context_menu()) return false;
+
+        return on_context_buttonpress(event, x, y);
     }
-    #endif
     
     private void on_photos_altered(Gee.Map<DataObject, Alteration> map) {
         if (!map.has_key(get_photo()))
@@ -1674,8 +1685,8 @@ public abstract class EditingHostPage : SinglePhotoPage {
     }
     
     // Return true to block the DnD handler from activating a drag
-    #if 0
-    protected override bool on_motion(Gdk.EventMotion event, int x, int y, Gdk.ModifierType mask) {
+    protected override bool on_motion(Gtk.EventControllerMotion event, double x, double y, Gdk.ModifierType 
mask) {
+        #if 0
         if (current_tool != null) {
             current_tool.on_motion(x, y, mask);
 
@@ -1684,12 +1695,13 @@ public abstract class EditingHostPage : SinglePhotoPage {
 
             return true;
         }
+        #endif
         
         update_cursor_for_zoom_context();
         
         if (is_pan_in_progress) {
-            int delta_x = ((int) event.x) - zoom_pan_start_point.x;
-            int delta_y = ((int) event.y) - zoom_pan_start_point.y;
+            int delta_x = (int)x - zoom_pan_start_point.x;
+            int delta_y = (int)y - zoom_pan_start_point.y;
 
             Gdk.Point viewport_center = get_zoom_state().get_viewport_center();
             viewport_center.x -= delta_x;
@@ -1704,13 +1716,16 @@ public abstract class EditingHostPage : SinglePhotoPage {
         return base.on_motion(event, x, y, mask);
     }
     
-    protected override bool on_leave_notify_event() {
+    protected override void on_leave_notify_event(Gtk.EventControllerMotion event) {
+        #if 0
         if (current_tool != null)
             return current_tool.on_leave_notify_event();
+            #endif
         
-        return base.on_leave_notify_event();
+        base.on_leave_notify_event(event);
     }
     
+    #if 0
     private void track_tool_window() {
         // if editing tool window is present and the user hasn't touched it, it moves with the window
         if (current_tool != null) {
@@ -2876,26 +2891,23 @@ public class LibraryPhotoPage : EditingHostPage {
         
         return base.on_left_released(event);
     }
+    #endif
 
-    private Gtk.Menu context_menu;
+    private Gtk.PopoverMenu context_menu;
 
-    private Gtk.Menu get_context_menu() {
+    private Gtk.PopoverMenu get_context_menu() {
         if (context_menu == null) {
-            var model = this.builder.get_object ("PhotoContextMenu")
-                as GLib.MenuModel;
-            context_menu = new Gtk.Menu.from_model (model);
-            context_menu.attach_to_widget (this, null);
+            context_menu = get_popover_menu_from_builder (this.builder, "PhotoContextMenu", this);
         }
 
         return this.context_menu;
     }
     
-    protected override bool on_context_buttonpress(Gdk.EventButton event) {
-        popup_context_menu(get_context_menu(), event);
+    protected override bool on_context_buttonpress(Gtk.EventController event, double x, double y) {
+        popup_context_menu(get_context_menu(), x, y);
 
         return true;
     }
-    #endif
 
     protected override bool on_context_keypress() {
         //popup_context_menu(get_context_menu());
diff --git a/src/SinglePhotoPage.vala b/src/SinglePhotoPage.vala
index 15093be4..8e716b13 100644
--- a/src/SinglePhotoPage.vala
+++ b/src/SinglePhotoPage.vala
@@ -51,7 +51,6 @@ public abstract class SinglePhotoPage : Page {
 
         scrolled.set_child(viewport);
 
-#if 0
         canvas.add_events(Gdk.EventMask.EXPOSURE_MASK | Gdk.EventMask.STRUCTURE_MASK 
             | Gdk.EventMask.SUBSTRUCTURE_MASK);
 
@@ -294,17 +293,14 @@ public abstract class SinglePhotoPage : Page {
         internal_repaint(true, null);
     }
 
-#if 0
-// TODO
     protected override void on_resize_finished(Gdk.Rectangle rect) {
         base.on_resize_finished(rect);
 
         // when the resize is completed, do a high-quality repaint
         repaint();
     }
-    #endif
 
-    private bool on_canvas_exposed(Cairo.Context exposed_ctx) {
+    private void on_canvas_exposed(Gtk.DrawingArea da, Cairo.Context exposed_ctx, int width, int height) {
         // draw pixmap onto canvas unless it's not been instantiated, in which case draw black
         // (so either old image or contents of another page is not left on screen)
         if (pixmap != null)
@@ -314,8 +310,6 @@ public abstract class SinglePhotoPage : Page {
 
         exposed_ctx.rectangle(0, 0, get_allocated_width(), get_allocated_height());
         exposed_ctx.paint();
-
-        return true;
     }
 
     protected virtual void new_surface(Cairo.Context ctx, Dimensions ctx_dim) {
@@ -473,7 +467,7 @@ public abstract class SinglePhotoPage : Page {
 
     protected override bool on_context_keypress() {
         //return popup_context_menu(get_page_context_menu());
-        return true;
+        return false;
     }
 
     protected virtual void on_previous_photo() {
diff --git a/src/direct/DirectPhotoPage.vala b/src/direct/DirectPhotoPage.vala
index 09f2ab64..d510a291 100644
--- a/src/direct/DirectPhotoPage.vala
+++ b/src/direct/DirectPhotoPage.vala
@@ -145,26 +145,21 @@ public class DirectPhotoPage : EditingHostPage {
         return get_photo().get_file();
     }
 
-#if 0
-    protected override bool on_context_buttonpress(Gdk.EventButton event) {
-        popup_context_menu(get_context_menu(), event);
+    protected override bool on_context_buttonpress(Gtk.EventController event, double x, double y) {
+        popup_context_menu(get_context_menu(), x, y);
 
         return true;
     }
 
-    private Gtk.Menu context_menu;
+    private Gtk.PopoverMenu context_menu;
 
-    private Gtk.Menu get_context_menu() {
+    private Gtk.PopoverMenu get_context_menu() {
         if (context_menu == null) {
-            var model = this.builder.get_object ("DirectContextMenu")
-                as GLib.MenuModel;
-            context_menu = new Gtk.Menu.from_model (model);
-            context_menu.attach_to_widget (this, null);
+            context_menu = get_popover_menu_from_builder (this.builder, "DirectContextMenu", this);
         }
 
         return this.context_menu;
     }
-    #endif
 
     private void update_zoom_menu_item_sensitivity() {
         set_action_sensitive("IncreaseSize", !get_zoom_state().is_max() && !get_photo_missing());
diff --git a/src/sidebar/Tree.vala b/src/sidebar/Tree.vala
index abc02ea4..c0875ac9 100644
--- a/src/sidebar/Tree.vala
+++ b/src/sidebar/Tree.vala
@@ -201,9 +201,7 @@ public class Sidebar.Tree : Gtk.TreeView {
 
     private void setup_default_context_menu() {
         try {
-            this.builder.add_from_resource(Resources.get_ui("sidebar_default_context.ui"));
-            var model = builder.get_object ("popup-menu") as GLib.MenuModel;
-            this.default_context_menu = new Gtk.PopoverMenu.from_model (model);
+            this.default_context_menu = get_popover_menu_from_builder(builder, "popup-menu", this);
             var group = new GLib.SimpleActionGroup ();
             group.add_action_entries (entries, this);
             this.insert_action_group ("sidebar", group);
diff --git a/src/tags/TagsBranch.vala b/src/tags/TagsBranch.vala
index 12056b08..ef6f10de 100644
--- a/src/tags/TagsBranch.vala
+++ b/src/tags/TagsBranch.vala
@@ -124,25 +124,11 @@ public class Tags.Branch : Sidebar.Branch {
 
 public class Tags.Header : Sidebar.Header, Sidebar.InternalDropTargetEntry, 
     Sidebar.Contextable {
-    private Gtk.Builder builder;
     private Gtk.PopoverMenu? context_menu = null;
     
     public Header() {
         base (_("Tags"), _("Organize and browse your photo’s tags"));
-        setup_context_menu();
-    }
-
-    private void setup_context_menu() {
-        this.builder = new Gtk.Builder ();
-        try {
-            this.builder.add_from_resource(Resources.get_ui("tag_sidebar_context.ui"));
-            var model = builder.get_object ("popup-menu") as GLib.MenuModel;
-            this.context_menu = new Gtk.PopoverMenu.from_model (model);
-        } catch (Error error) {
-            AppWindow.error_message("Error loading UI resource: %s".printf(
-                error.message));
-            Application.get_instance().panic();
-        }
+        this.context_menu = get_popover_menu_from_resource(Resources.get_ui("tag_sidebar_context.ui"), 
"popup-menu", null);
     }
 
     public bool internal_drop_received(Gee.List<MediaSource> media) {
diff --git a/src/util/ui.vala b/src/util/ui.vala
index 00a485cf..e5a0657c 100644
--- a/src/util/ui.vala
+++ b/src/util/ui.vala
@@ -59,6 +59,23 @@ public Gdk.Rectangle get_adjustment_page(Gtk.Adjustment hadj, Gtk.Adjustment vad
     return rect;
 }
 
+Gtk.PopoverMenu get_popover_menu_from_resource(string path, string id, Gtk.Widget? parent) {
+    var builder = new Gtk.Builder.from_resource(path);
+    return get_popover_menu_from_builder(builder, id, parent);
+}
+
+Gtk.PopoverMenu get_popover_menu_from_builder(Gtk.Builder builder, string id, Gtk.Widget? parent) {
+    var model = builder.get_object (id) as GLib.MenuModel;
+    var popover = new Gtk.PopoverMenu.from_model (model);
+    if (parent != null) {
+        popover.set_parent (parent);
+    }
+    popover.set_has_arrow(false);
+
+    return popover;
+}
+
+
 // Verifies that only the mask bits are set in the modifier field, disregarding mouse and 
 // key modifiers that are not normally of concern (i.e. Num Lock, Caps Lock, etc.).  Mask can be
 // one or more bits set, but should only consist of these values:


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]