Mercurial > foo_out_sdl
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 |
