4
4
#include " Win32Utils.h"
5
5
#include " ThemeHelper.h"
6
6
#include " XamlApp.h"
7
+ #include < ShellScalingApi.h>
8
+
9
+ #pragma comment(lib, "Shcore.lib")
7
10
8
11
namespace Magpie {
9
12
10
- bool MainWindow::Create (HINSTANCE hInstance, const RECT& windowRect , bool isMaximized) noexcept {
13
+ bool MainWindow::Create (HINSTANCE hInstance, winrt:: Point windowCenter, winrt:: Size windowSizeInDips , bool isMaximized) noexcept {
11
14
static const int _ = [](HINSTANCE hInstance) {
12
15
WNDCLASSEXW wcex{};
13
16
wcex.cbSize = sizeof (wcex);
@@ -27,7 +30,7 @@ bool MainWindow::Create(HINSTANCE hInstance, const RECT& windowRect, bool isMaxi
27
30
return 0 ;
28
31
}(hInstance);
29
32
30
- _CreateWindow (hInstance, windowRect );
33
+ const auto & [posToSet, sizeToSet] = _CreateWindow (hInstance, windowCenter, windowSizeInDips );
31
34
32
35
if (!_hWnd) {
33
36
return false ;
@@ -46,8 +49,10 @@ bool MainWindow::Create(HINSTANCE hInstance, const RECT& windowRect, bool isMaxi
46
49
47
50
// 1. 设置初始 XAML Islands 窗口的尺寸
48
51
// 2. 刷新窗口边框
49
- // 3. 防止窗口显示时背景闪烁: https://stackoverflow.com/questions/69715610/how-to-initialize-the-background-color-of-win32-app-to-something-other-than-whit
50
- SetWindowPos (_hWnd, NULL , 0 , 0 , 0 , 0 , SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
52
+ // 3. 无法获知 DPI 的情况下 _CreateWindow 创建的窗口尺寸为零,在这里延后设置窗口位置
53
+ // 4. 防止窗口显示时背景闪烁: https://stackoverflow.com/questions/69715610/how-to-initialize-the-background-color-of-win32-app-to-something-other-than-whit
54
+ SetWindowPos (_hWnd, NULL , posToSet.x , posToSet.y , sizeToSet.cx , sizeToSet.cy ,
55
+ (sizeToSet.cx == 0 ? (SWP_NOMOVE | SWP_NOSIZE) : 0 ) | SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS);
51
56
52
57
// Xaml 控件加载完成后显示主窗口
53
58
_content.Loaded ([this , isMaximized](winrt::IInspectable const &, winrt::RoutedEventArgs const &) {
@@ -155,8 +160,8 @@ LRESULT MainWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noex
155
160
// 设置窗口最小尺寸
156
161
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
157
162
mmi->ptMinTrackSize = {
158
- std::lround (550 * _currentDpi / double (USER_DEFAULT_SCREEN_DPI)),
159
- std::lround (300 * _currentDpi / double (USER_DEFAULT_SCREEN_DPI))
163
+ std::lroundf (550 * _currentDpi / float (USER_DEFAULT_SCREEN_DPI)),
164
+ std::lroundf (300 * _currentDpi / float (USER_DEFAULT_SCREEN_DPI))
160
165
};
161
166
return 0 ;
162
167
}
@@ -217,31 +222,113 @@ LRESULT MainWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noex
217
222
return base_type::_MessageHandler (msg, wParam, lParam);
218
223
}
219
224
220
- void MainWindow::_CreateWindow (HINSTANCE hInstance, const RECT& windowRect) noexcept {
221
- // 防止窗口启动时不在可见区域,Windows 不会自动处理。
222
- // 检查两个点的位置是否存在屏幕:窗口的中心点和上边框中心点。前者确保大部分窗口内容可见,后者确保大部分标题栏可见。
223
- const POINT windowCenter{
224
- (windowRect.left + windowRect.right ) / 2 ,
225
- (windowRect.top + windowRect.bottom ) / 2
226
- };
227
- const bool isValidPosition = MonitorFromPoint (windowCenter, MONITOR_DEFAULTTONULL)
228
- && MonitorFromPoint ({ windowCenter.x , windowRect.top }, MONITOR_DEFAULTTONULL);
225
+ std::pair<POINT, SIZE> MainWindow::_CreateWindow (HINSTANCE hInstance, winrt::Point windowCenter, winrt::Size windowSizeInDips) noexcept {
226
+ POINT windowPos = { CW_USEDEFAULT,CW_USEDEFAULT };
227
+ SIZE windowSize{};
228
+
229
+ // windowSizeInDips 小于零表示默认位置和尺寸
230
+ if (windowSizeInDips.Width > 0 ) {
231
+ // 检查窗口中心点的 DPI,根据我的测试,创建窗口时 Windows 使用窗口中心点确定 DPI。
232
+ // 如果窗口中心点不在任何屏幕上,则在默认位置启动,让调用者设置窗口尺寸。
233
+ const HMONITOR hMon = MonitorFromPoint (
234
+ { std::lroundf (windowCenter.X ),std::lroundf (windowCenter.Y ) },
235
+ MONITOR_DEFAULTTONULL
236
+ );
237
+ if (hMon) {
238
+ UINT dpi = USER_DEFAULT_SCREEN_DPI;
239
+ GetDpiForMonitor (hMon, MDT_EFFECTIVE_DPI, &dpi, &dpi);
240
+
241
+ const float dpiFactor = dpi / float (USER_DEFAULT_SCREEN_DPI);
242
+ const winrt::Size windowSizeInPixels = {
243
+ windowSizeInDips.Width * dpiFactor,
244
+ windowSizeInDips.Height * dpiFactor
245
+ };
246
+
247
+ windowSize.cx = std::lroundf (windowSizeInPixels.Width );
248
+ windowSize.cy = std::lroundf (windowSizeInPixels.Height );
249
+
250
+ MONITORINFO mi{ sizeof (mi) };
251
+ GetMonitorInfo (hMon, &mi);
252
+
253
+ // 确保启动位置在屏幕工作区内。不允许启动时跨越多个屏幕。
254
+ if (windowSize.cx <= mi.rcWork .right - mi.rcWork .left && windowSize.cy <= mi.rcWork .bottom - mi.rcWork .top ) {
255
+ windowPos.x = std::lroundf (windowCenter.X - windowSizeInPixels.Width / 2 );
256
+ windowPos.x = std::clamp (windowPos.x , mi.rcWork .left , mi.rcWork .right - windowSize.cx );
257
+
258
+ windowPos.y = std::lroundf (windowCenter.Y - windowSizeInPixels.Height / 2 );
259
+ windowPos.y = std::clamp (windowPos.y , mi.rcWork .top , mi.rcWork .bottom - windowSize.cy );
260
+ } else {
261
+ // 屏幕工作区无法容纳窗口则使用默认窗口尺寸
262
+ windowSize = {};
263
+ windowSizeInDips.Width = -1 .0f ;
264
+ }
265
+ }
266
+ }
229
267
230
268
// Win11 22H2 中为了使用 Mica 背景需指定 WS_EX_NOREDIRECTIONBITMAP
269
+ // windowSize 可能为零,并返回窗口尺寸给调用者
231
270
CreateWindowEx (
232
271
Win32Utils::GetOSVersion ().Is22H2OrNewer () ? WS_EX_NOREDIRECTIONBITMAP : 0 ,
233
272
CommonSharedConstants::MAIN_WINDOW_CLASS_NAME,
234
273
L" Magpie" ,
235
274
WS_OVERLAPPEDWINDOW,
236
- isValidPosition ? windowRect. left : CW_USEDEFAULT ,
237
- isValidPosition ? windowRect. top : CW_USEDEFAULT ,
238
- windowRect. right - windowRect. left ,
239
- windowRect. bottom - windowRect. top ,
275
+ windowPos. x ,
276
+ windowPos. y ,
277
+ windowSize. cx ,
278
+ windowSize. cy ,
240
279
NULL ,
241
280
NULL ,
242
281
hInstance,
243
282
this
244
283
);
284
+
285
+ if (windowSize.cx == 0 ) {
286
+ const HMONITOR hMon = MonitorFromWindow (_hWnd, MONITOR_DEFAULTTONEAREST);
287
+
288
+ MONITORINFO mi{ sizeof (mi) };
289
+ GetMonitorInfo (hMon, &mi);
290
+
291
+ const float dpiFactor = _currentDpi / float (USER_DEFAULT_SCREEN_DPI);
292
+ const winrt::Size workingAreaSizeInDips = {
293
+ (mi.rcWork .right - mi.rcWork .left ) / dpiFactor,
294
+ (mi.rcWork .bottom - mi.rcWork .top ) / dpiFactor
295
+ };
296
+
297
+ // 确保启动尺寸小于屏幕工作区
298
+ if (windowSizeInDips.Width <= 0 ||
299
+ windowSizeInDips.Width > workingAreaSizeInDips.Width ||
300
+ windowSizeInDips.Height > workingAreaSizeInDips.Height ) {
301
+ // 默认尺寸
302
+ static constexpr winrt::Size DEFAULT_SIZE{ 980 .0f , 690 .0f };
303
+
304
+ windowSizeInDips = DEFAULT_SIZE;
305
+
306
+ if (windowSizeInDips.Width > workingAreaSizeInDips.Width ||
307
+ windowSizeInDips.Height > workingAreaSizeInDips.Height ) {
308
+ // 屏幕太小无法容纳默认尺寸
309
+ windowSizeInDips.Width = workingAreaSizeInDips.Width * 0 .8f ;
310
+ windowSizeInDips.Height = windowSizeInDips.Width * DEFAULT_SIZE.Height / DEFAULT_SIZE.Width ;
311
+
312
+ if (windowSizeInDips.Height > workingAreaSizeInDips.Height ) {
313
+ windowSizeInDips.Height = workingAreaSizeInDips.Height * 0 .8f ;
314
+ windowSizeInDips.Width = windowSizeInDips.Height * DEFAULT_SIZE.Width / DEFAULT_SIZE.Height ;
315
+ }
316
+ }
317
+ }
318
+
319
+ windowSize.cx = std::lroundf (windowSizeInDips.Width * dpiFactor);
320
+ windowSize.cy = std::lroundf (windowSizeInDips.Height * dpiFactor);
321
+
322
+ // 确保启动位置在屏幕工作区内
323
+ RECT targetRect;
324
+ GetWindowRect (_hWnd, &targetRect);
325
+ windowPos.x = std::clamp (targetRect.left , mi.rcWork .left , mi.rcWork .right - windowSize.cx );
326
+ windowPos.y = std::clamp (targetRect.top , mi.rcWork .top , mi.rcWork .bottom - windowSize.cy );
327
+
328
+ return std::make_pair (windowPos, windowSize);
329
+ } else {
330
+ return {};
331
+ }
245
332
}
246
333
247
334
void MainWindow::_UpdateTheme () {
0 commit comments