Mercurial > foo_out_sdl
comparison foosdk/sdk/foobar2000/helpers/WindowPositionUtils.cpp @ 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 #include "StdAfx.h" | |
| 2 #include "WindowPositionUtils.h" | |
| 3 | |
| 4 #define FB2K_WPU_DEBUG 0 | |
| 5 namespace { | |
| 6 static BOOL GetParentWndRect(CWindow wndParent, CRect& rc) { | |
| 7 if (!wndParent.IsIconic()) { | |
| 8 return wndParent.GetWindowRect(rc); | |
| 9 } | |
| 10 WINDOWPLACEMENT pl = { sizeof(pl) }; | |
| 11 if (!wndParent.GetWindowPlacement(&pl)) return FALSE; | |
| 12 rc = pl.rcNormalPosition; | |
| 13 return TRUE; | |
| 14 } | |
| 15 | |
| 16 struct DeOverlapState { | |
| 17 CWindow m_thisWnd; | |
| 18 CPoint m_topLeft; | |
| 19 bool m_match; | |
| 20 }; | |
| 21 static BOOL CALLBACK MyEnumChildProc(HWND wnd, LPARAM param) { | |
| 22 DeOverlapState* state = reinterpret_cast<DeOverlapState*>(param); | |
| 23 if (wnd != state->m_thisWnd && IsWindowVisible(wnd)) { | |
| 24 CRect rc; | |
| 25 if (GetWindowRect(wnd, rc)) { | |
| 26 if (rc.TopLeft() == state->m_topLeft) { | |
| 27 state->m_match = true; return FALSE; | |
| 28 } | |
| 29 } | |
| 30 } | |
| 31 return TRUE; | |
| 32 } | |
| 33 static bool DeOverlapTest(CWindow wnd, CPoint topLeft) { | |
| 34 DeOverlapState state = {}; | |
| 35 state.m_thisWnd = wnd; state.m_topLeft = topLeft; state.m_match = false; | |
| 36 EnumThreadWindows(GetCurrentThreadId(), MyEnumChildProc, reinterpret_cast<LPARAM>(&state)); | |
| 37 return state.m_match; | |
| 38 } | |
| 39 static int DeOverlapDelta() { | |
| 40 return pfc::max_t<int>(GetSystemMetrics(SM_CYCAPTION), 1); | |
| 41 } | |
| 42 static void DeOverlap(CWindow wnd, CRect& rc) { | |
| 43 const int delta = DeOverlapDelta(); | |
| 44 for (;;) { | |
| 45 if (!DeOverlapTest(wnd, rc.TopLeft())) break; | |
| 46 rc.OffsetRect(delta, delta); | |
| 47 } | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 bool cfgDialogPositionData::grabFrom(CWindow wnd) { | |
| 52 CRect rc; | |
| 53 if (!GetClientRectAsSC(wnd, rc)) { | |
| 54 return false; | |
| 55 } | |
| 56 const CSize DPI = QueryScreenDPIEx(wnd); | |
| 57 m_dpiX = DPI.cx; m_dpiY = DPI.cy; | |
| 58 m_width = rc.Width(); m_height = rc.Height(); | |
| 59 m_posX = m_posY = posInvalid; | |
| 60 CWindow parent = wnd.GetParent(); | |
| 61 if (parent != NULL) { | |
| 62 CRect rcParent; | |
| 63 if (GetParentWndRect(parent, rcParent)) { | |
| 64 m_posX = rc.left - rcParent.left; | |
| 65 m_posY = rc.top - rcParent.top; | |
| 66 } | |
| 67 } else { | |
| 68 m_posX = rc.left; m_posY = rc.top; | |
| 69 } | |
| 70 return true; | |
| 71 } | |
| 72 | |
| 73 pfc::string8 cfgDialogPositionData::debug() const { | |
| 74 pfc::string_formatter ret; | |
| 75 if (m_width != sizeInvalid) ret << "W: " << m_width << "\n"; | |
| 76 if (m_height != sizeInvalid) ret << "H: " << m_height << "\n"; | |
| 77 if (m_posX != posInvalid) ret << "X: " << m_posX << "\n"; | |
| 78 if (m_posY != posInvalid) ret << "Y: " << m_posY << "\n"; | |
| 79 if (m_dpiX != dpiInvalid) ret << "DPI-X: " << m_dpiX << "\n"; | |
| 80 if (m_dpiY != dpiInvalid) ret << "DPI-Y: " << m_dpiY << "\n"; | |
| 81 return ret; | |
| 82 } | |
| 83 | |
| 84 cfgDialogPositionData cfgDialogPositionData::reDPI( CSize screenDPI ) const { | |
| 85 cfgDialogPositionData v = *this; | |
| 86 if (screenDPI.cx == 0 || screenDPI.cy == 0) { | |
| 87 PFC_ASSERT(!"Should not get here - something seriously wrong with the OS"); | |
| 88 return v; | |
| 89 } | |
| 90 if (v.m_dpiX != dpiInvalid && v.m_dpiX != screenDPI.cx) { | |
| 91 if (v.m_width != sizeInvalid) v.m_width = MulDiv(v.m_width, screenDPI.cx, v.m_dpiX); | |
| 92 if (v.m_posX != posInvalid) v.m_posX = MulDiv(v.m_posX, screenDPI.cx, v.m_dpiX); | |
| 93 } | |
| 94 if (v.m_dpiY != dpiInvalid && v.m_dpiY != screenDPI.cy) { | |
| 95 if (v.m_height != sizeInvalid) v.m_height = MulDiv(v.m_height, screenDPI.cy, v.m_dpiY); | |
| 96 if (v.m_posY != posInvalid) v.m_posY = MulDiv(v.m_posY, screenDPI.cy, v.m_dpiY); | |
| 97 } | |
| 98 v.m_dpiX = screenDPI.cx; | |
| 99 v.m_dpiY = screenDPI.cy; | |
| 100 return v; | |
| 101 } | |
| 102 | |
| 103 | |
| 104 bool cfgDialogPositionData::overrideDefaultSize(t_uint32 width, t_uint32 height) { | |
| 105 bool rv = false; | |
| 106 if (m_width == sizeInvalid && m_height == sizeInvalid) { | |
| 107 m_width = width; m_height = height; m_posX = m_posY = posInvalid; | |
| 108 m_dpiX = m_dpiY = 96; | |
| 109 rv = true; | |
| 110 } | |
| 111 return rv; | |
| 112 } | |
| 113 | |
| 114 bool cfgDialogPositionData::applyTo(CWindow wnd) const { | |
| 115 #if FB2K_WPU_DEBUG | |
| 116 FB2K_console_formatter() << "cfgDialogPositionData::applyTo(0x" << pfc::format_window( wnd ) << ")"; | |
| 117 FB2K_console_formatter() << "data:\n" << this->debug(); | |
| 118 #endif | |
| 119 const auto v = reDPI(QueryScreenDPIEx(wnd)); | |
| 120 #if FB2K_WPU_DEBUG | |
| 121 FB2K_console_formatter() << "after reDPI:\n" << v.debug(); | |
| 122 #endif | |
| 123 CWindow wndParent = wnd.GetParent(); | |
| 124 UINT flags = SWP_NOACTIVATE | SWP_NOZORDER; | |
| 125 CRect rc; | |
| 126 if (!GetClientRectAsSC(wnd, rc)) return false; | |
| 127 if (v.m_width != v.sizeInvalid && v.m_height != v.sizeInvalid && (wnd.GetWindowLong(GWL_STYLE) & WS_SIZEBOX) != 0) { | |
| 128 rc.right = rc.left + v.m_width; | |
| 129 rc.bottom = rc.top + v.m_height; | |
| 130 } else { | |
| 131 flags |= SWP_NOSIZE; | |
| 132 } | |
| 133 if (wndParent != NULL) { | |
| 134 CRect rcParent; | |
| 135 if (GetParentWndRect(wndParent, rcParent)) { | |
| 136 if (v.m_posX != v.posInvalid && v.m_posY != v.posInvalid) { | |
| 137 rc.MoveToXY(rcParent.TopLeft() + CPoint(v.m_posX, v.m_posY)); | |
| 138 } else { | |
| 139 CPoint center = rcParent.CenterPoint(); | |
| 140 rc.MoveToXY(center.x - rc.Width() / 2, center.y - rc.Height() / 2); | |
| 141 } | |
| 142 } | |
| 143 } else { | |
| 144 if (v.m_posX != v.posInvalid && v.m_posY != v.posInvalid ) { | |
| 145 rc.MoveToXY( v.m_posX, v.m_posY ); | |
| 146 } | |
| 147 } | |
| 148 if (!AdjustWindowRectHelper(wnd, rc)) return FALSE; | |
| 149 | |
| 150 DeOverlap(wnd, rc); | |
| 151 | |
| 152 { | |
| 153 CRect rcAdjust(0, 0, 1, 1); | |
| 154 if (wndParent != NULL) { | |
| 155 CRect temp; | |
| 156 if (wndParent.GetWindowRect(temp)) rcAdjust = temp; | |
| 157 } | |
| 158 AdjustRectToScreenArea(rc, rcAdjust); | |
| 159 } | |
| 160 | |
| 161 | |
| 162 return wnd.SetWindowPos(NULL, rc, flags); | |
| 163 } | |
| 164 | |
| 165 void cfgDialogPosition::read_from_window(HWND wnd) { | |
| 166 cfgDialogPositionData data; | |
| 167 if (data.grabFrom(wnd)) this->set(data); | |
| 168 } | |
| 169 | |
| 170 bool cfgDialogPosition::apply_to_window(HWND wnd) { | |
| 171 auto data = this->get(); | |
| 172 return data.applyTo(wnd); | |
| 173 } | |
| 174 | |
| 175 bool cfgWindowPositionData::grabFrom(CWindow wnd) { | |
| 176 WINDOWPLACEMENT wp = {sizeof(wp)}; | |
| 177 bool rv = !! wnd.GetWindowPlacement(&wp); | |
| 178 if (rv) { | |
| 179 if ( !wnd.IsWindowVisible() ) wp.showCmd = SW_HIDE; | |
| 180 this->m_wp = wp; | |
| 181 m_dpi = QueryScreenDPIEx(wnd); | |
| 182 PFC_ASSERT( m_dpi.cx > 0 && m_dpi.cy > 0 ); | |
| 183 } | |
| 184 return rv; | |
| 185 } | |
| 186 | |
| 187 static void reDPI(WINDOWPLACEMENT& wp, SIZE from, SIZE to) { | |
| 188 wp.rcNormalPosition.left = MulDiv(wp.rcNormalPosition.left, to.cx, from.cx); | |
| 189 wp.rcNormalPosition.right = MulDiv(wp.rcNormalPosition.right, to.cx, from.cx); | |
| 190 wp.rcNormalPosition.top = MulDiv(wp.rcNormalPosition.top, to.cy, from.cy); | |
| 191 wp.rcNormalPosition.bottom = MulDiv(wp.rcNormalPosition.bottom, to.cy, from.cy); | |
| 192 } | |
| 193 | |
| 194 bool applyWindowPlacement(HWND window, WINDOWPLACEMENT const& data, bool allowHidden); // window_placement_helper.cpp | |
| 195 | |
| 196 bool cfgWindowPositionData::applyTo(CWindow wnd, bool allowHidden) const { | |
| 197 WINDOWPLACEMENT wp = m_wp; | |
| 198 if ( wp.length == 0 ) return false; | |
| 199 auto dpi = QueryScreenDPIEx(wnd); | |
| 200 if (dpi.cx != m_dpi.cx || dpi.cy != m_dpi.cy) { | |
| 201 reDPI( wp, m_dpi, dpi ); | |
| 202 } | |
| 203 return applyWindowPlacement(wnd, wp, allowHidden); | |
| 204 } | |
| 205 | |
| 206 void cfgWindowPosition::read_from_window(HWND wnd) { | |
| 207 // grabFrom might work partially, fail to obtain size due to window being hidden, use last values | |
| 208 cfgWindowPositionData data = get(); | |
| 209 if ( data.grabFrom( wnd ) ) set(data); | |
| 210 } | |
| 211 | |
| 212 bool cfgWindowPosition::apply_to_window(HWND wnd, bool allowHidden) { | |
| 213 auto data = get(); | |
| 214 return data.applyTo( wnd, allowHidden ); | |
| 215 } | |
| 216 | |
| 217 void cfgWindowPosition::windowCreated(HWND wnd, bool allowHidden, DWORD showHow) { | |
| 218 auto data = get(); | |
| 219 switch (showHow) { | |
| 220 case SW_HIDE: | |
| 221 case SW_MINIMIZE: | |
| 222 data.m_wp.showCmd = showHow; | |
| 223 break; | |
| 224 } | |
| 225 if (!data.applyTo(wnd, allowHidden)) { | |
| 226 ::ShowWindow( wnd, showHow); | |
| 227 } | |
| 228 } |
