comparison foosdk/sdk/foobar2000/helpers/WindowPositionUtils.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
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