Improves RT window position management, fixes #6233 (#6239)

* Improves RT window events detection and RT window position computation

* Fixes some limitations in specific cases for Windows and MacOS

* Fixes RT window not restoring saved position at startup

- When maximized at startup, RT window position wasn't restored from
options file. So when unmaximazing RT, position was set to up left
corner instead of restored one
- Other fix: Removed some uses of deprecated Gtk functions

* Improves RT window management for independant edit window mode

Other fixes:
- Monitor ID wasn't correctly computed
- Removes some other deprecated functions uses
- Removes debug printf

* Fixes editor window aspects not restored in MEOW mode on Linux

Other fixes:
- Removes some Gtk warnings when adding a new editor panel in MEOW mode
- Adds robustness to avoid RT windows outside screen at startup loading
corrupted options file with negative position values

* Fixes incorrect max position saturation in dual screen configurations
This commit is contained in:
Pandagrapher 2021-06-21 21:22:14 +02:00 committed by GitHub
parent 7e36240f3a
commit 491b57bee7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 232 additions and 101 deletions

View File

@ -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<Gdk::Window> &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)
{

View File

@ -21,6 +21,7 @@
#include <gtkmm.h>
#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();
};

View File

@ -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);

View File

@ -219,7 +219,6 @@ public:
bool windowMaximized;
int windowMonitor;
int meowMonitor;
bool meowFullScreen;
bool meowMaximized;
int meowWidth;
int meowHeight;

113
rtgui/rtwindow.cc Normal file → Executable file
View File

@ -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)

3
rtgui/rtwindow.h Normal file → Executable file
View File

@ -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;