|
1
|
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 } |