[shotwell/wip/gtk4] Fix initial display of image



commit 9b3827f91a8ee43b81eeebc2ac688bf694f09da5
Author: Jens Georg <mail jensge org>
Date:   Fri Apr 15 20:54:57 2022 +0200

    Fix initial display of image

 src/AppWindow.vala        | 392 ----------------------------------------------
 src/FullscreenWindow.vala | 275 ++++++++++++++++++++++++++++++++
 src/Page.vala             |  51 ++----
 src/PageWindow.vala       | 126 +++++++++++++++
 src/PhotoPage.vala        |  20 ---
 src/SinglePhotoPage.vala  |   2 +
 src/meson.build           |   2 +
 7 files changed, 418 insertions(+), 450 deletions(-)
---
diff --git a/src/AppWindow.vala b/src/AppWindow.vala
index 697aedb6..eef1ade5 100644
--- a/src/AppWindow.vala
+++ b/src/AppWindow.vala
@@ -4,398 +4,6 @@
  * See the COPYING file in this distribution.
  */
 
-public class FullscreenWindow : PageWindow {
-    public const int TOOLBAR_INVOCATION_MSEC = 250;
-    public const int TOOLBAR_DISMISSAL_SEC = 2;
-    public const int TOOLBAR_CHECK_DISMISSAL_MSEC = 500;
-    
-    private Gtk.Overlay overlay = new Gtk.Overlay();
-    private Gtk.Box toolbar = null;
-    private Gtk.Button close_button = new Gtk.Button();
-    private Gtk.ToggleButton pin_button = new Gtk.ToggleButton();
-    private bool is_toolbar_shown = false;
-    private bool waiting_for_invoke = false;
-    private time_t left_toolbar_time = 0;
-    private bool switched_to = false;
-    private bool is_toolbar_dismissal_enabled;
-
-    private const GLib.ActionEntry[] entries = {
-        { "LeaveFullscreen", on_close }
-    };
-
-    public FullscreenWindow(Page page) {
-        base ();
-
-        set_current_page(page);
-
-        this.add_action_entries (entries, this);
-        const string[] accels = { "F11", null };
-        Application.set_accels_for_action ("win.LeaveFullscreen", accels);
-
-        //set_screen(AppWindow.get_instance().get_screen());
-        
-        // Needed so fullscreen will occur on correct monitor in multi-monitor setups
-        Gdk.Rectangle monitor = get_monitor_geometry();
-        //move(monitor.x, monitor.y);
-        
-        //set_border_width(0);
-
-        // restore pin state
-        is_toolbar_dismissal_enabled = Config.Facade.get_instance().get_pin_toolbar_state();
-        
-        pin_button.set_icon_name("view-pin-symbolic");
-        pin_button.set_label(_("Pin Toolbar"));
-        pin_button.set_tooltip_text(_("Pin the toolbar open"));
-        pin_button.set_active(!is_toolbar_dismissal_enabled);
-        pin_button.clicked.connect(update_toolbar_dismissal);
-        
-        close_button.set_icon_name("view-restore-symbolic");
-        close_button.set_tooltip_text(_("Leave fullscreen"));
-        close_button.set_action_name ("win.LeaveFullscreen");
-        
-        toolbar = page.get_toolbar();
-        //toolbar.set_show_arrow(false);
-        toolbar.valign = Gtk.Align.END;
-        toolbar.halign = Gtk.Align.CENTER;
-        //toolbar.expand = false;
-        toolbar.opacity = Resources.TRANSIENT_WINDOW_OPACITY;
-
-        if (page is SlideshowPage) {
-            // slideshow page doesn't own toolbar to hide it, subscribe to signal instead
-            ((SlideshowPage) page).hide_toolbar.connect(hide_toolbar);
-        } else {
-            // only non-slideshow pages should have pin button
-            toolbar.append(pin_button);
-        }
-
-        page.set_cursor_hide_time(TOOLBAR_DISMISSAL_SEC * 1000);
-        page.start_cursor_hiding();
-
-        toolbar.append(close_button);
-        
-        set_child(overlay);
-        overlay.set_child(page);
-        overlay.add_overlay (toolbar);
-
-        // call to set_default_size() saves one repaint caused by changing
-        // size from default to full screen. In slideshow mode, this change
-        // also causes pixbuf cache updates, so it really saves some work.
-        set_default_size(monitor.width, monitor.height);
-        
-        // need to create a Gdk.Window to set masks
-        fullscreen();
-        show();
-
-        // capture motion events to show the toolbar
-        //add_events(Gdk.EventMask.POINTER_MOTION_MASK);
-        
-        // If toolbar is enabled in "normal" ui OR was pinned in
-        // fullscreen, start off with toolbar invoked, as a clue for the
-        // user. Otherwise leave hidden unless activated by mouse over
-        if (Config.Facade.get_instance().get_display_toolbar() ||
-            !is_toolbar_dismissal_enabled) {
-            invoke_toolbar();
-        } else {
-            hide_toolbar();
-        }
-
-        // Toolbar steals keyboard focus from page, put it back again
-        page.grab_focus ();
-
-        // Do not show menubar in fullscreen
-        set_show_menubar (false);
-    }
-
-    public void disable_toolbar_dismissal() {
-        is_toolbar_dismissal_enabled = false;
-    }
-    
-    public void update_toolbar_dismissal() {
-        is_toolbar_dismissal_enabled = !pin_button.get_active();
-    }
-
-    private Gdk.Rectangle get_monitor_geometry() {
-        #if 0
-        var monitor = get_display().get_monitor_at_window(AppWindow.get_instance().get_window());
-        return monitor.get_geometry();
-        #endif
-
-        return Gdk.Rectangle();
-    }
-    
-    #if 0
-    public override bool configure_event(Gdk.EventConfigure event) {
-        bool result = base.configure_event(event);
-        
-        if (!switched_to) {
-            get_current_page().switched_to();
-            switched_to = true;
-        }
-        
-        return result;
-    }
-
-    public override bool key_press_event(Gdk.EventKey event) {
-        // check for an escape/abort 
-        if (Gdk.keyval_name(event.keyval) == "Escape") {
-            on_close();
-            
-            return true;
-        }
-        
-        // propagate to this (fullscreen) window respecting "stop propagation" result...
-        if (base.key_press_event != null && base.key_press_event(event))
-            return true;
-        
-        // ... then propagate to the underlying window hidden behind this fullscreen one
-        return AppWindow.get_instance().key_press_event(event);
-    }
-    #endif
-    
-    private void on_close() {
-        Config.Facade.get_instance().set_pin_toolbar_state(is_toolbar_dismissal_enabled);
-        hide_toolbar();
-        
-        AppWindow.get_instance().end_fullscreen();
-    }
-    
-    public new void close() {
-        on_close();
-    }
-    
-    public override void dispose() {
-        Page? page = get_current_page();
-        clear_current_page();
-        
-        if (page != null) {
-            page.stop_cursor_hiding();
-            page.switching_from();
-        }
-        
-        base.dispose();
-    }
-    
-    public override bool close_request() {
-        on_close();
-        AppWindow.get_instance().destroy();
-        
-        return true;
-    }
-    
-    #if 0
-    public override bool motion_notify_event(Gdk.EventMotion event) {
-        if (!is_toolbar_shown) {
-            // if pointer is in toolbar height range without the mouse down (i.e. in the middle of
-            // an edit operation) and it stays there the necessary amount of time, invoke the
-            // toolbar
-            if (!waiting_for_invoke && is_pointer_in_toolbar()) {
-                Timeout.add(TOOLBAR_INVOCATION_MSEC, on_check_toolbar_invocation);
-                waiting_for_invoke = true;
-            }
-        }
-        
-        return (base.motion_notify_event != null) ? base.motion_notify_event(event) : false;
-    }
-    #endif
-    
-    private bool is_pointer_in_toolbar() {
-        var seat = get_display().get_default_seat();
-        if (seat == null) {
-            debug("No seat for display");
-            
-            return false;
-        }
-        
-        #if 0
-        int py;
-        seat.get_pointer().get_position(null, null, out py);
-        
-        int wy;
-        toolbar.get_window().get_geometry(null, out wy, null, null);
-
-        return (py >= wy);
-        #endif
-        return false;
-    }
-    
-    private bool on_check_toolbar_invocation() {
-        waiting_for_invoke = false;
-        
-        if (is_toolbar_shown)
-            return false;
-        
-        if (!is_pointer_in_toolbar())
-            return false;
-        
-        invoke_toolbar();
-        
-        return false;
-    }
-    
-    private void invoke_toolbar() {
-        toolbar.show();
-
-        is_toolbar_shown = true;
-        
-        Timeout.add(TOOLBAR_CHECK_DISMISSAL_MSEC, on_check_toolbar_dismissal);
-    }
-    
-    private bool on_check_toolbar_dismissal() {
-        if (!is_toolbar_shown)
-            return false;
-        
-        // if dismissal is disabled, keep open but keep checking
-        if ((!is_toolbar_dismissal_enabled))
-            return true;
-        
-        // if the pointer is in toolbar range, keep it alive, but keep checking
-        if (is_pointer_in_toolbar()) {
-            left_toolbar_time = 0;
-
-            return true;
-        }
-        
-        // if this is the first time noticed, start the timer and keep checking
-        if (left_toolbar_time == 0) {
-            left_toolbar_time = time_t();
-            
-            return true;
-        }
-        
-        // see if enough time has elapsed
-        time_t now = time_t();
-        assert(now >= left_toolbar_time);
-
-        if (now - left_toolbar_time < TOOLBAR_DISMISSAL_SEC)
-            return true;
-        
-        hide_toolbar();
-        
-        return false;
-    }
-    
-    private void hide_toolbar() {
-        toolbar.hide();
-        is_toolbar_shown = false;
-    }
-}
-
-// PageWindow is a Gtk.Window with essential functions for hosting a Page.  There may be more than
-// one PageWindow in the system, and closing one does not imply exiting the application.
-//
-// PageWindow offers support for hosting a single Page; multiple Pages must be handled by the
-// subclass.  A subclass should set current_page to the user-visible Page for it to receive
-// various notifications.  It is the responsibility of the subclass to notify Pages when they're
-// switched to and from, and other aspects of the Page interface.
-public abstract class PageWindow : Gtk.ApplicationWindow {
-    private Page current_page = null;
-    private int busy_counter = 0;
-    
-    protected virtual void switched_pages(Page? old_page, Page? new_page) {
-    }
-    
-    protected PageWindow() {
-        Object (application: Application.get_instance().get_system_app ());
-
-        // the current page needs to know when modifier keys are pressed
-        #if 0
-        add_events(Gdk.EventMask.KEY_PRESS_MASK | Gdk.EventMask.KEY_RELEASE_MASK
-            | Gdk.EventMask.STRUCTURE_MASK);
-            #endif
-        set_show_menubar (true);
-
-    }
-    
-    public Page? get_current_page() {
-        return current_page;
-    }
-    
-    public virtual void set_current_page(Page page) {
-        if (current_page != null)
-            current_page.clear_container();
-        
-        Page? old_page = current_page;
-        current_page = page;
-        current_page.set_container(this);
-        
-        switched_pages(old_page, page);
-    }
-    
-    public virtual void clear_current_page() {
-        if (current_page != null)
-            current_page.clear_container();
-        
-        Page? old_page = current_page;
-        current_page = null;
-        
-        switched_pages(old_page, null);
-    }
-    
-    #if 0
-    public override bool key_press_event(Gdk.EventKey event) {
-        if (get_focus() is Gtk.Entry && get_focus().key_press_event(event))
-            return true;
-        
-        if (current_page != null && current_page.notify_app_key_pressed(event))
-            return true;
-        
-        return (base.key_press_event != null) ? base.key_press_event(event) : false;
-    }
-    
-    public override bool key_release_event(Gdk.EventKey event) {
-        if (get_focus() is Gtk.Entry && get_focus().key_release_event(event))
-            return true;
-       
-        if (current_page != null && current_page.notify_app_key_released(event))
-                return true;
-        
-        return (base.key_release_event != null) ? base.key_release_event(event) : false;
-    }
-
-    public override bool focus_in_event(Gdk.EventFocus event) {
-        if (current_page != null && current_page.notify_app_focus_in(event))
-                return true;
-        
-        return (base.focus_in_event != null) ? base.focus_in_event(event) : false;
-    }
-
-    public override bool focus_out_event(Gdk.EventFocus event) {
-        if (current_page != null && current_page.notify_app_focus_out(event))
-                return true;
-        
-        return (base.focus_out_event != null) ? base.focus_out_event(event) : false;
-    }
-    
-    public override bool configure_event(Gdk.EventConfigure event) {
-        if (current_page != null) {
-            if (current_page.notify_configure_event(event))
-                return true;
-        }
-
-        return (base.configure_event != null) ? base.configure_event(event) : false;
-    }
-    #endif
-
-    public void set_busy_cursor() {
-        if (busy_counter++ > 0)
-            return;
-
-        set_cursor (new Gdk.Cursor.from_name ("wait", null));
-    }
-    
-    public void set_normal_cursor() {
-        if (busy_counter <= 0) {
-            busy_counter = 0;
-            return;
-        } else if (--busy_counter > 0) {
-            return;
-        }
-
-        set_cursor (new Gdk.Cursor.from_name ("default", null));
-    }
-    
-}
-
 // AppWindow is the parent window for most windows in Shotwell (FullscreenWindow is the exception).
 // There are multiple types of AppWindows (LibraryWindow, DirectWindow) for different tasks, but only 
 // one AppWindow may exist per process.  Thus, if the user closes an AppWindow, the program exits.
