diff --git a/include/wx/gtk/private/tlwgeom.h b/include/wx/gtk/private/tlwgeom.h new file mode 100644 index 0000000000..31cc355997 --- /dev/null +++ b/include/wx/gtk/private/tlwgeom.h @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/gtk/private/tlwgeom.h +// Purpose: wxGTK-specific wxTLWGeometry class. +// Author: Vadim Zeitlin +// Created: 2018-04-29 +// Copyright: (c) 2018 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_GTK_PRIVATE_TLWGEOM_H_ +#define _WX_GTK_PRIVATE_TLWGEOM_H_ + +class wxTLWGeometry : public wxTLWGeometryGeneric +{ +public: + virtual bool Save(const Serializer& ser) const wxOVERRIDE + { + if ( !wxTLWGeometryGeneric::Save(ser) ) + return false; + + // Don't save the decoration sizes if we don't really have any values + // for them. + if ( m_decorSize.left || m_decorSize.right || + m_decorSize.top || m_decorSize.bottom ) + { + ser.SaveField("decor_l", m_decorSize.left); + ser.SaveField("decor_r", m_decorSize.right); + ser.SaveField("decor_t", m_decorSize.top); + ser.SaveField("decor_b", m_decorSize.bottom); + } + + return true; + } + + virtual bool Restore(Serializer& ser) wxOVERRIDE + { + if ( !wxTLWGeometryGeneric::Restore(ser) ) + return false; + + ser.RestoreField("decor_l", &m_decorSize.left); + ser.RestoreField("decor_r", &m_decorSize.right); + ser.RestoreField("decor_t", &m_decorSize.top); + ser.RestoreField("decor_b", &m_decorSize.bottom); + + return true; + } + + virtual bool GetFrom(const wxTopLevelWindow* tlw) wxOVERRIDE + { + if ( !wxTLWGeometryGeneric::GetFrom(tlw) ) + return false; + + m_decorSize = tlw->m_decorSize; + + return true; + } + + virtual bool ApplyTo(wxTopLevelWindow* tlw) wxOVERRIDE + { + if ( !wxTLWGeometryGeneric::ApplyTo(tlw) ) + return false; + + // Don't overwrite the current decoration size if we already have it. + if ( !tlw->m_decorSize.left && !tlw->m_decorSize.right && + !tlw->m_decorSize.top && !tlw->m_decorSize.bottom ) + { + tlw->m_decorSize = m_decorSize; + } + + return true; + } + +private: + wxTopLevelWindow::DecorSize m_decorSize; +}; + +#endif // _WX_GTK_PRIVATE_TLWGEOM_H_ diff --git a/include/wx/persist/toplevel.h b/include/wx/persist/toplevel.h index 07c1d61ae2..c1bec8667a 100644 --- a/include/wx/persist/toplevel.h +++ b/include/wx/persist/toplevel.h @@ -13,7 +13,6 @@ #include "wx/persist/window.h" #include "wx/toplevel.h" -#include "wx/display.h" // ---------------------------------------------------------------------------- // string constants used by wxPersistentTLW @@ -24,21 +23,13 @@ // windows, just persistent controls which have their own specific kind strings #define wxPERSIST_TLW_KIND "Window" -// names for various persistent options -#define wxPERSIST_TLW_X "x" -#define wxPERSIST_TLW_Y "y" -#define wxPERSIST_TLW_W "w" -#define wxPERSIST_TLW_H "h" - -#define wxPERSIST_TLW_MAXIMIZED "Maximized" -#define wxPERSIST_TLW_ICONIZED "Iconized" - // ---------------------------------------------------------------------------- // wxPersistentTLW: supports saving/restoring window position and size as well // as maximized/iconized/restore state // ---------------------------------------------------------------------------- -class wxPersistentTLW : public wxPersistentWindow +class wxPersistentTLW : public wxPersistentWindow, + private wxTopLevelWindow::GeometrySerializer { public: wxPersistentTLW(wxTopLevelWindow *tlw) @@ -50,96 +41,28 @@ public: { const wxTopLevelWindow * const tlw = Get(); - const wxPoint pos = tlw->GetScreenPosition(); - SaveValue(wxPERSIST_TLW_X, pos.x); - SaveValue(wxPERSIST_TLW_Y, pos.y); - - // notice that we use GetSize() here and not GetClientSize() because - // the latter doesn't return correct results for the minimized windows - // (at least not under Windows) - // - // of course, it shouldn't matter anyhow usually, the client size - // should be preserved as well unless the size of the decorations - // changed between the runs - const wxSize size = tlw->GetSize(); - SaveValue(wxPERSIST_TLW_W, size.x); - SaveValue(wxPERSIST_TLW_H, size.y); - - SaveValue(wxPERSIST_TLW_MAXIMIZED, tlw->IsMaximized()); - SaveValue(wxPERSIST_TLW_ICONIZED, tlw->IsIconized()); -#ifdef __WXGTK20__ - SaveValue("decor_l", tlw->m_decorSize.left); - SaveValue("decor_r", tlw->m_decorSize.right); - SaveValue("decor_t", tlw->m_decorSize.top); - SaveValue("decor_b", tlw->m_decorSize.bottom); -#endif + tlw->SaveGeometry(*this); } virtual bool Restore() wxOVERRIDE { wxTopLevelWindow * const tlw = Get(); - wxPoint pos; - wxSize size; - - const bool hasPos = RestoreValue(wxPERSIST_TLW_X, &pos.x) && - RestoreValue(wxPERSIST_TLW_Y, &pos.y); - const bool hasSize = RestoreValue(wxPERSIST_TLW_W, &size.x) && - RestoreValue(wxPERSIST_TLW_H, &size.y); -#ifdef __WXGTK20__ - wxTopLevelWindowGTK::DecorSize decorSize; - if (tlw->m_decorSize.top == 0 && - RestoreValue("decor_l", &decorSize.left) && - RestoreValue("decor_r", &decorSize.right) && - RestoreValue("decor_t", &decorSize.top) && - RestoreValue("decor_b", &decorSize.bottom)) - { - tlw->m_decorSize = decorSize; - } -#endif - - if ( hasPos ) - { - // to avoid making the window completely invisible if it had been - // shown on a monitor which was disconnected since the last run - // (this is pretty common for notebook with external displays) - // - // NB: we should allow window position to be (slightly) off screen, - // it's not uncommon to position the window so that its upper - // left corner has slightly negative coordinate - if ( wxDisplay::GetFromPoint(pos) != wxNOT_FOUND || - (hasSize && wxDisplay::GetFromPoint(pos + size) != wxNOT_FOUND) ) - { - tlw->Move(pos, wxSIZE_ALLOW_MINUS_ONE); - } - //else: should we try to adjust position/size somehow? - } - - if ( hasSize ) - { - // a previous version of the program could have saved the window - // size which used to be big enough, but which is not big enough - // any more for the new version, so check that the size we restore - // doesn't cut off parts of the window - size.IncTo(tlw->GetBestSize()); - tlw->SetSize(size); - } - - // note that the window can be both maximized and iconized - bool maximized; - if ( RestoreValue(wxPERSIST_TLW_MAXIMIZED, &maximized) && maximized ) - tlw->Maximize(); - - bool iconized; - if ( RestoreValue(wxPERSIST_TLW_ICONIZED, &iconized) && iconized ) - tlw->Iconize(); - - // the most important property of the window that we restore is its - // size, so disregard the value of hasPos here - return hasSize; + return tlw->RestoreToGeometry(*this); } virtual wxString GetKind() const wxOVERRIDE { return wxPERSIST_TLW_KIND; } + +private: + virtual bool SaveField(const wxString& name, int value) const wxOVERRIDE + { + return SaveValue(name, value); + } + + virtual bool RestoreField(const wxString& name, int* value) wxOVERRIDE + { + return RestoreValue(name, value); + } }; inline wxPersistentObject *wxCreatePersistentObject(wxTopLevelWindow *tlw) diff --git a/include/wx/private/tlwgeom.h b/include/wx/private/tlwgeom.h new file mode 100644 index 0000000000..aa601f14e3 --- /dev/null +++ b/include/wx/private/tlwgeom.h @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/private/tlwgeom.h +// Purpose: Declaration of platform-specific and private wxTLWGeometry. +// Author: Vadim Zeitlin +// Created: 2018-04-29 +// Copyright: (c) 2018 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_PRIVATE_TLWGEOM_H_ +#define _WX_PRIVATE_TLWGEOM_H_ + +#include "wx/display.h" +#include "wx/toplevel.h" + +// ---------------------------------------------------------------------------- +// wxTLWGeometryBase: abstract base class for platform-specific classes +// ---------------------------------------------------------------------------- + +// wxTLWGeometry contains full information about the window geometry, which may +// include things other than the obvious ones like its current position and +// size (e.g. under MSW it also stores the position of the maximized window, +// under GTK the size of non-client decorations etc). It is private to wx and +// is only used indirectly, via wxTopLevelWindow::SaveGeometry() and +// RestoreToGeometry() methods, in the public API. + +class wxTLWGeometryBase +{ +public: + typedef wxTopLevelWindow::GeometrySerializer Serializer; + + wxTLWGeometryBase() {} + virtual ~wxTLWGeometryBase() {} + + // Initialize from the given window. + virtual bool GetFrom(const wxTopLevelWindow* tlw) = 0; + + // Resize the window to use this geometry. + virtual bool ApplyTo(wxTopLevelWindow* tlw) = 0; + + // Serialize or deserialize the object by using the provided object for + // writing/reading the values of the different fields of this object. + virtual bool Save(const Serializer& ser) const = 0; + virtual bool Restore(Serializer& ser) = 0; +}; + +// ---------------------------------------------------------------------------- +// wxTLWGeometryGeneric: simplest possible generic implementation +// ---------------------------------------------------------------------------- + +// names for various persistent options +#define wxPERSIST_TLW_X "x" +#define wxPERSIST_TLW_Y "y" +#define wxPERSIST_TLW_W "w" +#define wxPERSIST_TLW_H "h" + +#define wxPERSIST_TLW_MAXIMIZED "Maximized" +#define wxPERSIST_TLW_ICONIZED "Iconized" + +class wxTLWGeometryGeneric : public wxTLWGeometryBase +{ +public: + wxTLWGeometryGeneric() + { + m_hasPos = + m_hasSize = + m_iconized = + m_maximized = false; + } + + virtual bool Save(const Serializer& ser) const wxOVERRIDE + { + if ( !ser.SaveField(wxPERSIST_TLW_X, m_rectScreen.x) || + !ser.SaveField(wxPERSIST_TLW_Y, m_rectScreen.y) ) + return false; + + if ( !ser.SaveField(wxPERSIST_TLW_W, m_rectScreen.width) || + !ser.SaveField(wxPERSIST_TLW_H, m_rectScreen.height) ) + return false; + + if ( !ser.SaveField(wxPERSIST_TLW_MAXIMIZED, m_maximized) ) + return false; + + if ( !ser.SaveField(wxPERSIST_TLW_ICONIZED, m_iconized) ) + return false; + + return true; + } + + virtual bool Restore(Serializer& ser) wxOVERRIDE + { + m_hasPos = ser.RestoreField(wxPERSIST_TLW_X, &m_rectScreen.x) && + ser.RestoreField(wxPERSIST_TLW_Y, &m_rectScreen.y); + + m_hasSize = ser.RestoreField(wxPERSIST_TLW_W, &m_rectScreen.width) && + ser.RestoreField(wxPERSIST_TLW_H, &m_rectScreen.height); + + int tmp; + if ( ser.RestoreField(wxPERSIST_TLW_MAXIMIZED, &tmp) ) + m_maximized = tmp != 0; + + if ( ser.RestoreField(wxPERSIST_TLW_ICONIZED, &tmp) ) + m_iconized = tmp != 0; + + // If we restored at least something, return true. + return m_hasPos || m_hasSize || m_maximized || m_iconized; + } + + virtual bool GetFrom(const wxTopLevelWindow* tlw) wxOVERRIDE + { + m_rectScreen = tlw->GetScreenRect(); + m_hasPos = + m_hasSize = true; + m_iconized = tlw->IsIconized(); + m_maximized = tlw->IsMaximized(); + + return true; + } + + virtual bool ApplyTo(wxTopLevelWindow* tlw) wxOVERRIDE + { + if ( m_hasPos ) + { + // to avoid making the window completely invisible if it had been + // shown on a monitor which was disconnected since the last run + // (this is pretty common for notebook with external displays) + // + // NB: we should allow window position to be (slightly) off screen, + // it's not uncommon to position the window so that its upper + // left corner has slightly negative coordinate + if ( wxDisplay::GetFromPoint(m_rectScreen.GetTopLeft()) != wxNOT_FOUND || + (m_hasSize && + wxDisplay::GetFromPoint(m_rectScreen.GetBottomRight()) != wxNOT_FOUND) ) + { + tlw->Move(m_rectScreen.GetTopLeft(), wxSIZE_ALLOW_MINUS_ONE); + } + //else: should we try to adjust position/size somehow? + } + + if ( m_hasSize ) + { + // a previous version of the program could have saved the window + // size which used to be big enough, but which is not big enough + // any more for the new version, so check that the size we restore + // doesn't cut off parts of the window + wxSize size = m_rectScreen.GetSize(); + size.IncTo(tlw->GetBestSize()); + tlw->SetSize(size); + } + + // note that the window can be both maximized and iconized + if ( m_maximized ) + tlw->Maximize(); + + if ( m_iconized ) + tlw->Iconize(); + + return true; + } + +private: + wxRect m_rectScreen; + bool m_hasPos; + bool m_hasSize; + bool m_iconized; + bool m_maximized; +}; + +#ifdef __WXGTK20__ + #include "wx/gtk/private/tlwgeom.h" +#else + class wxTLWGeometry : public wxTLWGeometryGeneric + { + }; +#endif + +#endif // _WX_PRIVATE_TLWGEOM_H_ diff --git a/include/wx/toplevel.h b/include/wx/toplevel.h index 50b5e26121..9927d67db1 100644 --- a/include/wx/toplevel.h +++ b/include/wx/toplevel.h @@ -238,6 +238,35 @@ public: wxWindow *SetTmpDefaultItem(wxWindow *win) { wxWindow *old = GetDefaultItem(); m_winTmpDefault = win; return old; } + + // Class for saving/restoring fields describing the window geometry. + // + // This class is used by the functions below to allow saving the geometry + // of the window and restoring it later. The components describing geometry + // are platform-dependent, so there is no struct containing them and + // instead the methods of this class are used to save or [try to] restore + // whichever components are used under the current platform. + class GeometrySerializer + { + public: + virtual ~GeometrySerializer() {} + + // If saving a field returns false, it's fatal error and SaveGeometry() + // will return false. + virtual bool SaveField(const wxString& name, int value) const = 0; + + // If restoring a field returns false, it just means that the field is + // not present and RestoreToGeometry() still continues with restoring + // the other values. + virtual bool RestoreField(const wxString& name, int* value) = 0; + }; + + // Save the current window geometry using the provided serializer and + // restore the window to the previously saved geometry. + bool SaveGeometry(const GeometrySerializer& ser) const; + bool RestoreToGeometry(GeometrySerializer& ser); + + // implementation only from now on // ------------------------------- diff --git a/interface/wx/toplevel.h b/interface/wx/toplevel.h index 0dd1befbc8..44731c8790 100644 --- a/interface/wx/toplevel.h +++ b/interface/wx/toplevel.h @@ -350,6 +350,94 @@ public: */ void Restore(); + /** + Class used with SaveGeometry() and RestoreToGeometry(). + + This is an abstract base class, i.e. to use it you must define a + derived class implementing the pure virtual SaveField() and + RestoreField() methods. + + For example, if you wished to store the window geometry in a database, + you could derive a class saving fields such as "width" or "height" in a + table in this database and restoring them from it later. + + @since 3.1.2 + */ + class GeometrySerializer + { + /** + Save a single field with the given value. + + Note that if this function returns @false, SaveGeometry() supposes + that saving the geometry failed and returns @false itself, without + even trying to save anything else. + + @param name uniquely identifies the field but is otherwise + arbitrary. + @param value value of the field (can be positive or negative, i.e. + it can't be assumed that a value like -1 is invalid). + + @return @true if the field was saved or @false if saving it failed, + resulting in wxTopLevelWindow::SaveGeometry() failure. + */ + virtual bool SaveField(const wxString& name, int value) const = 0; + + /** + Try to restore a single field. + + Unlike for SaveField(), returning @false from this function may + indicate that the value simply wasn't present and doesn't prevent + RestoreToGeometry() from continuing with trying to restore the + other values. + + @param name uniquely identifies the field + @param value non-@NULL pointer to the value to be filled by this + function + + @return @true if the value was retrieved or @false if it wasn't + found or an error occurred. + */ + virtual bool RestoreField(const wxString& name, int* value) = 0; + }; + + /** + Restores the window to the previously saved geometry. + + This is a companion function to SaveGeometry() and can be called later + to restore the window to the geometry it had when it was saved. + + @param ser An object implementing GeometrySerializer virtual methods. + + @return @true if any (and, usually, but not necessarily, all) of the + window geometry attributes were restored or @false if there was no + saved geometry information at all or restoring it failed. + + @since 3.1.2 + */ + bool RestoreToGeometry(GeometrySerializer& ser); + + /** + Save the current window geometry to allow restoring it later. + + After calling this function, window geometry is saved in the provided + serializer and calling RestoreToGeometry() with the same serializer + later (i.e. usually during a subsequent program execution) would + restore the window to the same position, size, maximized/minimized + state etc. + + This function is used by wxPersistentTLW, so it is not necessary to use + it if the goal is to just save and restore window geometry in the + simplest possible way. However is more flexibility is required, it can + be also used directly with a custom serializer object. + + @param ser An object implementing GeometrySerializer virtual methods. + + @return @true if the geometry was saved, @false if doing it failed + + @since 3.1.2 + */ + bool SaveGeometry(const GeometrySerializer& ser) const; + /** Changes the default item for the panel, usually @a win is a button. diff --git a/src/common/toplvcmn.cpp b/src/common/toplvcmn.cpp index c5eff7f463..a5660a3b23 100644 --- a/src/common/toplvcmn.cpp +++ b/src/common/toplvcmn.cpp @@ -31,6 +31,8 @@ #include "wx/display.h" +#include "wx/private/tlwgeom.h" + // ---------------------------------------------------------------------------- // event table // ---------------------------------------------------------------------------- @@ -310,6 +312,28 @@ void wxTopLevelWindowBase::DoCentre(int dir) SetSize(rect, wxSIZE_ALLOW_MINUS_ONE); } +// ---------------------------------------------------------------------------- +// Saving/restoring geometry +// ---------------------------------------------------------------------------- + +bool wxTopLevelWindowBase::SaveGeometry(const GeometrySerializer& ser) const +{ + wxTLWGeometry geom; + if ( !geom.GetFrom(static_cast(this)) ) + return false; + + return geom.Save(ser); +} + +bool wxTopLevelWindowBase::RestoreToGeometry(GeometrySerializer& ser) +{ + wxTLWGeometry geom; + if ( !geom.Restore(ser) ) + return false; + + return geom.ApplyTo(static_cast(this)); +} + // ---------------------------------------------------------------------------- // wxTopLevelWindow size management: we exclude the areas taken by // menu/status/toolbars from the client area, so the client area is what's