|
1
|
1 #pragma once
|
|
|
2
|
|
|
3 #ifdef _WIN32
|
|
|
4
|
|
|
5 #include "win32_misc.h"
|
|
|
6 #include "../SDK/cfg_var.h"
|
|
|
7
|
|
|
8 static BOOL AdjustWindowRectHelper(CWindow wnd, CRect & rc) {
|
|
|
9 const DWORD style = wnd.GetWindowLong(GWL_STYLE), exstyle = wnd.GetWindowLong(GWL_EXSTYLE);
|
|
|
10 return AdjustWindowRectEx(&rc,style,(style & WS_POPUP) ? wnd.GetMenu() != NULL : FALSE, exstyle);
|
|
|
11 }
|
|
|
12
|
|
|
13 static void AdjustRectToScreenArea(CRect & rc, CRect rcParent) {
|
|
|
14 HMONITOR monitor = MonitorFromRect(rcParent,MONITOR_DEFAULTTONEAREST);
|
|
|
15 MONITORINFO mi = {sizeof(MONITORINFO)};
|
|
|
16 if (GetMonitorInfo(monitor,&mi)) {
|
|
|
17 const CRect clip = mi.rcWork;
|
|
|
18 if (rc.right > clip.right) rc.OffsetRect(clip.right - rc.right, 0);
|
|
|
19 if (rc.bottom > clip.bottom) rc.OffsetRect(0, clip.bottom - rc.bottom);
|
|
|
20 if (rc.left < clip.left) rc.OffsetRect(clip.left - rc.left, 0);
|
|
|
21 if (rc.top < clip.top) rc.OffsetRect(0, clip.top - rc.top);
|
|
|
22 }
|
|
|
23 }
|
|
|
24
|
|
|
25 static BOOL GetClientRectAsSC(CWindow wnd, CRect & rc) {
|
|
|
26 CRect temp;
|
|
|
27 if (!wnd.GetClientRect(temp)) return FALSE;
|
|
|
28 if (temp.IsRectNull()) return FALSE;
|
|
|
29 if (!wnd.ClientToScreen(temp)) return FALSE;
|
|
|
30 rc = temp;
|
|
|
31 return TRUE;
|
|
|
32 }
|
|
|
33
|
|
|
34
|
|
|
35 static BOOL CenterWindowGetRect(CWindow wnd, CWindow wndParent, CRect & out) {
|
|
|
36 CRect parent, child;
|
|
|
37 if (!wndParent.GetWindowRect(&parent) || !wnd.GetWindowRect(&child)) return FALSE;
|
|
|
38 {
|
|
|
39 CPoint origin = parent.CenterPoint();
|
|
|
40 origin.Offset( - child.Width() / 2, - child.Height() / 2);
|
|
|
41 child.OffsetRect( origin - child.TopLeft() );
|
|
|
42 }
|
|
|
43 AdjustRectToScreenArea(child, parent);
|
|
|
44 out = child;
|
|
|
45 return TRUE;
|
|
|
46 }
|
|
|
47
|
|
|
48 static BOOL CenterWindowAbove(CWindow wnd, CWindow wndParent) {
|
|
|
49 CRect rc;
|
|
|
50 if (!CenterWindowGetRect(wnd, wndParent, rc)) return FALSE;
|
|
|
51 return wnd.SetWindowPos(NULL,rc,SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
52 }
|
|
|
53
|
|
|
54 static BOOL ShowWindowCentered(CWindow wnd,CWindow wndParent) {
|
|
|
55 CRect rc;
|
|
|
56 if (!CenterWindowGetRect(wnd, wndParent, rc)) return FALSE;
|
|
|
57 return wnd.SetWindowPos(HWND_TOP,rc,SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
|
58 }
|
|
|
59
|
|
|
60 typedef cfg_struct_t<SIZE> cfgWindowSize;
|
|
|
61
|
|
|
62 class cfgWindowSizeTracker {
|
|
|
63 public:
|
|
|
64 cfgWindowSizeTracker(cfgWindowSize & p_var) : m_var(p_var) {}
|
|
|
65
|
|
|
66 bool Apply(HWND p_wnd) {
|
|
|
67 bool retVal = false;
|
|
|
68 m_applied = false;
|
|
|
69 auto s = m_var.get();
|
|
|
70 if (s.cx > 0 && s.cy > 0) {
|
|
|
71 CRect rect(0,0,s.cx,s.cy);
|
|
|
72 if (AdjustWindowRectHelper(p_wnd, rect)) {
|
|
|
73 SetWindowPos(p_wnd,NULL,0,0,rect.right-rect.left,rect.bottom-rect.top,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
|
|
|
74 retVal = true;
|
|
|
75 }
|
|
|
76 }
|
|
|
77 m_applied = true;
|
|
|
78 return retVal;
|
|
|
79 }
|
|
|
80
|
|
|
81 BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
|
|
|
82 if (uMsg == WM_SIZE && m_applied) {
|
|
|
83 if (lParam != 0) {
|
|
|
84 m_var.set({ (short)LOWORD(lParam), (short)HIWORD(lParam) });
|
|
|
85 }
|
|
|
86 }
|
|
|
87 return FALSE;
|
|
|
88 }
|
|
|
89 private:
|
|
|
90 cfgWindowSize & m_var;
|
|
|
91 bool m_applied = false;
|
|
|
92 };
|
|
|
93
|
|
|
94 class cfgDialogSizeTracker : public cfgWindowSizeTracker {
|
|
|
95 public:
|
|
|
96 cfgDialogSizeTracker(cfgWindowSize & p_var) : cfgWindowSizeTracker(p_var) {}
|
|
|
97 BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
|
|
|
98 if (cfgWindowSizeTracker::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult)) return TRUE;
|
|
|
99 if (uMsg == WM_INITDIALOG) Apply(hWnd);
|
|
|
100 return FALSE;
|
|
|
101 }
|
|
|
102 };
|
|
|
103
|
|
|
104 struct cfgDialogPositionData {
|
|
|
105 static constexpr int32_t
|
|
|
106 posInvalid = 0x80000000;
|
|
|
107 static constexpr uint32_t
|
|
|
108 sizeInvalid = 0xFFFFFFFF,
|
|
|
109 dpiInvalid = 0;
|
|
|
110
|
|
|
111 uint32_t m_width = sizeInvalid, m_height = sizeInvalid;
|
|
|
112 int32_t m_posX = posInvalid, m_posY = posInvalid;
|
|
|
113 uint32_t m_dpiX = dpiInvalid, m_dpiY = dpiInvalid;
|
|
|
114
|
|
|
115 cfgDialogPositionData reDPI(CSize) const;
|
|
|
116 bool grabFrom(CWindow wnd);
|
|
|
117 bool applyTo(CWindow wnd) const;
|
|
|
118
|
|
|
119 bool overrideDefaultSize(t_uint32 width, t_uint32 height);
|
|
|
120
|
|
|
121 pfc::string8 debug() const;
|
|
|
122 };
|
|
|
123
|
|
|
124 struct cfgWindowPositionData {
|
|
|
125 WINDOWPLACEMENT m_wp = {};
|
|
|
126 SIZE m_dpi = {};
|
|
|
127
|
|
|
128 bool grabFrom(CWindow wnd);
|
|
|
129 bool applyTo(CWindow wnd, bool allowHidden = false) const;
|
|
|
130 };
|
|
|
131
|
|
|
132 FB2K_STREAM_READER_OVERLOAD(cfgDialogPositionData) {
|
|
|
133 stream >> value.m_width >> value.m_height;
|
|
|
134 try {
|
|
|
135 stream >> value.m_posX >> value.m_posY >> value.m_dpiX >> value.m_dpiY;
|
|
|
136 } catch (exception_io_data const &) {
|
|
|
137 value.m_posX = value.m_posY = cfgDialogPositionData::posInvalid;
|
|
|
138 value.m_dpiX = value.m_dpiY = cfgDialogPositionData::dpiInvalid;
|
|
|
139 }
|
|
|
140 return stream;
|
|
|
141 }
|
|
|
142 FB2K_STREAM_WRITER_OVERLOAD(cfgDialogPositionData) {
|
|
|
143 return stream << value.m_width << value.m_height << value.m_posX << value.m_posY << value.m_dpiX << value.m_dpiY;
|
|
|
144 }
|
|
|
145
|
|
|
146 class cfgDialogPosition : public cfg_struct_t<cfgDialogPositionData> {
|
|
|
147 public:
|
|
|
148 cfgDialogPosition(const GUID& id) : cfg_struct_t(id) {}
|
|
|
149
|
|
|
150 //! Read and save size data from HWND.
|
|
|
151 void read_from_window(HWND);
|
|
|
152 //! Apply saved size data to HWND.
|
|
|
153 bool apply_to_window(HWND);
|
|
|
154
|
|
|
155 void AddWindow(HWND wnd) { apply_to_window(wnd); }
|
|
|
156 void RemoveWindow(HWND wnd) { read_from_window(wnd); }
|
|
|
157 };
|
|
|
158
|
|
|
159 class cfgWindowPosition : public cfg_struct_t<cfgWindowPositionData> {
|
|
|
160 public:
|
|
|
161 cfgWindowPosition(const GUID & id) : cfg_struct_t(id) {}
|
|
|
162
|
|
|
163 //! Read and save size data from HWND.
|
|
|
164 void read_from_window(HWND);
|
|
|
165 //! Apply saved size data to HWND.
|
|
|
166 bool apply_to_window(HWND, bool allowHidden = false);
|
|
|
167 //! New window created, show it with saved metrics.
|
|
|
168 void windowCreated(HWND, bool allowHidden = false, DWORD showHow = SW_SHOW);
|
|
|
169 };
|
|
|
170
|
|
|
171 class cfgDialogPositionTracker {
|
|
|
172 public:
|
|
|
173 cfgDialogPositionTracker(cfgDialogPosition & p_var) : m_var(p_var) {}
|
|
|
174 ~cfgDialogPositionTracker() {Cleanup();}
|
|
|
175
|
|
|
176 BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
|
|
|
177 if (uMsg == WM_CREATE || uMsg == WM_INITDIALOG) {
|
|
|
178 Cleanup();
|
|
|
179 m_wnd = hWnd;
|
|
|
180 m_var.AddWindow(m_wnd);
|
|
|
181 } else if (uMsg == WM_DESTROY) {
|
|
|
182 PFC_ASSERT( hWnd == m_wnd );
|
|
|
183 Cleanup();
|
|
|
184 }
|
|
|
185 return FALSE;
|
|
|
186 }
|
|
|
187
|
|
|
188 private:
|
|
|
189 void Cleanup() {
|
|
|
190 if (m_wnd != NULL) {
|
|
|
191 m_var.RemoveWindow(m_wnd);
|
|
|
192 m_wnd = NULL;
|
|
|
193 }
|
|
|
194 }
|
|
|
195 cfgDialogPosition & m_var;
|
|
|
196 CWindow m_wnd;
|
|
|
197 };
|
|
|
198
|
|
|
199 //! DPI-safe window size var \n
|
|
|
200 //! Stores size in pixel and original DPI\n
|
|
|
201 //! Use with cfgWindowSizeTracker2
|
|
|
202 struct cfgWindowSize2_data {
|
|
|
203 CSize m_size = CSize(0, 0), m_dpi = CSize(0, 0);
|
|
|
204 };
|
|
|
205
|
|
|
206 class cfgWindowSize2 : public cfg_struct_t< cfgWindowSize2_data > {
|
|
|
207 public:
|
|
|
208 cfgWindowSize2(const GUID & p_guid) : cfg_struct_t(p_guid) {}
|
|
|
209
|
|
|
210 bool is_valid() {
|
|
|
211 auto v = cfg_struct_t::get();
|
|
|
212 return v.m_size.cx > 0 && v.m_size.cy > 0;
|
|
|
213 }
|
|
|
214
|
|
|
215 CSize get( CSize forDPI ) {
|
|
|
216 auto v = cfg_struct_t::get();
|
|
|
217 if ( forDPI == v.m_dpi ) return v.m_size;
|
|
|
218
|
|
|
219 CSize ret;
|
|
|
220 ret.cx = MulDiv( v.m_size.cx, forDPI.cx, v.m_dpi.cx );
|
|
|
221 ret.cy = MulDiv( v.m_size.cy, forDPI.cy, v.m_dpi.cy );
|
|
|
222 return ret;
|
|
|
223 }
|
|
|
224 };
|
|
|
225
|
|
|
226 //! Forward messages to this class to utilize cfgWindowSize2
|
|
|
227 class cfgWindowSizeTracker2 : public CMessageMap {
|
|
|
228 public:
|
|
|
229 cfgWindowSizeTracker2( cfgWindowSize2 & var ) : m_var(var) {}
|
|
|
230
|
|
|
231 BEGIN_MSG_MAP_EX(cfgWindowSizeTracker2)
|
|
|
232 if (uMsg == WM_CREATE || uMsg == WM_INITDIALOG) {
|
|
|
233 Apply(hWnd);
|
|
|
234 }
|
|
|
235 MSG_WM_SIZE( OnSize )
|
|
|
236 END_MSG_MAP()
|
|
|
237
|
|
|
238 bool Apply(HWND p_wnd) {
|
|
|
239 bool retVal = false;
|
|
|
240 m_applied = false;
|
|
|
241 if (m_var.is_valid()) {
|
|
|
242 CRect rect( CPoint(0,0), m_var.get( m_DPI ) );
|
|
|
243 if (AdjustWindowRectHelper(p_wnd, rect)) {
|
|
|
244 SetWindowPos(p_wnd,NULL,0,0,rect.right-rect.left,rect.bottom-rect.top,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
|
|
|
245 retVal = true;
|
|
|
246 }
|
|
|
247 }
|
|
|
248 m_applied = true;
|
|
|
249 return retVal;
|
|
|
250 }
|
|
|
251
|
|
|
252 private:
|
|
|
253 void OnSize(UINT nType, CSize size) {
|
|
|
254 if ( m_applied && size.cx > 0 && size.cy > 0 ) {
|
|
|
255 m_var.set( { size, m_DPI } );
|
|
|
256 }
|
|
|
257 SetMsgHandled(FALSE);
|
|
|
258 }
|
|
|
259 cfgWindowSize2 & m_var;
|
|
|
260 bool m_applied = false;
|
|
|
261 const CSize m_DPI = QueryScreenDPIEx();
|
|
|
262 };
|
|
|
263
|
|
|
264 #endif // _WIN32
|