diff options
Diffstat (limited to 'wx+')
-rw-r--r-- | wx+/dc.h | 2 | ||||
-rw-r--r-- | wx+/grid.cpp | 93 | ||||
-rw-r--r-- | wx+/no_flicker.h | 5 | ||||
-rw-r--r-- | wx+/popup_dlg.cpp | 10 | ||||
-rw-r--r-- | wx+/tooltip.cpp | 17 | ||||
-rw-r--r-- | wx+/window_layout.h (renamed from wx+/font_size.h) | 41 | ||||
-rw-r--r-- | wx+/window_tools.h | 216 |
7 files changed, 246 insertions, 138 deletions
@@ -13,7 +13,7 @@ #include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER #include <wx/dcscreen.h> #include <wx/bmpbndl.h> - #include <gtk/gtk.h> +// #include <gtk/gtk.h> namespace zen diff --git a/wx+/grid.cpp b/wx+/grid.cpp index f997d72c..306d4847 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -152,7 +152,7 @@ void GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& te - wxDC::DrawText also calls wxDC::GetTextExtent()!! => wxDC::DrawLabel() boils down to 3(!) calls to wxDC::GetTextExtent()!!! - wxDC::DrawLabel results in GetTextExtent() call even for empty strings!!! - => skip the wxDC::DrawLabel() cruft and directly call wxDC::DrawText()! */ + => NEVER EVER call wxDC::DrawLabel() cruft and directly call wxDC::DrawText()! */ assert(!contains(text, L'\n')); if (rect.width <= 0 || rect.height <= 0 || text.empty()) return; @@ -284,7 +284,11 @@ public: Bind(wxEVT_MOUSEWHEEL, [this](wxMouseEvent& event) { onMouseWheel (event); }); Bind(wxEVT_MOUSE_CAPTURE_LOST, [this](wxMouseCaptureLostEvent& event) { onMouseCaptureLost(event); }); - Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent& event) { onKeyDown(event); }); + Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent& event) + { + if (!sendEventToParent(event)) //let parent collect all key events + event.Skip(); + }); //Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) { onKeyUp (event); }); -> superfluous? assert(GetClientAreaOrigin() == wxPoint()); //generally assumed when dealing with coordinates below @@ -332,12 +336,6 @@ private: virtual void onLeaveWindow (wxMouseEvent& event) { event.Skip(); } virtual void onMouseCaptureLost(wxMouseCaptureLostEvent& event) { event.Skip(); } - void onKeyDown(wxKeyEvent& event) - { - if (!sendEventToParent(event)) //let parent collect all key events - event.Skip(); - } - void onMouseWheel(wxMouseEvent& event) { /* MSDN, WM_MOUSEWHEEL: "Sent to the focus window when the mouse wheel is rotated. @@ -816,13 +814,6 @@ private: void onMouseRightDown(wxMouseEvent& event) override { evalMouseMovement(event.GetPosition()); //update highlight in obscure cases (e.g. right-click while other context menu is open) - freezeMouseHighlight_ = true; //e.g. while showing context menu - - ZEN_ON_SCOPE_EXIT( - freezeMouseHighlight_ = false; - //update mouse highlight (e.g. mouse position changed after showing context menu) - evalMouseMovement(ScreenToClient(wxGetMousePosition())); - ); const wxPoint mousePos = GetPosition() + event.GetPosition(); @@ -837,6 +828,9 @@ private: if (fillGapAfterColumns) sendEventToParent(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, ColumnType::none, mousePos)); + //update mouse highlight (e.g. mouse position changed after showing context menu) => needed on Linux/macOS + evalMouseMovement(ScreenToClient(wxGetMousePosition())); + event.Skip(); } @@ -883,7 +877,7 @@ private: if (action->wantResize) SetCursor(wxCURSOR_SIZEWE); //window-local only! :) else - SetCursor(*wxSTANDARD_CURSOR); + SetCursor(*wxSTANDARD_CURSOR); //NOOP when setting same cursor } else { @@ -926,7 +920,7 @@ private: void setMouseHighlight(const std::optional<size_t>& hl) { - if (highlightCol_ != hl && !freezeMouseHighlight_) + if (highlightCol_ != hl) { highlightCol_ = hl; Refresh(); @@ -935,8 +929,7 @@ private: std::unique_ptr<ColumnResizing> activeResizing_; std::unique_ptr<ColumnMove> activeClickOrMove_; - std::optional<size_t> highlightCol_; //column during mouse-over - bool freezeMouseHighlight_ = false; + std::optional<size_t> highlightCol_; int colLabelHeight_ = 0; const wxFont labelFont_; @@ -966,9 +959,19 @@ public: { wxMouseCaptureLostEvent evt; GetEventHandler()->ProcessEvent(evt); //better integrate into event handling rather than calling onMouseCaptureLost() directly!? + return; } - else - event.Skip(); + + /* using keyboard: => clear distracting mouse highlights + + wxEVT_KEY_DOWN evaluation order: + 1. this callback + 2. Grid::SubWindow ... sendEventToParent() + 3. clients binding to Grid wxEVT_KEY_DOWN + 4. Grid::onKeyDown() */ + setMouseHighlight(std::nullopt); + + event.Skip(); }); } @@ -1050,8 +1053,11 @@ private: if (activeSelection_->getFirstClick().row_ == row) return activeSelection_->getFirstClick().hoverArea_; } - else if (highlight_.row == row) - return highlight_.rowHover; + else if (highlight_) + { + if (makeSigned(highlight_->row) == row) + return highlight_->rowHover; + } return HoverArea::none; } @@ -1110,13 +1116,6 @@ private: if (auto prov = refParent().getDataProvider()) { evalMouseMovement(event.GetPosition()); //update highlight in obscure cases (e.g. right-click while other context menu is open) - freezeMouseHighlight_ = true; //e.g. while showing context menu - - ZEN_ON_SCOPE_EXIT( - freezeMouseHighlight_ = false; - //update mouse highlight (e.g. mouse position changed after showing context menu) - evalMouseMovement(ScreenToClient(wxGetMousePosition())); - ); const wxPoint mousePos = GetPosition() + event.GetPosition(); const ptrdiff_t rowCount = refParent().getRowCount(); @@ -1166,6 +1165,9 @@ private: } } } + + //update mouse highlight (e.g. mouse position changed after showing context menu) => needed on Linux/macOS + evalMouseMovement(ScreenToClient(wxGetMousePosition())); } event.Skip(); //allow changing focus } @@ -1272,7 +1274,7 @@ private: if (activeSelection_) activeSelection_->evalMousePos(); //call on both mouse movement + timer event! else - setMouseHighlight({row, rowHover}); + setMouseHighlight(rowHover != HoverArea::none ? std::make_optional<MouseHighlight>({static_cast<size_t>(row), rowHover}) : std::nullopt); } } @@ -1286,14 +1288,14 @@ private: activeSelection_.reset(); Refresh(); } - setMouseHighlight({-1, HoverArea::none}); + setMouseHighlight(std::nullopt); //event.Skip(); -> we DID handle it! } void onLeaveWindow(wxMouseEvent& event) override { if (!activeSelection_) //wxEVT_LEAVE_WINDOW does not respect mouse capture! - setMouseHighlight({-1, HoverArea::none}); + setMouseHighlight(std::nullopt); //CAVEAT: we can get wxEVT_MOTION *after* wxEVT_LEAVE_WINDOW: see RowLabelWin::redirectMouseEvent() // => therefore we also redirect wxEVT_LEAVE_WINDOW, but user will see a little flicker when moving between RowLabelWin and MainWin @@ -1433,33 +1435,33 @@ private: struct MouseHighlight { - ptrdiff_t row = -1; + size_t row = 0; HoverArea rowHover = HoverArea::none; bool operator==(const MouseHighlight&) const = default; }; - void setMouseHighlight(const MouseHighlight& hl) + void setMouseHighlight(const std::optional<MouseHighlight>& hl) { - if (highlight_ != hl && !freezeMouseHighlight_) + assert(!hl || hl->row < refParent().getRowCount() && hl->rowHover != HoverArea::none); + if (highlight_ != hl) { - const ptrdiff_t rowCount = refParent().getRowCount(); - if (0 <= highlight_.row && highlight_.row < rowCount && highlight_.rowHover != HoverArea::none) //no highlight_? => NOP! - refreshRow(highlight_.row); + if (highlight_) + refreshRow(highlight_->row); highlight_ = hl; - if (0 <= highlight_.row && highlight_.row < rowCount && highlight_.rowHover != HoverArea::none) //no highlight_? => NOP! - refreshRow(highlight_.row); + if (highlight_) + refreshRow(highlight_->row); } } + RowLabelWin& rowLabelWin_; ColLabelWin& colLabelWin_; std::unique_ptr<MouseSelection> activeSelection_; //bound while user is selecting with mouse - MouseHighlight highlight_; //current mouse highlight - bool freezeMouseHighlight_ = false; + std::optional<MouseHighlight> highlight_; size_t cursorRow_ = 0; size_t selectionAnchor_ = 0; @@ -1736,10 +1738,7 @@ void Grid::onKeyDown(wxKeyEvent& event) auto moveCursorTo = [&](ptrdiff_t row) { if (rowCount > 0) - { - row = std::clamp<ptrdiff_t>(row, 0, rowCount - 1); - setGridCursor(row, GridEventPolicy::allow); - } + setGridCursor(std::clamp<ptrdiff_t>(row, 0, rowCount - 1), GridEventPolicy::allow); }; auto selectWithCursorTo = [&](ptrdiff_t row) diff --git a/wx+/no_flicker.h b/wx+/no_flicker.h index 1c91bd48..9f91bbdb 100644 --- a/wx+/no_flicker.h +++ b/wx+/no_flicker.h @@ -55,9 +55,8 @@ void setTextWithUrls(wxRichTextCtrl& richCtrl, const wxString& newText) for (auto it = newText.begin();;) { - const wchar_t urlPrefix[] = L"https://"; - const auto itUrl = std::search(it, newText.end(), - urlPrefix, urlPrefix + strLength(urlPrefix)); + const std::wstring_view urlPrefix = L"https://"; + const auto itUrl = std::search(it, newText.end(), urlPrefix.begin(), urlPrefix.end()); if (it != itUrl) blocks.emplace_back(BlockType::text, wxString(it, itUrl)); diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp index c30426cb..b5a194ad 100644 --- a/wx+/popup_dlg.cpp +++ b/wx+/popup_dlg.cpp @@ -13,10 +13,9 @@ #include "app_main.h" #include "bitmap_button.h" #include "no_flicker.h" -#include "font_size.h" +#include "window_layout.h" #include "image_resources.h" #include "popup_dlg_generated.h" -#include "std_button_layout.h" #include "taskbar.h" #include "window_tools.h" @@ -273,12 +272,17 @@ public: //set std order after button visibility was set setStandardButtonLayout(*bSizerStdButtons, stdBtns); - updateGui(); + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() +#ifdef __WXGTK3__ + Show(); //GTK3 size calculation requires visible window: https://github.com/wxWidgets/wxWidgets/issues/16088 + Hide(); //avoid old position flash when Center() moves window (asynchronously?) +#endif Center(); //needs to be re-applied after a dialog size change! + Raise(); //[!] popup may be triggered by ffs_batch job running in the background! if (m_buttonAccept->IsEnabled()) diff --git a/wx+/tooltip.cpp b/wx+/tooltip.cpp index c56d80a1..5ad5da31 100644 --- a/wx+/tooltip.cpp +++ b/wx+/tooltip.cpp @@ -13,7 +13,6 @@ #include "image_tools.h" #include "bitmap_button.h" #include "dc.h" - #include <gtk/gtk.h> using namespace zen; @@ -77,11 +76,12 @@ void Tooltip::show(const wxString& text, wxPoint mousePos, const wxImage* img) } if (imgChanged || txtChanged) - { //tipWindow_->Layout(); -> apparently not needed!? tipWindow_->GetSizer()->SetSizeHints(tipWindow_); //~=Fit() + SetMinSize() - //Linux: Fit() seems to be broken => call EVERY time inside show, not only if text or bmp change -> still true?!? - } +#ifdef __WXGTK3__ + //GTK3 size calculation requires visible window: https://github.com/wxWidgets/wxWidgets/issues/16088 + //=> call wxWindow::Show() to "execute" +#endif const wxPoint newPos = wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft ? mousePos - wxPoint(fastFromDIP(TIP_WINDOW_OFFSET_DIP) + tipWindow_->GetSize().GetWidth(), 0) : @@ -101,16 +101,13 @@ void Tooltip::hide() { if (tipWindow_) { -#if GTK_MAJOR_VERSION == 2 //the tooltip sometimes turns blank or is not shown again after it was hidden: e.g. drag-selection on middle grid +#ifdef __WXGTK2__ //the tooltip sometimes turns blank or is not shown again after it was hidden: e.g. drag-selection on middle grid + //=> no such issues on GTK3! tipWindow_->Destroy(); //apply brute force: tipWindow_ = nullptr; // lastUsedImg_ = wxNullImage; - -#elif GTK_MAJOR_VERSION == 3 - tipWindow_->Hide(); #else -#error unknown GTK version! + tipWindow_->Hide(); #endif - } } diff --git a/wx+/font_size.h b/wx+/window_layout.h index da74eada..8a86ec86 100644 --- a/wx+/font_size.h +++ b/wx+/window_layout.h @@ -4,12 +4,14 @@ // * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * // ***************************************************************************** -#ifndef FONT_SIZE_H_23849632846734343234532 -#define FONT_SIZE_H_23849632846734343234532 +#ifndef WINDOW_LAYOUT_H_23849632846734343234532 +#define WINDOW_LAYOUT_H_23849632846734343234532 #include <zen/basic_math.h> #include <wx/window.h> +#include <wx/spinctrl.h> #include <zen/scope_guard.h> + #include <gtk/gtk.h> #include "dc.h" @@ -19,7 +21,7 @@ namespace zen void setRelativeFontSize(wxWindow& control, double factor); void setMainInstructionFont(wxWindow& control); //following Windows/Gnome/OS X guidelines - +void setDefaultWidth(wxSpinCtrl& m_spinCtrl); @@ -47,6 +49,37 @@ void setMainInstructionFont(wxWindow& control) control.SetFont(font); } + + +inline +void setDefaultWidth(wxSpinCtrl& m_spinCtrl) +{ +#ifdef __WXGTK3__ + //there's no way to set width using GTK's CSS! => + m_spinCtrl.InvalidateBestSize(); + ::gtk_entry_set_width_chars(GTK_ENTRY(m_spinCtrl.m_widget), 3); + +#if 0 //apparently not needed!? + if (::gtk_check_version(3, 12, 0) == NULL) + ::gtk_entry_set_max_width_chars(GTK_ENTRY(m_spinCtrl.m_widget), 3); +#endif + + //get rid of excessive default width on old GTK3 3.14 (Debian); + //gtk_entry_set_width_chars() not working => mitigate + m_spinCtrl.SetMinSize({fastFromDIP(100), -1}); //must be wider than gtk_entry_set_width_chars(), or it breaks newer GTK e.g. 3.22! + +#if 0 //generic property syntax: + GValue bval = G_VALUE_INIT; + ::g_value_init(&bval, G_TYPE_BOOLEAN); + ::g_value_set_boolean(&bval, false); + ZEN_ON_SCOPE_EXIT(::g_value_unset(&bval)); + ::g_object_set_property(G_OBJECT(m_spinCtrl.m_widget), "visibility", &bval); +#endif +#else + m_spinCtrl.SetMinSize({fastFromDIP(70), -1}); +#endif + +} } -#endif //FONT_SIZE_H_23849632846734343234532 +#endif //WINDOW_LAYOUT_H_23849632846734343234532 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<wxPoint> pos, bool isMaximized, wxSize defaultSize) +class WindowLayout { - wxSize newSize = defaultSize; - std::optional<wxPoint> 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<wxSize> size; + std::optional<wxPoint> pos; + bool isMaximized = false; + }; + static void setInitial(wxTopLevelWindow& topWin, const Layout& layout, wxSize defaultSize) + { + initialLayouts_[&topWin] = layout; + + wxSize newSize = defaultSize; + std::optional<wxPoint> 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<wxString>(beforeFirst(phrase, '=', IfNotFoundReturn::none))] = + /**/ stringTo<int>(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<wxString, int> 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<wxSize> size; - std::optional<wxPoint> 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<wxSize> size = topWin.GetSize(); + std::optional<wxPoint> 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<std::string>(name) + '=' + numberTo<std::string>(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<const wxTopLevelWindow* /*don't access! use as key only!*/, Layout> initialLayouts_; +}; } } |