diff --git a/rtgui/editwindow.cc b/rtgui/editwindow.cc index 3ae3e47fa..d0e53d730 100644 --- a/rtgui/editwindow.cc +++ b/rtgui/editwindow.cc @@ -31,11 +31,17 @@ extern Glib::ustring argv0; // Check if the system has more than one display and option is set bool EditWindow::isMultiDisplayEnabled() { - return options.multiDisplayMode > 0 && Gdk::Screen::get_default()->get_n_monitors() > 1; + const auto screen = Gdk::Screen::get_default(); + + if (screen) { + return options.multiDisplayMode > 0 && screen->get_display()->get_n_monitors() > 1; + } else { + return false; // There is no default screen + } } -// Should only be created once, auto-creates window on correct display -EditWindow* EditWindow::getInstance(RTWindow* p, bool restore) +// Should only be created once +EditWindow* EditWindow::getInstance(RTWindow* p) { struct EditWindowInstance { @@ -47,13 +53,15 @@ EditWindow* EditWindow::getInstance(RTWindow* p, bool restore) }; static EditWindowInstance instance_(p); - if(restore) { - instance_.editWnd.restoreWindow(); - } return &instance_.editWnd; } -EditWindow::EditWindow (RTWindow* p) : resolution(RTScalable::baseDPI), parent(p) , isFullscreen(false), isClosed(true) +EditWindow::EditWindow (RTWindow* p) + : resolution(RTScalable::baseDPI) + , parent(p) + , isFullscreen(false) + , isClosed(true) + , isMinimized(false) { updateResolution(); @@ -70,6 +78,8 @@ EditWindow::EditWindow (RTWindow* p) : resolution(RTScalable::baseDPI), parent(p mainNB->signal_switch_page().connect_notify(sigc::mem_fun(*this, &EditWindow::on_mainNB_switch_page)); signal_key_press_event().connect(sigc::mem_fun(*this, &EditWindow::keyPressed)); + signal_window_state_event().connect(sigc::mem_fun(*this, &EditWindow::on_window_state_event)); + onConfEventConn = signal_configure_event().connect(sigc::mem_fun(*this, &EditWindow::on_configure_event)); Gtk::Box* mainBox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); mainBox->pack_start(*mainNB); @@ -78,43 +88,74 @@ EditWindow::EditWindow (RTWindow* p) : resolution(RTScalable::baseDPI), parent(p } -void EditWindow::restoreWindow() { +void EditWindow::restoreWindow() +{ + if (isClosed) { + onConfEventConn.block(true); // Avoid getting size and position while window is being moved, maximized, ... - if(isClosed) { - int meowMonitor = 0; - if(isMultiDisplayEnabled()) { - if(options.meowMonitor >= 0) { // use display from last session if available - meowMonitor = std::min(options.meowMonitor, Gdk::Screen::get_default()->get_n_monitors() - 1); - } else { // Determine the other display - const Glib::RefPtr< Gdk::Window >& wnd = parent->get_window(); - meowMonitor = parent->get_screen()->get_monitor_at_window(wnd) == 0 ? 1 : 0; + int meowMonitor = 0; // By default, set to main monitor + const auto display = get_screen()->get_display(); + + if (isMultiDisplayEnabled()) { + if (options.meowMonitor >= 0) { // Use display from last session if available + meowMonitor = std::max(0, std::min(options.meowMonitor, display->get_n_monitors() - 1)); + } else { // Determine the main RT window display + const Glib::RefPtr &wnd = parent->get_window(); + + // Retrieve window monitor ID + const int monitor_nb = display->get_n_monitors(); + + for (int id = 0; id < monitor_nb; id++) { + if (display->get_monitor_at_window(wnd) == display->get_monitor(id)) { + meowMonitor = id; + break; + } + } } } Gdk::Rectangle lMonitorRect; - get_screen()->get_monitor_geometry(meowMonitor, lMonitorRect); - if(options.meowMaximized) { + display->get_monitor(meowMonitor)->get_geometry(lMonitorRect); + +#ifdef __APPLE__ + // Get macOS menu bar height + Gdk::Rectangle lWorkAreaRect; + display->get_monitor(std::min(meowMonitor, display->get_n_monitors() - 1))->get_workarea(lWorkAreaRect); + const int macMenuBarHeight = lWorkAreaRect.get_y(); + + // Place RT window to saved one in options file + if (options.meowX <= lMonitorRect.get_x() + lMonitorRect.get_width() + && options.meowX >= 0 + && options.meowY <= lMonitorRect.get_y() + lMonitorRect.get_height() - macMenuBarHeight + && options.meowY >= 0) { + move(options.meowX, options.meowY + macMenuBarHeight); + } else { + move(lMonitorRect.get_x(), lMonitorRect.get_y() + macMenuBarHeight); + } +#else + // Place RT window to saved one in options file + if (options.meowX <= lMonitorRect.get_x() + lMonitorRect.get_width() + && options.meowX >= 0 + && options.meowY <= lMonitorRect.get_y() + lMonitorRect.get_height() + && options.meowY >= 0) { + move(options.meowX, options.meowY); + } else { move(lMonitorRect.get_x(), lMonitorRect.get_y()); + } +#endif + + // Maximize RT window according to options file + if (options.meowMaximized) { maximize(); } else { + unmaximize(); resize(options.meowWidth, options.meowHeight); - if(options.meowX <= lMonitorRect.get_x() + lMonitorRect.get_width() && options.meowY <= lMonitorRect.get_y() + lMonitorRect.get_height()) { - move(options.meowX, options.meowY); - } else { - move(lMonitorRect.get_x(), lMonitorRect.get_y()); - } - } - show_all(); - - isFullscreen = options.meowFullScreen; - - if(isFullscreen) { - fullscreen(); } isClosed = false; - } + onConfEventConn.block(false); + } } void EditWindow::on_realize () @@ -174,27 +215,23 @@ bool EditWindow::on_configure_event(GdkEventConfigure* event) setAppIcon(); } - if (get_realized() && is_visible()) { - if(!is_maximized()) { - get_position(options.meowX, options.meowY); - get_size(options.meowWidth, options.meowHeight); - } - options.meowMaximized = is_maximized(); + if (!options.meowMaximized && !isFullscreen && !isMinimized) { + get_position(options.meowX, options.meowY); + get_size(options.meowWidth, options.meowHeight); } return Gtk::Widget::on_configure_event(event); } -/* HOMBRE: Disabling this since it's maximized when opened anyway. - * Someday, the EditorWindow might save its own position and state, so it'll have to be uncommented bool EditWindow::on_window_state_event(GdkEventWindowState* event) { - if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) { - options.windowMaximized = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED; - } + // Retrieve RT window states + options.meowMaximized = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED; + isMinimized = event->new_window_state & GDK_WINDOW_STATE_ICONIFIED; + isFullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN; return Gtk::Widget::on_window_state_event(event); -}*/ +} void EditWindow::on_mainNB_switch_page(Gtk::Widget* widget, guint page_num) { @@ -241,7 +278,8 @@ void EditWindow::addEditorPanel (EditorPanel* ep, const std::string &name) epanels[ name ] = ep; filesEdited.insert ( name ); parent->fpanel->refreshEditedState (filesEdited); - ep->setAspect(); + + show_all(); } void EditWindow::remEditorPanel (EditorPanel* ep) @@ -281,10 +319,20 @@ bool EditWindow::selectEditorPanel(const std::string &name) void EditWindow::toFront () { - // when using the secondary window on the same monitor as the primary window we need to present the secondary window. + // When using the secondary window on the same monitor as the primary window we need to present the secondary window. // If we don't, it will stay in background when opening 2nd, 3rd... editor, which is annoying // It will also deiconify the window - present(); + // To avoid unexpected behavior while window is being updated, present() function is called after at idle + idle_register.add( + [this]()-> bool + { + onConfEventConn.block(true); // Avoid getting size and position while window is being moved, maximized, ... + present(); + onConfEventConn.block(false); + + return false; + } + ); } bool EditWindow::keyPressed (GdkEventKey* event) @@ -308,27 +356,63 @@ bool EditWindow::keyPressed (GdkEventKey* event) return false; } - } -void EditWindow::toggleFullscreen () +void EditWindow::toggleFullscreen() { + onConfEventConn.block(true); // Avoid getting size and position while window is getting fullscreen + isFullscreen ? unfullscreen() : fullscreen(); - options.meowFullScreen = isFullscreen = !isFullscreen; + + onConfEventConn.block(false); } -void EditWindow::writeOptions() { +void EditWindow::get_position(int& x, int& y) const +{ + // Call native function + Gtk::Window::get_position(x, y); - if(is_visible()) { - if(isMultiDisplayEnabled()) { - options.meowMonitor = get_screen()->get_monitor_at_window(get_window()); + // Retrieve display (concatenation of all monitors) size + int width = 0, height = 0; + const auto display = get_screen()->get_display(); + const int nbMonitors = display->get_n_monitors(); + + for (int i = 0; i < nbMonitors; i++) { + Gdk::Rectangle lMonitorRect; + display->get_monitor(i)->get_geometry(lMonitorRect); + width = std::max(width, lMonitorRect.get_x() + lMonitorRect.get_width()); + height = std::max(height, lMonitorRect.get_y() + lMonitorRect.get_height()); + } + + // Saturate position at monitor limits to avoid unexpected behavior (fixes #6233) + x = std::min(width, std::max(0, x)); + y = std::min(height, std::max(0, y)); +} + +void EditWindow::writeOptions() +{ + if (is_visible()) { + if (isMultiDisplayEnabled()) { + // Retrieve window monitor ID + options.meowMonitor = 0; + const auto display = get_screen()->get_display(); + const int monitor_nb = display->get_n_monitors(); + + for (int id = 0; id < monitor_nb; id++) { + if (display->get_monitor_at_window(get_window()) == display->get_monitor(id)) { + options.windowMonitor = id; + break; + } + } } - options.meowMaximized = is_maximized(); - get_position(options.meowX, options.meowY); - get_size(options.meowWidth,options.meowHeight); + if (!options.meowMaximized && !isFullscreen && !isMinimized) { + get_position(options.meowX, options.meowY); + get_size(options.meowWidth, options.meowHeight); + } } } + bool EditWindow::on_delete_event(GdkEventAny* event) { diff --git a/rtgui/editwindow.h b/rtgui/editwindow.h index f4ada571d..b8eeaee82 100644 --- a/rtgui/editwindow.h +++ b/rtgui/editwindow.h @@ -21,6 +21,7 @@ #include #include "rtimage.h" +#include "guiutils.h" class EditorPanel; class RTWindow; @@ -40,17 +41,20 @@ private: bool isFullscreen; bool isClosed; + bool isMinimized; + sigc::connection onConfEventConn; void toggleFullscreen (); - void restoreWindow(); bool updateResolution(); void setAppIcon(); + IdleRegister idle_register; + public: // Check if the system has more than one display and option is set static bool isMultiDisplayEnabled(); - // Should only be created once, auto-creates window on correct display - static EditWindow* getInstance(RTWindow* p, bool restore = true); + // Should only be created once + static EditWindow* getInstance(RTWindow* p); explicit EditWindow (RTWindow* p); @@ -65,8 +69,10 @@ public: bool keyPressed (GdkEventKey* event); bool on_configure_event(GdkEventConfigure* event) override; bool on_delete_event(GdkEventAny* event) override; - //bool on_window_state_event(GdkEventWindowState* event); + bool on_window_state_event(GdkEventWindowState* event) override; void on_mainNB_switch_page(Gtk::Widget* page, guint page_num); void set_title_decorated(Glib::ustring fname); void on_realize () override; + void get_position(int& x, int& y) const; + void restoreWindow(); }; diff --git a/rtgui/options.cc b/rtgui/options.cc index ec36eb166..4e6beb7f4 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -298,7 +298,6 @@ void Options::setDefaults() windowMaximized = true; windowMonitor = 0; meowMonitor = -1; - meowFullScreen = false; meowMaximized = true; meowWidth = 1200; meowHeight = 680; @@ -1244,10 +1243,6 @@ void Options::readFromFile(Glib::ustring fname) meowMonitor = keyFile.get_integer("GUI", "MeowMonitor"); } - if (keyFile.has_key("GUI", "MeowFullScreen")) { - meowFullScreen = keyFile.get_boolean("GUI", "MeowFullScreen"); - } - if (keyFile.has_key("GUI", "MeowMaximized")) { meowMaximized = keyFile.get_boolean("GUI", "MeowMaximized"); } @@ -2270,7 +2265,6 @@ void Options::saveToFile(Glib::ustring fname) keyFile.set_integer("GUI", "WindowY", windowY); keyFile.set_integer("GUI", "WindowMonitor", windowMonitor); keyFile.set_integer("GUI", "MeowMonitor", meowMonitor); - keyFile.set_boolean("GUI", "MeowFullScreen", meowFullScreen); keyFile.set_boolean("GUI", "MeowMaximized", meowMaximized); keyFile.set_integer("GUI", "MeowWidth", meowWidth); keyFile.set_integer("GUI", "MeowHeight", meowHeight); diff --git a/rtgui/options.h b/rtgui/options.h index 19963ba39..bc5e41c91 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -219,7 +219,6 @@ public: bool windowMaximized; int windowMonitor; int meowMonitor; - bool meowFullScreen; bool meowMaximized; int meowWidth; int meowHeight; diff --git a/rtgui/rtwindow.cc b/rtgui/rtwindow.cc old mode 100644 new mode 100755 index cf77374ce..7f87d4ec4 --- a/rtgui/rtwindow.cc +++ b/rtgui/rtwindow.cc @@ -282,8 +282,10 @@ RTWindow::RTWindow () on_delete_has_run = false; is_fullscreen = false; + is_minimized = false; property_destroy_with_parent().set_value (false); signal_window_state_event().connect ( sigc::mem_fun (*this, &RTWindow::on_window_state_event) ); + onConfEventConn = signal_configure_event().connect ( sigc::mem_fun (*this, &RTWindow::on_configure_event) ); signal_key_press_event().connect ( sigc::mem_fun (*this, &RTWindow::keyPressed) ); signal_key_release_event().connect(sigc::mem_fun(*this, &RTWindow::keyReleased)); @@ -522,7 +524,7 @@ void RTWindow::showErrors() bool RTWindow::on_configure_event (GdkEventConfigure* event) { - if (!is_maximized() && is_visible()) { + if (!options.windowMaximized && !is_fullscreen && !is_minimized) { get_size (options.windowWidth, options.windowHeight); get_position (options.windowX, options.windowY); } @@ -535,10 +537,11 @@ bool RTWindow::on_configure_event (GdkEventConfigure* event) bool RTWindow::on_window_state_event (GdkEventWindowState* event) { - if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) { - options.windowMaximized = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED; - } - + // Retrieve RT window states + options.windowMaximized = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED; + is_minimized = event->new_window_state & GDK_WINDOW_STATE_ICONIFIED; + is_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN; + return Gtk::Widget::on_window_state_event (event); } @@ -577,8 +580,10 @@ void RTWindow::addEditorPanel (EditorPanel* ep, const std::string &name) { if (options.multiDisplayMode > 0) { EditWindow * wndEdit = EditWindow::getInstance (this); - wndEdit->show(); wndEdit->addEditorPanel (ep, name); + wndEdit->show_all(); + wndEdit->restoreWindow(); // Need to be called after RTWindow creation to work with all OS Windows Manager + ep->setAspect(); wndEdit->toFront(); } else { ep->setParent (this); @@ -795,7 +800,7 @@ bool RTWindow::on_delete_event (GdkEventAny* event) if (isSingleTabMode() || simpleEditor) { isProcessing = epanel->getIsProcessing(); } else if (options.multiDisplayMode > 0) { - editWindow = EditWindow::getInstance (this, false); + editWindow = EditWindow::getInstance (this); isProcessing = editWindow->isProcessing(); } else { int pageCount = mainNB->get_n_pages(); @@ -863,12 +868,22 @@ bool RTWindow::on_delete_event (GdkEventAny* event) FileBrowserEntry::hdr.reset(); FileBrowserEntry::ps.reset(); - if (!options.windowMaximized) { + if (!options.windowMaximized && !is_fullscreen && !is_minimized) { get_size (options.windowWidth, options.windowHeight); get_position (options.windowX, options.windowY); } - options.windowMonitor = get_screen()->get_monitor_at_window (get_window()); + // Retrieve window monitor ID + options.windowMonitor = 0; + const auto display = get_screen()->get_display(); + const int monitor_nb = display->get_n_monitors(); + + for (int id = 0; id < monitor_nb; id++) { + if (display->get_monitor_at_window(get_window()) == display->get_monitor(id)) { + options.windowMonitor = id; + break; + } + } try { Options::save (); @@ -981,25 +996,25 @@ void RTWindow::error(const Glib::ustring& descr) void RTWindow::toggle_fullscreen () { + onConfEventConn.block(true); // Avoid getting size and position while window is getting fullscreen + if (is_fullscreen) { unfullscreen(); - is_fullscreen = false; if (btn_fullscreen) { - //btn_fullscreen->set_label(M("MAIN_BUTTON_FULLSCREEN")); btn_fullscreen->set_tooltip_markup (M ("MAIN_BUTTON_FULLSCREEN")); btn_fullscreen->set_image (*iFullscreen); } } else { fullscreen(); - is_fullscreen = true; if (btn_fullscreen) { - //btn_fullscreen->set_label(M("MAIN_BUTTON_UNFULLSCREEN")); btn_fullscreen->set_tooltip_markup (M ("MAIN_BUTTON_UNFULLSCREEN")); btn_fullscreen->set_image (*iFullscreen_exit); } } + + onConfEventConn.block(false); } void RTWindow::SetEditorCurrent() @@ -1105,40 +1120,70 @@ bool RTWindow::splashClosed (GdkEventAny* event) void RTWindow::setWindowSize () { + onConfEventConn.block(true); // Avoid getting size and position while window is being moved, maximized, ... + Gdk::Rectangle lMonitorRect; - get_screen()->get_monitor_geometry (std::min (options.windowMonitor, Gdk::Screen::get_default()->get_n_monitors() - 1), lMonitorRect); + const auto display = get_screen()->get_display(); + display->get_monitor (std::min (options.windowMonitor, display->get_n_monitors() - 1))->get_geometry(lMonitorRect); #ifdef __APPLE__ // Get macOS menu bar height - const Gdk::Rectangle lWorkAreaRect = get_screen()->get_monitor_workarea (std::min (options.windowMonitor, Gdk::Screen::get_default()->get_n_monitors() - 1)); + Gdk::Rectangle lWorkAreaRect; + display->get_monitor (std::min (options.windowMonitor, display->get_n_monitors() - 1))->get_workarea(lWorkAreaRect); const int macMenuBarHeight = lWorkAreaRect.get_y(); + + // Place RT window to saved one in options file + if (options.windowX <= lMonitorRect.get_x() + lMonitorRect.get_width() + && options.windowX >= 0 + && options.windowY <= lMonitorRect.get_y() + lMonitorRect.get_height() - macMenuBarHeight + && options.windowY >= 0) { + move (options.windowX, options.windowY + macMenuBarHeight); + } else { + move (lMonitorRect.get_x(), lMonitorRect.get_y() + macMenuBarHeight); + } +#else + // Place RT window to saved one in options file + if (options.windowX <= lMonitorRect.get_x() + lMonitorRect.get_width() + && options.windowX >= 0 + && options.windowY <= lMonitorRect.get_y() + lMonitorRect.get_height() + && options.windowY >= 0) { + move (options.windowX, options.windowY); + } else { + move (lMonitorRect.get_x(), lMonitorRect.get_y()); + } #endif + // Maximize RT window according to options file if (options.windowMaximized) { -#ifdef __APPLE__ - move (lMonitorRect.get_x(), lMonitorRect.get_y() + macMenuBarHeight); -#else - move (lMonitorRect.get_x(), lMonitorRect.get_y()); -#endif maximize(); } else { unmaximize(); resize (options.windowWidth, options.windowHeight); - -#ifdef __APPLE__ - if (options.windowX <= lMonitorRect.get_x() + lMonitorRect.get_width() && options.windowY <= lMonitorRect.get_y() + lMonitorRect.get_height() - macMenuBarHeight) { - move (options.windowX, options.windowY + macMenuBarHeight); - } else { - move (lMonitorRect.get_x(), lMonitorRect.get_y() + macMenuBarHeight); - } -#else - if (options.windowX <= lMonitorRect.get_x() + lMonitorRect.get_width() && options.windowY <= lMonitorRect.get_y() + lMonitorRect.get_height()) { - move (options.windowX, options.windowY); - } else { - move (lMonitorRect.get_x(), lMonitorRect.get_y()); - } -#endif } + + onConfEventConn.block(false); +} + +void RTWindow::get_position(int& x, int& y) const +{ + // Call native function + Gtk::Window::get_position (x, y); + + // Retrieve display (concatenation of all monitors) size + int width = 0, height = 0; + const auto display = get_screen()->get_display(); + const int nbMonitors = display->get_n_monitors(); + + for (int i = 0; i < nbMonitors; i++) { + Gdk::Rectangle lMonitorRect; + display->get_monitor(i)->get_geometry(lMonitorRect); + width = std::max(width, lMonitorRect.get_x() + lMonitorRect.get_width()); + height = std::max(height, lMonitorRect.get_y() + lMonitorRect.get_height()); + } + + // Saturate position at monitor limits to avoid unexpected behavior (fixes #6233) + x = std::min(width, std::max(0, x)); + y = std::min(height, std::max(0, y)); } void RTWindow::set_title_decorated (Glib::ustring fname) diff --git a/rtgui/rtwindow.h b/rtgui/rtwindow.h old mode 100644 new mode 100755 index aa1830d89..0c1cd2572 --- a/rtgui/rtwindow.h +++ b/rtgui/rtwindow.h @@ -51,6 +51,8 @@ private: Gtk::ProgressBar prProgBar; PLDBridge* pldBridge; bool is_fullscreen; + bool is_minimized; + sigc::connection onConfEventConn; bool on_delete_has_run; Gtk::Button * btn_fullscreen; @@ -96,6 +98,7 @@ public: void showPreferences (); void on_realize () override; void toggle_fullscreen (); + void get_position(int& x, int& y) const; void setProgress(double p) override; void setProgressStr(const Glib::ustring& str) override;