From 5a6ed4e8eadf3af3d141b246f124d67b62a1357c Mon Sep 17 00:00:00 2001 From: "B. Stack" Date: Tue, 21 Feb 2023 12:13:36 -0500 Subject: add upstream 12.1 --- wx+/window_tools.h | 216 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 146 insertions(+), 70 deletions(-) (limited to 'wx+/window_tools.h') diff --git a/wx+/window_tools.h b/wx+/window_tools.h index 73faf272..179508f8 100644 --- a/wx+/window_tools.h +++ b/wx+/window_tools.h @@ -92,96 +92,172 @@ private: namespace { -void setInitialWindowSize(wxTopLevelWindow& topWin, wxSize size, std::optional pos, bool isMaximized, wxSize defaultSize) +class WindowLayout { - wxSize newSize = defaultSize; - std::optional newPos; - //set dialog size and position: - // - width/height are invalid if the window is minimized (eg x,y = -32000; width = 160, height = 28) - // - multi-monitor setup: dialog may be placed on second monitor which is currently turned off - if (size.GetWidth () > 0 && - size.GetHeight() > 0) +public: + struct Layout { - if (pos) + std::optional size; + std::optional pos; + bool isMaximized = false; + }; + static void setInitial(wxTopLevelWindow& topWin, const Layout& layout, wxSize defaultSize) + { + initialLayouts_[&topWin] = layout; + + wxSize newSize = defaultSize; + std::optional newPos; + //set dialog size and position: + // - width/height are invalid if the window is minimized (eg x,y = -32000; width = 160, height = 28) + // - multi-monitor setup: dialog may be placed on second monitor which is currently turned off + if (layout.size && + layout.size->GetWidth () > 0 && + layout.size->GetHeight() > 0) { - //calculate how much of the dialog will be visible on screen - const int dlgArea = size.GetWidth() * size.GetHeight(); - int dlgAreaMaxVisible = 0; + if (layout.pos) + { + //calculate how much of the dialog will be visible on screen + const int dlgArea = layout.size->GetWidth() * layout.size->GetHeight(); + int dlgAreaMaxVisible = 0; + + const int monitorCount = wxDisplay::GetCount(); + for (int i = 0; i < monitorCount; ++i) + { + wxRect overlap = wxDisplay(i).GetClientArea().Intersect(wxRect(*layout.pos, *layout.size)); + dlgAreaMaxVisible = std::max(dlgAreaMaxVisible, overlap.GetWidth() * overlap.GetHeight()); + } + + if (dlgAreaMaxVisible > 0.1 * dlgArea //at least 10% of the dialog should be visible! + ) + { + newSize = *layout.size; + newPos = layout.pos; + } + } + else + newSize = *layout.size; + } - const int monitorCount = wxDisplay::GetCount(); - for (int i = 0; i < monitorCount; ++i) + //old comment: "wxGTK's wxWindow::SetSize seems unreliable and behaves like a wxWindow::SetClientSize + // => use wxWindow::SetClientSize instead (for the record: no such issue on Windows/macOS) + //2018-10-15: Weird new problem on CentOS/Ubuntu: SetClientSize() + SetPosition() fail to set correct dialog *position*, but SetSize() + SetPosition() do! + // => old issues with SetSize() seem to be gone... => revert to SetSize() + if (newPos) + topWin.SetSize(wxRect(*newPos, newSize)); + else + { + topWin.SetSize(newSize); + topWin.Center(); + } + + if (layout.isMaximized) //no real need to support both maximize and full screen functions + { + topWin.Maximize(true); + } + + +#if 0 //wxWidgets alternative: apparently no benefits (not even on Wayland! but strange decisions: why restore the minimized state!???) + class GeoSerializer : public wxTopLevelWindow::GeometrySerializer + { + public: + GeoSerializer(const std::string& l) { - wxRect overlap = wxDisplay(i).GetClientArea().Intersect(wxRect(*pos, size)); - dlgAreaMaxVisible = std::max(dlgAreaMaxVisible, overlap.GetWidth() * overlap.GetHeight()); + split(l, ' ', [&](const std::string_view phrase) + { + assert(phrase.empty() || contains(phrase, '=')); + if (contains(phrase, '=')) + valuesByName_[utfTo(beforeFirst(phrase, '=', IfNotFoundReturn::none))] = + /**/ stringTo(afterFirst(phrase, '=', IfNotFoundReturn::none)); + }); } - if (dlgAreaMaxVisible > 0.1 * dlgArea //at least 10% of the dialog should be visible! - ) + bool SaveField(const wxString& name, int value) const /*NO, this must not be const!*/ override { return false; } + + bool RestoreField(const wxString& name, int* value) /*const: yes, this MAY(!) be const*/ override { - newSize = size; - newPos = pos; + auto it = valuesByName_.find(name); + if (it == valuesByName_.end()) + return false; + * value = it->second; + return true; } - } - else - newSize = size; - } + private: + std::unordered_map valuesByName_; + } serializer(layout); - //old comment: "wxGTK's wxWindow::SetSize seems unreliable and behaves like a wxWindow::SetClientSize - // => use wxWindow::SetClientSize instead (for the record: no such issue on Windows/macOS) - //2018-10-15: Weird new problem on CentOS/Ubuntu: SetClientSize() + SetPosition() fail to set correct dialog *position*, but SetSize() + SetPosition() do! - // => old issues with SetSize() seem to be gone... => revert to SetSize() - if (newPos) - topWin.SetSize(wxRect(*newPos, newSize)); - else - { - topWin.SetSize(newSize); - topWin.Center(); + if (!topWin.RestoreToGeometry(serializer)) //apparently no-fail as long as GeometrySerializer::RestoreField is! + assert(false); +#endif } - if (isMaximized) //no real need to support both maximize and full screen functions + //destructive! changes window size! + static Layout getBeforeClose(wxTopLevelWindow& topWin) { - topWin.Maximize(true); - } -} - + //we need to portably retrieve non-iconized, non-maximized size and position + // non-portable: Win32 GetWindowPlacement(); wxWidgets take: wxTopLevelWindow::SaveGeometry/RestoreToGeometry() + if (topWin.IsIconized()) + topWin.Iconize(false); -struct WindowLayoutWeak -{ - std::optional size; - std::optional pos; - bool isMaximized = false; -}; -//destructive! changes window size! -WindowLayoutWeak getWindowSizeBeforeClose(wxTopLevelWindow& topWin) -{ - //we need to portably retrieve non-iconized, non-maximized size and position - // non-portable: Win32 GetWindowPlacement(); wxWidgets take: wxTopLevelWindow::RestoreToGeometry() - if (topWin.IsIconized()) - topWin.Iconize(false); + bool isMaximized = false; + if (topWin.IsMaximized()) //evaluate AFTER uniconizing! + { + topWin.Maximize(false); + isMaximized = true; + } - WindowLayoutWeak layout; - if (topWin.IsMaximized()) //evaluate AFTER uniconizing! - { - topWin.Maximize(false); - layout.isMaximized = true; - } + std::optional size = topWin.GetSize(); + std::optional pos = topWin.GetPosition(); - layout.size = topWin.GetSize(); - layout.pos = topWin.GetPosition(); + if (isMaximized) + if (!topWin.IsShown() //=> Win: can't trust size GetSize()/GetPosition(): still at full screen size! + //wxGTK: returns full screen size and strange position (65/-4) + //OS X 10.9 (but NO issue on 10.11!) returns full screen size and strange position (0/-22) + || pos->y < 0 + ) + { + size = std::nullopt; + pos = std::nullopt; + } - if (layout.isMaximized) - if (!topWin.IsShown() //=> Win: can't trust size GetSize()/GetPosition(): still at full screen size! - //wxGTK: returns full screen size and strange position (65/-4) - //OS X 10.9 (but NO issue on 10.11!) returns full screen size and strange position (0/-22) - || layout.pos->y < 0 - ) + //reuse previous values if current ones are not available: + if (const auto it = initialLayouts_.find(&topWin); + it != initialLayouts_.end()) { - layout.size = std::nullopt; - layout.pos = std::nullopt; + if (!size) + size = it->second.size; + + if (!pos) + pos = it->second.pos; } - return layout; -} + return {size, pos, isMaximized}; + +#if 0 //wxWidgets alternative: apparently no benefits (not even on Wayland! but strange decisions: why restore the minimized state!???) + struct : wxTopLevelWindow::GeometrySerializer + { + bool SaveField(const wxString& name, int value) const /*NO, this must not be const!*/ override + { + layout_ += utfTo(name) + '=' + numberTo(value) + ' '; + return true; + } + + bool RestoreField(const wxString& name, int* value) /*const: yes, this MAY(!) be const*/ override { return false; } + + mutable //wxWidgets people: 1. learn when and when not to use const for input/output functions! see SaveField/RestoreField() + // 2. learn flexible software design: why are input/output tied up in a single GeometrySerializer implementation? + std::string layout_; + } serializer; + + if (topWin.SaveGeometry(serializer)) //apparently no-fail as long as GeometrySerializer::SaveField is! + return serializer.layout_; + else + assert(false); +#endif + } + +private: + inline static std::unordered_map initialLayouts_; +}; } } -- cgit