diff --git a/src/FullscreenWindow.vala b/src/FullscreenWindow.vala
new file mode 100644
index 00000000..31870113
--- /dev/null
+++ b/src/FullscreenWindow.vala
@@ -0,0 +1,275 @@
+public class FullscreenWindow : PageWindow {
+    public const int TOOLBAR_INVOCATION_MSEC = 250;
+    public const int TOOLBAR_DISMISSAL_SEC = 2;
+    public const int TOOLBAR_CHECK_DISMISSAL_MSEC = 500;
+    
+    private Gtk.Overlay overlay = new Gtk.Overlay();
+    private Gtk.Box toolbar = null;
+    private Gtk.Button close_button = new Gtk.Button();
+    private Gtk.ToggleButton pin_button = new Gtk.ToggleButton();
+    private bool is_toolbar_shown = false;
+    private bool waiting_for_invoke = false;
+    private time_t left_toolbar_time = 0;
+    private bool switched_to = false;
+    private bool is_toolbar_dismissal_enabled;
+
+    private const GLib.ActionEntry[] entries = {
+        { "LeaveFullscreen", on_close }
+    };
+
+    public FullscreenWindow(Page page) {
+        base ();
+
+        set_current_page(page);
+
+        this.add_action_entries (entries, this);
+        const string[] accels = { "F11", null };
+        Application.set_accels_for_action ("win.LeaveFullscreen", accels);
+
+        //set_screen(AppWindow.get_instance().get_screen());
+        
+        // Needed so fullscreen will occur on correct monitor in multi-monitor setups
+        Gdk.Rectangle monitor = get_monitor_geometry();
+        //move(monitor.x, monitor.y);
+        
+        //set_border_width(0);
+
+        // restore pin state
+        is_toolbar_dismissal_enabled = Config.Facade.get_instance().get_pin_toolbar_state();
+        
+        pin_button.set_icon_name("view-pin-symbolic");
+        pin_button.set_label(_("Pin Toolbar"));
+        pin_button.set_tooltip_text(_("Pin the toolbar open"));
+        pin_button.set_active(!is_toolbar_dismissal_enabled);
+        pin_button.clicked.connect(update_toolbar_dismissal);
+        
+        close_button.set_icon_name("view-restore-symbolic");
+        close_button.set_tooltip_text(_("Leave fullscreen"));
+        close_button.set_action_name ("win.LeaveFullscreen");
+        
+        toolbar = page.get_toolbar();
+        //toolbar.set_show_arrow(false);
+        toolbar.valign = Gtk.Align.END;
+        toolbar.halign = Gtk.Align.CENTER;
+        //toolbar.expand = false;
+        toolbar.opacity = Resources.TRANSIENT_WINDOW_OPACITY;
+
+        if (page is SlideshowPage) {
+            // slideshow page doesn't own toolbar to hide it, subscribe to signal instead
+            ((SlideshowPage) page).hide_toolbar.connect(hide_toolbar);
+        } else {
+            // only non-slideshow pages should have pin button
+            toolbar.append(pin_button);
+        }
+
+        page.set_cursor_hide_time(TOOLBAR_DISMISSAL_SEC * 1000);
+        page.start_cursor_hiding();
+
+        toolbar.append(close_button);
+        
+        set_child(overlay);
+        overlay.set_child(page);
+        overlay.add_overlay (toolbar);
+
+        // call to set_default_size() saves one repaint caused by changing
+        // size from default to full screen. In slideshow mode, this change
+        // also causes pixbuf cache updates, so it really saves some work.
+        set_default_size(monitor.width, monitor.height);
+        
+        // need to create a Gdk.Window to set masks
+        fullscreen();
+        show();
+
+        // capture motion events to show the toolbar
+        //add_events(Gdk.EventMask.POINTER_MOTION_MASK);
+        
+        // If toolbar is enabled in "normal" ui OR was pinned in
+        // fullscreen, start off with toolbar invoked, as a clue for the
+        // user. Otherwise leave hidden unless activated by mouse over
+        if (Config.Facade.get_instance().get_display_toolbar() ||
+            !is_toolbar_dismissal_enabled) {
+            invoke_toolbar();
+        } else {
+            hide_toolbar();
+        }
+
+        // Toolbar steals keyboard focus from page, put it back again
+        page.grab_focus ();
+
+        // Do not show menubar in fullscreen
+        set_show_menubar (false);
+    }
+
+    public void disable_toolbar_dismissal() {
+        is_toolbar_dismissal_enabled = false;
+    }
+    
+    public void update_toolbar_dismissal() {
+        is_toolbar_dismissal_enabled = !pin_button.get_active();
+    }
+
+    private Gdk.Rectangle get_monitor_geometry() {
+        #if 0
+        var monitor = get_display().get_monitor_at_window(AppWindow.get_instance().get_window());
+        return monitor.get_geometry();
+        #endif
+
+        return Gdk.Rectangle();
+    }
+    
+    public override bool configure_event(int width, int height) {
+        bool result = base.configure_event(width, height);
+        
+        if (!switched_to) {
+            get_current_page().switched_to();
+            switched_to = true;
+        }
+        
+        return result;
+    }
+
+    #if 0
+    public override bool key_press_event(Gdk.EventKey event) {
+        // check for an escape/abort 
+        if (Gdk.keyval_name(event.keyval) == "Escape") {
+            on_close();
+            
+            return true;
+        }
+        
+        // propagate to this (fullscreen) window respecting "stop propagation" result...
+        if (base.key_press_event != null && base.key_press_event(event))
+            return true;
+        
+        // ... then propagate to the underlying window hidden behind this fullscreen one
+        return AppWindow.get_instance().key_press_event(event);
+    }
+    #endif
+    
+    private void on_close() {
+        Config.Facade.get_instance().set_pin_toolbar_state(is_toolbar_dismissal_enabled);
+        hide_toolbar();
+        
+        AppWindow.get_instance().end_fullscreen();
+    }
+    
+    public new void close() {
+        on_close();
+    }
+    
+    public override void dispose() {
+        Page? page = get_current_page();
+        clear_current_page();
+        
+        if (page != null) {
+            page.stop_cursor_hiding();
+            page.switching_from();
+        }
+        
+        base.dispose();
+    }
+    
+    public override bool close_request() {
+        on_close();
+        AppWindow.get_instance().destroy();
+        
+        return true;
+    }
+    
+    #if 0
+    public override bool motion_notify_event(Gdk.EventMotion event) {
+        if (!is_toolbar_shown) {
+            // if pointer is in toolbar height range without the mouse down (i.e. in the middle of
+            // an edit operation) and it stays there the necessary amount of time, invoke the
+            // toolbar
+            if (!waiting_for_invoke && is_pointer_in_toolbar()) {
+                Timeout.add(TOOLBAR_INVOCATION_MSEC, on_check_toolbar_invocation);
+                waiting_for_invoke = true;
+            }
+        }
+        
+        return (base.motion_notify_event != null) ? base.motion_notify_event(event) : false;
+    }
+    #endif
+    
+    private bool is_pointer_in_toolbar() {
+        var seat = get_display().get_default_seat();
+        if (seat == null) {
+            debug("No seat for display");
+            
+            return false;
+        }
+        
+        #if 0
+        int py;
+        seat.get_pointer().get_position(null, null, out py);
+        
+        int wy;
+        toolbar.get_window().get_geometry(null, out wy, null, null);
+
+        return (py >= wy);
+        #endif
+        return false;
+    }
+    
+    private bool on_check_toolbar_invocation() {
+        waiting_for_invoke = false;
+        
+        if (is_toolbar_shown)
+            return false;
+        
+        if (!is_pointer_in_toolbar())
+            return false;
+        
+        invoke_toolbar();
+        
+        return false;
+    }
+    
+    private void invoke_toolbar() {
+        toolbar.show();
+
+        is_toolbar_shown = true;
+        
+        Timeout.add(TOOLBAR_CHECK_DISMISSAL_MSEC, on_check_toolbar_dismissal);
+    }
+    
+    private bool on_check_toolbar_dismissal() {
+        if (!is_toolbar_shown)
+            return false;
+        
+        // if dismissal is disabled, keep open but keep checking
+        if ((!is_toolbar_dismissal_enabled))
+            return true;
+        
+        // if the pointer is in toolbar range, keep it alive, but keep checking
+        if (is_pointer_in_toolbar()) {
+            left_toolbar_time = 0;
+
+            return true;
+        }
+        
+        // if this is the first time noticed, start the timer and keep checking
+        if (left_toolbar_time == 0) {
+            left_toolbar_time = time_t();
+            
+            return true;
+        }
+        
+        // see if enough time has elapsed
+        time_t now = time_t();
+        assert(now >= left_toolbar_time);
+
+        if (now - left_toolbar_time < TOOLBAR_DISMISSAL_SEC)
+            return true;
+        
+        hide_toolbar();
+        
+        return false;
+    }
+    
+    private void hide_toolbar() {
+        toolbar.hide();
+        is_toolbar_shown = false;
+    }
+}
diff --git a/src/Page.vala b/src/Page.vala
index cef3c56f..73184b35 100644
--- a/src/Page.vala
+++ b/src/Page.vala
@@ -971,16 +971,7 @@ public abstract class Page : Gtk.Box {
     public bool notify_app_focus_out(Gdk.EventFocus event) {
         return false;
     }
-    
-    protected virtual void on_move(Gdk.Rectangle rect) {
-    }
-    
-    protected virtual void on_move_start(Gdk.Rectangle rect) {
-    }
-    
-    protected virtual void on_move_finished(Gdk.Rectangle rect) {
-    }
-    #endif    
+    #endif
     
     protected virtual void on_resize(Gdk.Rectangle rect) {
     }
@@ -991,26 +982,19 @@ public abstract class Page : Gtk.Box {
     protected virtual void on_resize_finished(Gdk.Rectangle rect) {
     }
 
-    #if 0
-
-    protected virtual bool on_configure(Gdk.EventConfigure event, Gdk.Rectangle rect) {
+    protected virtual bool on_configure(Gdk.Rectangle rect) {
         return false;
     }
-    
-    public bool notify_configure_event(Gdk.EventConfigure event) {
+
+    public bool notify_configure_event(int width, int height) {
         Gdk.Rectangle rect = Gdk.Rectangle();
-        rect.x = event.x;
-        rect.y = event.y;
-        rect.width = event.width;
-        rect.height = event.height;
+        rect.x = 0;
+        rect.y = 0;
+        rect.width = width;
+        rect.height = height;
         
         // special case events, to report when a configure first starts (and appears to end)
-        if (last_configure_ms == 0) {
-            if (last_position.x != rect.x || last_position.y != rect.y) {
-                on_move_start(rect);
-                report_move_finished = true;
-            }
-            
+        if (last_configure_ms == 0) {            
             if (last_position.width != rect.width || last_position.height != rect.height) {
                 on_resize_start(rect);
                 report_resize_finished = true;
@@ -1020,20 +1004,16 @@ public abstract class Page : Gtk.Box {
             // wait time before it's noticed
             Timeout.add(CONSIDER_CONFIGURE_HALTED_MSEC / 8, check_configure_halted);
         }
-        
-        if (last_position.x != rect.x || last_position.y != rect.y)
-            on_move(rect);
-        
+                
         if (last_position.width != rect.width || last_position.height != rect.height)
             on_resize(rect);
         
         last_position = rect;
         last_configure_ms = now_ms();
 
-        return on_configure(event, rect);
+        return on_configure(rect);
     }
-    #endif
-    
+
     private bool check_configure_halted() {
         if (is_destroyed)
             return false;
@@ -1043,12 +1023,7 @@ public abstract class Page : Gtk.Box {
         
         Gtk.Allocation allocation;
         get_allocation(out allocation);
-        
-        #if 0
-        if (report_move_finished)
-            on_move_finished((Gdk.Rectangle) allocation);
-            #endif
-        
+                
         if (report_resize_finished)
             on_resize_finished((Gdk.Rectangle) allocation);
         
diff --git a/src/PageWindow.vala b/src/PageWindow.vala
new file mode 100644
index 00000000..e0fa56a1
--- /dev/null
+++ b/src/PageWindow.vala
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+//
+// PageWindow is a Gtk.Window with essential functions for hosting a Page.  There may be more than
+// one PageWindow in the system, and closing one does not imply exiting the application.
+//
+// PageWindow offers support for hosting a single Page; multiple Pages must be handled by the
+// subclass.  A subclass should set current_page to the user-visible Page for it to receive
+// various notifications.  It is the responsibility of the subclass to notify Pages when they're
+// switched to and from, and other aspects of the Page interface.
+public abstract class PageWindow : Gtk.ApplicationWindow {
+    private Page current_page = null;
+    private int busy_counter = 0;
+    
+    protected virtual void switched_pages(Page? old_page, Page? new_page) {
+    }
+    
+    protected PageWindow() {
+        Object (application: Application.get_instance().get_system_app ());
+
+        // the current page needs to know when modifier keys are pressed
+        #if 0
+        add_events(Gdk.EventMask.KEY_PRESS_MASK | Gdk.EventMask.KEY_RELEASE_MASK
+            | Gdk.EventMask.STRUCTURE_MASK);
+            #endif
+        set_show_menubar (true);
+
+        notify["maximized"].connect(synthesize_configure_event);
+        notify["default-width"].connect(synthesize_configure_event);
+        notify["default-height"].connect(synthesize_configure_event);
+        notify["fullscreened"].connect(synthesize_configure_event);
+    }
+
+    private void synthesize_configure_event() {
+        int width = get_surface().get_width();
+        int height = get_surface().get_height();
+        configure_event(width, height);
+    }
+
+    public virtual bool configure_event(int width, int height) {
+        if (current_page != null) {
+            if (current_page.notify_configure_event(width, height))
+                return true;
+        }      
+        
+        return false;
+    }
+    
+    public Page? get_current_page() {
+        return current_page;
+    }
+    
+    public virtual void set_current_page(Page page) {
+        if (current_page != null)
+            current_page.clear_container();
+        
+        Page? old_page = current_page;
+        current_page = page;
+        current_page.set_container(this);
+        
+        switched_pages(old_page, page);
+    }
+    
+    public virtual void clear_current_page() {
+        if (current_page != null)
+            current_page.clear_container();
+        
+        Page? old_page = current_page;
+        current_page = null;
+        
+        switched_pages(old_page, null);
+    }
+    
+    #if 0
+    public override bool key_press_event(Gdk.EventKey event) {
+        if (get_focus() is Gtk.Entry && get_focus().key_press_event(event))
+            return true;
+        
+        if (current_page != null && current_page.notify_app_key_pressed(event))
+            return true;
+        
+        return (base.key_press_event != null) ? base.key_press_event(event) : false;
+    }
+    
+    public override bool key_release_event(Gdk.EventKey event) {
+        if (get_focus() is Gtk.Entry && get_focus().key_release_event(event))
+            return true;
+       
+        if (current_page != null && current_page.notify_app_key_released(event))
+                return true;
+        
+        return (base.key_release_event != null) ? base.key_release_event(event) : false;
+    }
+
+    public override bool focus_in_event(Gdk.EventFocus event) {
+        if (current_page != null && current_page.notify_app_focus_in(event))
+                return true;
+        
+        return (base.focus_in_event != null) ? base.focus_in_event(event) : false;
+    }
+
+    public override bool focus_out_event(Gdk.EventFocus event) {
+        if (current_page != null && current_page.notify_app_focus_out(event))
+                return true;
+        
+        return (base.focus_out_event != null) ? base.focus_out_event(event) : false;
+    }
+    #endif
+
+    public void set_busy_cursor() {
+        if (busy_counter++ > 0)
+            return;
+
+        set_cursor (new Gdk.Cursor.from_name ("wait", null));
+    }
+    
+    public void set_normal_cursor() {
+        if (busy_counter <= 0) {
+            busy_counter = 0;
+            return;
+        } else if (--busy_counter > 0) {
+            return;
+        }
+
+        set_cursor (new Gdk.Cursor.from_name ("default", null));
+    }
+}
diff --git a/src/PhotoPage.vala b/src/PhotoPage.vala
index 0cd04ae5..38d13e26 100644
--- a/src/PhotoPage.vala
+++ b/src/PhotoPage.vala
@@ -1728,26 +1728,6 @@ public abstract class EditingHostPage : SinglePhotoPage {
     }
     
     #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) {
-            EditingTools.EditingToolWindow tool_window = current_tool.get_tool_window();
-            if (tool_window != null && !tool_window.has_user_moved())
-                place_tool_window();
-        }
-    }
-    
-    protected override void on_move(Gdk.Rectangle rect) {
-        track_tool_window();
-        
-        base.on_move(rect);
-    }
-
-    protected override void on_move_finished(Gdk.Rectangle rect) {
-        last_locations.clear();
-
-        base.on_move_finished(rect);
-    }
 
     private bool on_keyboard_pan_event(Gdk.EventKey event) {
         ZoomState current_zoom_state = get_zoom_state();
diff --git a/src/SinglePhotoPage.vala b/src/SinglePhotoPage.vala
index 05872f23..dc40fe5b 100644
--- a/src/SinglePhotoPage.vala
+++ b/src/SinglePhotoPage.vala
@@ -55,6 +55,7 @@ public abstract class SinglePhotoPage : Page {
         viewport.notify["default-height"].connect(on_viewport_resize);
         viewport.notify["maximized"].connect(on_viewport_resize);
 
+        canvas.resize.connect(on_viewport_resize);
         canvas.set_draw_func(on_canvas_exposed);
         set_event_source(canvas);
         Config.Facade.get_instance().colors_changed.connect(on_colors_changed);
@@ -287,6 +288,7 @@ public abstract class SinglePhotoPage : Page {
     }
 
     private void on_viewport_resize() {
+        print("Viewport resized!");
         // do fast repaints while resizing
         internal_repaint(true, null);
     }
diff --git a/src/meson.build b/src/meson.build
index bac1d984..6f50a098 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -165,6 +165,8 @@ executable(
         'import-roll/ImportRollEntry.vala',        
         'main.vala',
         'AppWindow.vala',
+        'FullscreenWindow.vala',
+        'PageWindow.vala',
         'CollectionPage.vala',
         'NaturalCollate.vala',
         'Thumbnail.vala',


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