/////////////////////////////////////////////////////////////////////////////// // Name: wx/msw/private/tlwgeom.h // Purpose: wxMSW-specific wxTLWGeometry class. // Author: Vadim Zeitlin // Created: 2018-04-29 // Copyright: (c) 2018 Vadim Zeitlin <vadim@wxwidgets.org> // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// #ifndef _WX_MSW_PRIVATE_TLWGEOM_H_ #define _WX_MSW_PRIVATE_TLWGEOM_H_ #include "wx/log.h" #include "wx/msw/private.h" // names for MSW-specific options #define wxPERSIST_TLW_MAX_X "xmax" #define wxPERSIST_TLW_MAX_Y "ymax" class wxTLWGeometry : public wxTLWGeometryBase { public: wxTLWGeometry() { wxZeroMemory(m_placement); m_placement.length = sizeof(m_placement); } virtual bool Save(const Serializer& ser) const wxOVERRIDE { // For compatibility with the existing saved positions/sizes, use the // same keys as the generic version (which was previously used under // MSW too). // Normal position and size. const RECT& rc = m_placement.rcNormalPosition; if ( !ser.SaveField(wxPERSIST_TLW_X, rc.left) || !ser.SaveField(wxPERSIST_TLW_Y, rc.top) ) return false; if ( !ser.SaveField(wxPERSIST_TLW_W, rc.right - rc.left) || !ser.SaveField(wxPERSIST_TLW_H, rc.bottom - rc.top) ) return false; // Maximized/minimized state. UINT show = m_placement.showCmd; if ( !ser.SaveField(wxPERSIST_TLW_MAXIMIZED, show == SW_SHOWMAXIMIZED) ) return false; if ( !ser.SaveField(wxPERSIST_TLW_ICONIZED, show == SW_SHOWMINIMIZED) ) return false; // Maximized window position. const POINT pt = m_placement.ptMaxPosition; if ( !ser.SaveField(wxPERSIST_TLW_MAX_X, pt.x) || !ser.SaveField(wxPERSIST_TLW_MAX_Y, pt.y) ) return false; // We don't currently save the minimized window position, it doesn't // seem useful for anything and is probably just a left over from // Windows 3.1 days, when icons were positioned on the desktop instead // of being located in the taskbar. return true; } virtual bool Restore(Serializer& ser) wxOVERRIDE { // Normal position and size. wxRect r; if ( !ser.RestoreField(wxPERSIST_TLW_X, &r.x) || !ser.RestoreField(wxPERSIST_TLW_Y, &r.y) || !ser.RestoreField(wxPERSIST_TLW_W, &r.width) || !ser.RestoreField(wxPERSIST_TLW_H, &r.height) ) return false; wxCopyRectToRECT(r, m_placement.rcNormalPosition); // Maximized/minimized state. // // Note the special case of SW_MINIMIZE: while GetWindowPlacement() // returns SW_SHOWMINIMIZED when the window is iconized, we restore it // as SW_MINIMIZE as this is what the code in wxTLW checks to determine // whether the window is supposed to be iconized or not. // // Just to confuse matters further, note that SW_MAXIMIZE is exactly // the same thing as SW_SHOWMAXIMIZED. int tmp; UINT& show = m_placement.showCmd; if ( ser.RestoreField(wxPERSIST_TLW_MAXIMIZED, &tmp) && tmp ) show = SW_MAXIMIZE; else if ( ser.RestoreField(wxPERSIST_TLW_ICONIZED, &tmp) && tmp ) show = SW_MINIMIZE; else show = SW_SHOWNORMAL; // Maximized window position. if ( ser.RestoreField(wxPERSIST_TLW_MAX_X, &r.x) && ser.RestoreField(wxPERSIST_TLW_MAX_Y, &r.y) ) { m_placement.ptMaxPosition.x = r.x; m_placement.ptMaxPosition.y = r.y; } return true; } virtual bool GetFrom(const wxTopLevelWindow* tlw) wxOVERRIDE { if ( !::GetWindowPlacement(GetHwndOf(tlw), &m_placement) ) { wxLogLastError(wxS("GetWindowPlacement")); return false; } if (m_placement.showCmd != SW_SHOWMAXIMIZED && m_placement.showCmd != SW_SHOWMINIMIZED) { RECT rcWindow; ::GetWindowRect(tlw->GetHWND(), &rcWindow); // Height and width should be the same unless the user performed // an Aero Snap operation. const RECT rcNormal = m_placement.rcNormalPosition; if ((rcWindow.bottom - rcWindow.top) != (rcNormal.bottom - rcNormal.top) || (rcWindow.right - rcWindow.left) != (rcNormal.right - rcNormal.left)) { WinStruct<MONITORINFO> mi; if (!::GetMonitorInfo(::MonitorFromWindow(tlw->GetHWND(), MONITOR_DEFAULTTONEAREST), &mi)) { wxLogLastError("GetMonitorInfo"); return false; } // If the tray is on the top or the left, then the rectangle needs to // be adjusted to match what ::SetWindowPlacement expects. if (mi.rcMonitor.top < mi.rcWork.top || mi.rcMonitor.left < mi.rcWork.left) { // Negative offset to eliminate the tray width/height. OffsetRect(&rcWindow, (mi.rcMonitor.left - mi.rcWork.left), (mi.rcMonitor.top - mi.rcWork.top)); } ::CopyRect(&m_placement.rcNormalPosition, &rcWindow); } } return true; } virtual bool ApplyTo(wxTopLevelWindow* tlw) wxOVERRIDE { // There is a subtlety here: if the window is currently hidden, // restoring its geometry shouldn't show it, so we must use SW_HIDE as // show command, but showing it later should restore it to the correct // state, so we need to remember it in wxTLW itself. And even if it's // currently shown, we still need to update its show command, so that // it matches the real window state after SetWindowPlacement() call. tlw->MSWSetShowCommand(m_placement.showCmd); if ( !tlw->IsShown() ) { m_placement.showCmd = SW_HIDE; } const wxSize oldDPI = tlw->GetDPI(); if ( !::SetWindowPlacement(GetHwndOf(tlw), &m_placement) ) { wxLogLastError(wxS("SetWindowPlacement")); return false; } // Check if a window DPI hasn't changed, as the result of being placed // on a monitor with a different DPI from the main one because in this // case its size is not restored properly. const wxSize newDPI = tlw->GetDPI(); if ( oldDPI != newDPI ) { // So try setting the placement again as now it will use the // correct scaling factor, because the window is already on the // correct monitor. if ( !::SetWindowPlacement(GetHwndOf(tlw), &m_placement) ) { wxLogLastError(wxS("SetWindowPlacement (2nd time)")); return false; } } return true; } private: WINDOWPLACEMENT m_placement; }; #endif // _WX_MSW_PRIVATE_TLWGEOM_H_