wxExtend
Additional templates and function helpers for wxWidgets
Loading...
Searching...
No Matches
tlwgeom.h
1/*
2 ​​​SPDX-License-Identifier: GPL-3.0-or-later
3 Copyright © 2015-2022 Amebis
4 Copyright © 2016 GÉANT
5*/
6
7#pragma once
8
9#include <codeanalysis\warnings.h>
10#pragma warning(push)
11#pragma warning(disable: WXWIDGETS_CODE_ANALYSIS_WARNINGS)
12#include <wx/dynlib.h>
13#include <wx/private/tlwgeom.h>
14#pragma warning(pop)
15
16#ifndef USER_DEFAULT_SCREEN_DPI
17#define USER_DEFAULT_SCREEN_DPI 96
18#endif
19
21
22#define wxPERSIST_TLW_MONITOR_X "xmon"
23#define wxPERSIST_TLW_MONITOR_Y "ymon"
24#define wxPERSIST_TLW_MONITOR_W "wmon"
25#define wxPERSIST_TLW_MONITOR_H "hmon"
26#define wxPERSIST_TLW_DPI_HORZ "xdpi"
27#define wxPERSIST_TLW_DPI_VERT "ydpi"
28
29class wxTLWGeometryEx : public wxTLWGeometryBase
30{
31public:
32 wxTLWGeometryEx()
33 {
34 wxZeroMemory(m_placement);
35 m_placement.length = sizeof(m_placement);
36
37 wxZeroMemory(m_mntinfo);
38 m_mntinfo.cbSize = sizeof(m_mntinfo);
39
40 m_dpiHorz = USER_DEFAULT_SCREEN_DPI;
41 m_dpiVert = USER_DEFAULT_SCREEN_DPI;
42 }
43
44 virtual bool Save(const Serializer& ser) const wxOVERRIDE
45 {
46 // For compatibility with the existing saved positions/sizes, use the
47 // same keys as the generic version (which was previously used under
48 // MSW too).
49
50 // Normal position and size.
51 const RECT& rc = m_placement.rcNormalPosition;
52 if (!ser.SaveField(wxPERSIST_TLW_X, rc.left) ||
53 !ser.SaveField(wxPERSIST_TLW_Y, rc.top) ||
54 !ser.SaveField(wxPERSIST_TLW_W, rc.right - rc.left) ||
55 !ser.SaveField(wxPERSIST_TLW_H, rc.bottom - rc.top))
56 return false;
57
58 // Maximized/minimized state.
59 UINT show = m_placement.showCmd;
60 if (!ser.SaveField(wxPERSIST_TLW_MAXIMIZED, show == SW_SHOWMAXIMIZED))
61 return false;
62
63 if (!ser.SaveField(wxPERSIST_TLW_ICONIZED, show == SW_SHOWMINIMIZED))
64 return false;
65
66 // Maximized window position.
67 const POINT pt = m_placement.ptMaxPosition;
68 if (!ser.SaveField(wxPERSIST_TLW_MAX_X, pt.x) ||
69 !ser.SaveField(wxPERSIST_TLW_MAX_Y, pt.y))
70 return false;
71
72 // We don't currently save the minimized window position, it doesn't
73 // seem useful for anything and is probably just a left over from
74 // Windows 3.1 days, when icons were positioned on the desktop instead
75 // of being located in the taskbar.
76
77 // Monitor position and size.
78 const RECT& rcMon = m_mntinfo.rcWork;
79 if (!ser.SaveField(wxPERSIST_TLW_MONITOR_X, rcMon.left) ||
80 !ser.SaveField(wxPERSIST_TLW_MONITOR_Y, rcMon.top) ||
81 !ser.SaveField(wxPERSIST_TLW_MONITOR_W, rcMon.right - rcMon.left) ||
82 !ser.SaveField(wxPERSIST_TLW_MONITOR_H, rcMon.bottom - rcMon.top))
83 return false;
84
85 // DPI.
86 if (!ser.SaveField(wxPERSIST_TLW_DPI_HORZ, m_dpiHorz) ||
87 !ser.SaveField(wxPERSIST_TLW_DPI_VERT, m_dpiVert))
88 return false;
89
90 return true;
91 }
92
93 virtual bool Restore(Serializer& ser) wxOVERRIDE
94 {
95 // Normal position and size.
96 wxRect r;
97 if (!ser.RestoreField(wxPERSIST_TLW_X, &r.x) ||
98 !ser.RestoreField(wxPERSIST_TLW_Y, &r.y) ||
99 !ser.RestoreField(wxPERSIST_TLW_W, &r.width) ||
100 !ser.RestoreField(wxPERSIST_TLW_H, &r.height))
101 return false;
102 wxCopyRectToRECT(r, m_placement.rcNormalPosition);
103
104 // Maximized/minimized state.
105 //
106 // Note the special case of SW_MINIMIZE: while GetWindowPlacement()
107 // returns SW_SHOWMINIMIZED when the window is iconized, we restore it
108 // as SW_MINIMIZE as this is what the code in wxTLW checks to determine
109 // whether the window is supposed to be iconized or not.
110 //
111 // Just to confuse matters further, note that SW_MAXIMIZE is exactly
112 // the same thing as SW_SHOWMAXIMIZED.
113 int tmp;
114 UINT& show = m_placement.showCmd;
115 if (ser.RestoreField(wxPERSIST_TLW_MAXIMIZED, &tmp) && tmp)
116 show = SW_MAXIMIZE;
117 else if (ser.RestoreField(wxPERSIST_TLW_ICONIZED, &tmp) && tmp)
118 show = SW_MINIMIZE;
119 else
120 show = SW_SHOWNORMAL;
121
122 // Maximized window position.
123 if (ser.RestoreField(wxPERSIST_TLW_MAX_X, &r.x) &&
124 ser.RestoreField(wxPERSIST_TLW_MAX_Y, &r.y))
125 {
126 m_placement.ptMaxPosition.x = r.x;
127 m_placement.ptMaxPosition.y = r.y;
128 } else {
129 m_placement.ptMaxPosition.x = -1;
130 m_placement.ptMaxPosition.y = -1;
131 }
132
133 m_placement.ptMinPosition.x = -1;
134 m_placement.ptMinPosition.y = -1;
135
136 // Monitor position and size.
137 wxRect rmon;
138 if (!ser.RestoreField(wxPERSIST_TLW_MONITOR_X, &rmon.x) ||
139 !ser.RestoreField(wxPERSIST_TLW_MONITOR_Y, &rmon.y) ||
140 !ser.RestoreField(wxPERSIST_TLW_MONITOR_W, &rmon.width) ||
141 !ser.RestoreField(wxPERSIST_TLW_MONITOR_H, &rmon.height))
142 return false;
143 wxCopyRectToRECT(rmon, m_mntinfo.rcWork);
144
145 // DPI.
146 if (!ser.RestoreField(wxPERSIST_TLW_DPI_HORZ, &r.x) ||
147 !ser.RestoreField(wxPERSIST_TLW_DPI_VERT, &r.y))
148 return false;
149 m_dpiHorz = r.x;
150 m_dpiVert = r.y;
151
152 return true;
153 }
154
155 virtual bool GetFrom(const wxTopLevelWindow* tlw) wxOVERRIDE
156 {
157 WXHWND hWnd = GetHwndOf(tlw);
158 if (!::GetWindowPlacement(hWnd, &m_placement))
159 {
160 wxLogLastError(wxS("GetWindowPlacement"));
161 return false;
162 }
163
164 HMONITOR hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
165 wxASSERT_MSG(hMonitor, wxT("error locating monitor"));
166 _Analysis_assume_(hMonitor);
167 if (!::GetMonitorInfo(hMonitor, &m_mntinfo))
168 {
169 wxLogLastError(wxS("GetMonitorInfo"));
170 return false;
171 }
172
173 GetDPI(hWnd, &m_dpiHorz, &m_dpiVert) || GetDPI(hMonitor, &m_dpiHorz, &m_dpiVert);
174
175 return true;
176 }
177
178 virtual bool ApplyTo(wxTopLevelWindow* tlw) wxOVERRIDE
179 {
180 // There is a subtlety here: if the window is currently hidden,
181 // restoring its geometry shouldn't show it, so we must use SW_HIDE as
182 // show command, but showing it later should restore it to the correct
183 // state, so we need to remember it in wxTLW itself. And even if it's
184 // currently shown, we still need to update its show command, so that
185 // it matches the real window state after SetWindowPlacement() call.
186 tlw->MSWSetShowCommand(m_placement.showCmd);
187 if (!tlw->IsShown())
188 {
189 m_placement.showCmd = SW_HIDE;
190 }
191
192 // Get monitor to restore window to.
193 HMONITOR hMonitor = ::MonitorFromRect(&m_mntinfo.rcWork, MONITOR_DEFAULTTONEAREST);
194 wxASSERT_MSG(hMonitor, wxT("error locating monitor"));
195 _Analysis_assume_(hMonitor);
196 MONITORINFO mntinfo;
197 mntinfo.cbSize = sizeof(mntinfo);
198 if (!::GetMonitorInfo(hMonitor, &mntinfo))
199 {
200 wxLogLastError(wxS("GetMonitorInfo"));
201 return false;
202 }
203
204 UINT dpiHorz, dpiVert;
205 GetDPI(hMonitor, &dpiHorz, &dpiVert);
206
207 SIZE
208 sizeWorkPrev = {
209 m_mntinfo.rcWork.right - m_mntinfo.rcWork.left,
210 m_mntinfo.rcWork.bottom - m_mntinfo.rcWork.top
211 },
212 sizeWork = {
213 mntinfo.rcWork.right - mntinfo.rcWork.left,
214 mntinfo.rcWork.bottom - mntinfo.rcWork.top
215 };
216
217 //
218 // Project the coordinates:
219 // - Position relative to monitor working area center.
220 // - Scale according to DPI.
221 //
222
223 if (m_placement.ptMaxPosition.x != -1 && m_placement.ptMaxPosition.y != -1) {
224 m_placement.ptMaxPosition.x = wxMulDivInt32(m_placement.ptMaxPosition.x - m_mntinfo.rcWork.left, sizeWork.cx, sizeWorkPrev.cx) + mntinfo.rcWork.left;
225 m_placement.ptMaxPosition.y = wxMulDivInt32(m_placement.ptMaxPosition.y - m_mntinfo.rcWork.top, sizeWork.cy, sizeWorkPrev.cy) + mntinfo.rcWork.top;
226 }
227
228 SIZE sizeWndPrev, sizeWnd;
229 HWND hWnd = GetHwndOf(tlw);
230
231 if (tlw->GetWindowStyle() & wxRESIZE_BORDER) {
232 sizeWndPrev.cx = m_placement.rcNormalPosition.right - m_placement.rcNormalPosition.left;
233 sizeWndPrev.cy = m_placement.rcNormalPosition.bottom - m_placement.rcNormalPosition.top;
234 sizeWnd.cx = wxMulDivInt32(sizeWndPrev.cx, dpiHorz, m_dpiHorz);
235 sizeWnd.cy = wxMulDivInt32(sizeWndPrev.cy, dpiVert, m_dpiVert);
236 } else {
237 // The window is not resizable. Do not change its size.
238 WINDOWPLACEMENT placement = { sizeof(placement) };
239 if (!::GetWindowPlacement(hWnd, &placement))
240 {
241 wxLogLastError(wxS("GetWindowPlacement"));
242 return false;
243 }
244 SIZE size = {
245 placement.rcNormalPosition.right - placement.rcNormalPosition.left,
246 placement.rcNormalPosition.bottom - placement.rcNormalPosition.top
247 };
248
249 UINT dpiWndHorz, dpiWndVert;
250 GetDPI(hWnd, &dpiWndHorz, &dpiWndVert) || GetDPI(::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), &dpiWndHorz, &dpiWndVert);
251
252 sizeWndPrev.cx = wxMulDivInt32(size.cx, m_dpiHorz, dpiWndHorz);
253 sizeWndPrev.cy = wxMulDivInt32(size.cy, m_dpiVert, dpiWndVert);
254 sizeWnd.cx = wxMulDivInt32(size.cx, dpiHorz, dpiWndHorz);
255 sizeWnd.cy = wxMulDivInt32(size.cy, dpiVert, dpiWndVert);
256 }
257
258 m_placement.rcNormalPosition.left = wxMulDivInt32(m_placement.rcNormalPosition.left + sizeWndPrev.cx / 2 - m_mntinfo.rcWork.left, sizeWork.cx, sizeWorkPrev.cx) + mntinfo.rcWork.left - sizeWnd.cx / 2;
259 m_placement.rcNormalPosition.top = wxMulDivInt32(m_placement.rcNormalPosition.top + sizeWndPrev.cy / 2 - m_mntinfo.rcWork.top, sizeWork.cy, sizeWorkPrev.cy) + mntinfo.rcWork.top - sizeWnd.cy / 2;
260 m_placement.rcNormalPosition.right = m_placement.rcNormalPosition.left + sizeWnd.cx;
261 m_placement.rcNormalPosition.bottom = m_placement.rcNormalPosition.top + sizeWnd.cy;
262
263 if (!::SetWindowPlacement(hWnd, &m_placement))
264 {
265 wxLogLastError(wxS("SetWindowPlacement"));
266 return false;
267 }
268
269 return true;
270 }
271
272private:
273 static bool GetDPI(_In_ HWND hWnd, _Out_ UINT *dpiHorz, _Out_ UINT *dpiVert)
274 {
275 wxASSERT(dpiHorz);
276 wxASSERT(dpiVert);
277
278#if wxUSE_DYNLIB_CLASS
279 typedef UINT(WINAPI *GetDpiForWindow_t)(HWND);
280 static bool s_checkedGetDpiForWindow = false;
281 static GetDpiForWindow_t s_pfnGetDpiForWindow = NULL;
282 if (!s_checkedGetDpiForWindow && s_dllUser32.IsLoaded()) {
283 s_pfnGetDpiForWindow = (GetDpiForWindow_t)s_dllUser32.RawGetSymbol(wxT("GetDpiForWindow"));
284 s_checkedGetDpiForWindow = true;
285 }
286
287 if (s_pfnGetDpiForWindow) {
288 *dpiHorz = *dpiVert = s_pfnGetDpiForWindow(hWnd);
289 return true;
290 }
291#endif
292
293 *dpiHorz = *dpiVert = USER_DEFAULT_SCREEN_DPI;
294 return false;
295 }
296
297 static bool GetDPI(_In_ HMONITOR hMonitor, _Out_ UINT *dpiHorz, _Out_ UINT *dpiVert)
298 {
299 wxASSERT(dpiHorz);
300 wxASSERT(dpiVert);
301
302#if wxUSE_DYNLIB_CLASS
303 enum MONITOR_DPI_TYPE {
304 MDT_EFFECTIVE_DPI = 0,
305 MDT_ANGULAR_DPI = 1,
306 MDT_RAW_DPI = 2,
307 MDT_DEFAULT = MDT_EFFECTIVE_DPI
308 };
309 typedef HRESULT(WINAPI *GetDpiForMonitor_t)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);
310 static GetDpiForMonitor_t s_pfnGetDpiForMonitor = NULL;
311 if (!s_pfnGetDpiForMonitor) {
312 if (s_dllShCore.IsLoaded())
313 s_pfnGetDpiForMonitor = (GetDpiForMonitor_t)s_dllShCore.GetSymbol(wxT("GetDpiForMonitor"));
314 }
315
316 if (s_pfnGetDpiForMonitor) {
317 s_pfnGetDpiForMonitor(hMonitor, MDT_DEFAULT, dpiHorz, dpiVert);
318 return true;
319 }
320#endif
321
322 *dpiHorz = *dpiVert = USER_DEFAULT_SCREEN_DPI;
323 return false;
324 }
325
326private:
327 WINDOWPLACEMENT m_placement;
328 MONITORINFO m_mntinfo;
329 UINT m_dpiHorz;
330 UINT m_dpiVert;
331
332#if wxUSE_DYNLIB_CLASS
333 static wxDynamicLibrary s_dllUser32;
334 static wxDynamicLibrary s_dllShCore;
335#endif
336};
337