Merge branch 'dev' into render-system

This commit is contained in:
Xu 2023-11-26 20:30:25 +08:00
commit c8f4e842ee
10 changed files with 209 additions and 81 deletions

View file

@ -307,7 +307,7 @@ if majorVersion != None:
with open("version.json", "w", encoding="utf-8") as f:
json.dump(
{
"version": f"{majorVersion}.{minorVersion},{patchVersion}",
"version": f"{majorVersion}.{minorVersion}.{patchVersion}",
"tag": tag,
"binary": {
"x64": {

View file

@ -82,13 +82,8 @@ StartUpOptions App::Initialize(int) {
}
result.IsError = false;
const RECT& windowRect = settings.WindowRect();
result.MainWndRect = {
(float)windowRect.left,
(float)windowRect.top,
(float)windowRect.right,
(float)windowRect.bottom
};
result.MainWindowCenter = settings.MainWindowCenter();
result.MainWindowSizeInDips = settings.MainWindowSizeInDips();
result.IsWndMaximized= settings.IsWindowMaximized();
result.IsNeedElevated = settings.IsAlwaysRunAsAdmin();

View file

@ -43,7 +43,8 @@ namespace Magpie.App {
};
struct StartUpOptions {
Windows.Foundation.Rect MainWndRect;
Windows.Foundation.Point MainWindowCenter;
Windows.Foundation.Size MainWindowSizeInDips;
Boolean IsError;
Boolean IsWndMaximized;
Boolean IsNeedElevated;

View file

@ -13,6 +13,9 @@
#include "JsonHelper.h"
#include "ScalingMode.h"
#include "LocalizationService.h"
#include <ShellScalingApi.h>
#pragma comment(lib, "Shcore.lib")
using namespace ::Magpie::Core;
@ -450,13 +453,18 @@ void AppSettings::_UpdateWindowPlacement() noexcept {
return;
}
_windowRect = {
wp.rcNormalPosition.left,
wp.rcNormalPosition.top,
wp.rcNormalPosition.right - wp.rcNormalPosition.left,
wp.rcNormalPosition.bottom - wp.rcNormalPosition.top
_mainWindowCenter = {
(wp.rcNormalPosition.left + wp.rcNormalPosition.right) / 2.0f,
(wp.rcNormalPosition.top + wp.rcNormalPosition.bottom) / 2.0f
};
_isWindowMaximized = wp.showCmd == SW_MAXIMIZE;
const float dpiFactor = GetDpiForWindow(hwndMain) / float(USER_DEFAULT_SCREEN_DPI);
_mainWindowSizeInDips = {
(wp.rcNormalPosition.right - wp.rcNormalPosition.left) / dpiFactor,
(wp.rcNormalPosition.bottom - wp.rcNormalPosition.top) / dpiFactor,
};
_isMainWindowMaximized = wp.showCmd == SW_MAXIMIZE;
}
bool AppSettings::_Save(const _AppSettingsData& data) noexcept {
@ -485,16 +493,16 @@ bool AppSettings::_Save(const _AppSettingsData& data) noexcept {
writer.Key("windowPos");
writer.StartObject();
writer.Key("x");
writer.Int(data._windowRect.left);
writer.Key("y");
writer.Int(data._windowRect.top);
writer.Key("centerX");
writer.Double(data._mainWindowCenter.X);
writer.Key("centerY");
writer.Double(data._mainWindowCenter.Y);
writer.Key("width");
writer.Uint((uint32_t)data._windowRect.right);
writer.Double(data._mainWindowSizeInDips.Width);
writer.Key("height");
writer.Uint((uint32_t)data._windowRect.bottom);
writer.Double(data._mainWindowSizeInDips.Height);
writer.Key("maximized");
writer.Bool(data._isWindowMaximized);
writer.Bool(data._isMainWindowMaximized);
writer.EndObject();
writer.Key("shortcuts");
@ -592,25 +600,49 @@ void AppSettings::_LoadSettings(const rapidjson::GenericObject<true, rapidjson::
auto windowPosNode = root.FindMember("windowPos");
if (windowPosNode != root.MemberEnd() && windowPosNode->value.IsObject()) {
const auto& windowRectObj = windowPosNode->value.GetObj();
const auto& windowPosObj = windowPosNode->value.GetObj();
int x = 0;
int y = 0;
if (JsonHelper::ReadInt(windowRectObj, "x", x, true)
&& JsonHelper::ReadInt(windowRectObj, "y", y, true)) {
_windowRect.left = x;
_windowRect.top = y;
Point center{};
Size size{};
if (JsonHelper::ReadFloat(windowPosObj, "centerX", center.X, true) &&
JsonHelper::ReadFloat(windowPosObj, "centerY", center.Y, true) &&
JsonHelper::ReadFloat(windowPosObj, "width", size.Width, true) &&
JsonHelper::ReadFloat(windowPosObj, "height", size.Height, true)) {
_mainWindowCenter = center;
_mainWindowSizeInDips = size;
} else {
// 尽最大努力和旧版本兼容
int x = 0;
int y = 0;
uint32_t width = 0;
uint32_t height = 0;
if (JsonHelper::ReadInt(windowPosObj, "x", x, true) &&
JsonHelper::ReadInt(windowPosObj, "y", y, true) &&
JsonHelper::ReadUInt(windowPosObj, "width", width, true) &&
JsonHelper::ReadUInt(windowPosObj, "height", height, true)) {
_mainWindowCenter = {
x + width / 2.0f,
y + height / 2.0f
};
// 如果窗口位置不存在屏幕则使用主屏幕的缩放,猜错的后果仅是窗口尺寸错误,
// 无论如何原始缩放信息已经丢失。
const HMONITOR hMon = MonitorFromPoint(
{ std::lroundf(_mainWindowCenter.X), std::lroundf(_mainWindowCenter.Y) },
MONITOR_DEFAULTTOPRIMARY
);
UINT dpi = USER_DEFAULT_SCREEN_DPI;
GetDpiForMonitor(hMon, MDT_EFFECTIVE_DPI, &dpi, &dpi);
const float dpiFactor = dpi / float(USER_DEFAULT_SCREEN_DPI);
_mainWindowSizeInDips = {
width / dpiFactor,
height / dpiFactor
};
}
}
uint32_t width = 0;
uint32_t height = 0;
if (JsonHelper::ReadUInt(windowRectObj, "width", width, true)
&& JsonHelper::ReadUInt(windowRectObj, "height", height, true)) {
_windowRect.right = (LONG)width;
_windowRect.bottom = (LONG)height;
}
JsonHelper::ReadBool(windowRectObj, "maximized", _isWindowMaximized);
JsonHelper::ReadBool(windowPosObj, "maximized", _isMainWindowMaximized);
}
auto shortcutsNode = root.FindMember("shortcuts");
@ -704,7 +736,7 @@ bool AppSettings::_LoadProfile(
const rapidjson::GenericObject<true, rapidjson::Value>& profileObj,
Profile& profile,
bool isDefault
) {
) const {
if (!isDefault) {
if (!JsonHelper::ReadString(profileObj, "name", profile.name, true)) {
return false;

View file

@ -35,8 +35,10 @@ struct _AppSettingsData {
// -1 表示使用系统设置
int _language = -1;
// X, Y, 长, 高
RECT _windowRect{ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT };
// 保存窗口中心点和 DPI 无关的窗口尺寸
Point _mainWindowCenter{};
// 小于零表示默认位置和尺寸
Size _mainWindowSizeInDips{ -1.0f,-1.0f };
Theme _theme = Theme::System;
// 必须在 1~5 之间
@ -58,7 +60,7 @@ struct _AppSettingsData {
bool _isInlineParams = false;
bool _isShowTrayIcon = true;
bool _isAutoRestore = false;
bool _isWindowMaximized = false;
bool _isMainWindowMaximized = false;
bool _isAutoCheckForUpdates = true;
bool _isCheckForPreviewUpdates = false;
};
@ -114,12 +116,16 @@ public:
_themeChangedEvent.remove(token);
}
const RECT& WindowRect() const noexcept {
return _windowRect;
Point MainWindowCenter() const noexcept {
return _mainWindowCenter;
}
Size MainWindowSizeInDips() const noexcept {
return _mainWindowSizeInDips;
}
bool IsWindowMaximized() const noexcept {
return _isWindowMaximized;
return _isMainWindowMaximized;
}
const Shortcut& GetShortcut(ShortcutAction action) const {
@ -358,7 +364,7 @@ private:
const rapidjson::GenericObject<true, rapidjson::Value>& profileObj,
Profile& profile,
bool isDefault = false
);
) const;
bool _SetDefaultShortcuts();
void _SetDefaultScalingModes();

View file

@ -4,10 +4,13 @@
#include "Win32Utils.h"
#include "ThemeHelper.h"
#include "XamlApp.h"
#include <ShellScalingApi.h>
#pragma comment(lib, "Shcore.lib")
namespace Magpie {
bool MainWindow::Create(HINSTANCE hInstance, const RECT& windowRect, bool isMaximized) noexcept {
bool MainWindow::Create(HINSTANCE hInstance, winrt::Point windowCenter, winrt::Size windowSizeInDips, bool isMaximized) noexcept {
static const int _ = [](HINSTANCE hInstance) {
WNDCLASSEXW wcex{};
wcex.cbSize = sizeof(wcex);
@ -27,7 +30,7 @@ bool MainWindow::Create(HINSTANCE hInstance, const RECT& windowRect, bool isMaxi
return 0;
}(hInstance);
_CreateWindow(hInstance, windowRect);
const auto& [posToSet, sizeToSet] = _CreateWindow(hInstance, windowCenter, windowSizeInDips);
if (!_hWnd) {
return false;
@ -46,8 +49,10 @@ bool MainWindow::Create(HINSTANCE hInstance, const RECT& windowRect, bool isMaxi
// 1. 设置初始 XAML Islands 窗口的尺寸
// 2. 刷新窗口边框
// 3. 防止窗口显示时背景闪烁: https://stackoverflow.com/questions/69715610/how-to-initialize-the-background-color-of-win32-app-to-something-other-than-whit
SetWindowPos(_hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
// 3. 无法获知 DPI 的情况下 _CreateWindow 创建的窗口尺寸为零,在这里延后设置窗口位置
// 4. 防止窗口显示时背景闪烁: https://stackoverflow.com/questions/69715610/how-to-initialize-the-background-color-of-win32-app-to-something-other-than-whit
SetWindowPos(_hWnd, NULL, posToSet.x, posToSet.y, sizeToSet.cx, sizeToSet.cy,
(sizeToSet.cx == 0 ? (SWP_NOMOVE | SWP_NOSIZE) : 0) | SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS);
// Xaml 控件加载完成后显示主窗口
_content.Loaded([this, isMaximized](winrt::IInspectable const&, winrt::RoutedEventArgs const&) {
@ -155,8 +160,8 @@ LRESULT MainWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noex
// 设置窗口最小尺寸
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
mmi->ptMinTrackSize = {
std::lround(550 * _currentDpi / double(USER_DEFAULT_SCREEN_DPI)),
std::lround(300 * _currentDpi / double(USER_DEFAULT_SCREEN_DPI))
std::lroundf(550 * _currentDpi / float(USER_DEFAULT_SCREEN_DPI)),
std::lroundf(300 * _currentDpi / float(USER_DEFAULT_SCREEN_DPI))
};
return 0;
}
@ -217,31 +222,113 @@ LRESULT MainWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noex
return base_type::_MessageHandler(msg, wParam, lParam);
}
void MainWindow::_CreateWindow(HINSTANCE hInstance, const RECT& windowRect) noexcept {
// 防止窗口启动时不在可见区域Windows 不会自动处理。
// 检查两个点的位置是否存在屏幕:窗口的中心点和上边框中心点。前者确保大部分窗口内容可见,后者确保大部分标题栏可见。
const POINT windowCenter{
(windowRect.left + windowRect.right) / 2,
(windowRect.top + windowRect.bottom) / 2
};
const bool isValidPosition = MonitorFromPoint(windowCenter, MONITOR_DEFAULTTONULL)
&& MonitorFromPoint({ windowCenter.x, windowRect.top }, MONITOR_DEFAULTTONULL);
std::pair<POINT, SIZE> MainWindow::_CreateWindow(HINSTANCE hInstance, winrt::Point windowCenter, winrt::Size windowSizeInDips) noexcept {
POINT windowPos = { CW_USEDEFAULT,CW_USEDEFAULT };
SIZE windowSize{};
// windowSizeInDips 小于零表示默认位置和尺寸
if (windowSizeInDips.Width > 0) {
// 检查窗口中心点的 DPI根据我的测试创建窗口时 Windows 使用窗口中心点确定 DPI。
// 如果窗口中心点不在任何屏幕上,则在默认位置启动,让调用者设置窗口尺寸。
const HMONITOR hMon = MonitorFromPoint(
{ std::lroundf(windowCenter.X),std::lroundf(windowCenter.Y) },
MONITOR_DEFAULTTONULL
);
if (hMon) {
UINT dpi = USER_DEFAULT_SCREEN_DPI;
GetDpiForMonitor(hMon, MDT_EFFECTIVE_DPI, &dpi, &dpi);
const float dpiFactor = dpi / float(USER_DEFAULT_SCREEN_DPI);
const winrt::Size windowSizeInPixels = {
windowSizeInDips.Width * dpiFactor,
windowSizeInDips.Height * dpiFactor
};
windowSize.cx = std::lroundf(windowSizeInPixels.Width);
windowSize.cy = std::lroundf(windowSizeInPixels.Height);
MONITORINFO mi{ sizeof(mi) };
GetMonitorInfo(hMon, &mi);
// 确保启动位置在屏幕工作区内。不允许启动时跨越多个屏幕。
if (windowSize.cx <= mi.rcWork.right - mi.rcWork.left && windowSize.cy <= mi.rcWork.bottom - mi.rcWork.top) {
windowPos.x = std::lroundf(windowCenter.X - windowSizeInPixels.Width / 2);
windowPos.x = std::clamp(windowPos.x, mi.rcWork.left, mi.rcWork.right - windowSize.cx);
windowPos.y = std::lroundf(windowCenter.Y - windowSizeInPixels.Height / 2);
windowPos.y = std::clamp(windowPos.y, mi.rcWork.top, mi.rcWork.bottom - windowSize.cy);
} else {
// 屏幕工作区无法容纳窗口则使用默认窗口尺寸
windowSize = {};
windowSizeInDips.Width = -1.0f;
}
}
}
// Win11 22H2 中为了使用 Mica 背景需指定 WS_EX_NOREDIRECTIONBITMAP
// windowSize 可能为零,并返回窗口尺寸给调用者
CreateWindowEx(
Win32Utils::GetOSVersion().Is22H2OrNewer() ? WS_EX_NOREDIRECTIONBITMAP : 0,
CommonSharedConstants::MAIN_WINDOW_CLASS_NAME,
L"Magpie",
WS_OVERLAPPEDWINDOW,
isValidPosition ? windowRect.left : CW_USEDEFAULT,
isValidPosition ? windowRect.top : CW_USEDEFAULT,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
windowPos.x,
windowPos.y,
windowSize.cx,
windowSize.cy,
NULL,
NULL,
hInstance,
this
);
if (windowSize.cx == 0) {
const HMONITOR hMon = MonitorFromWindow(_hWnd, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi{ sizeof(mi) };
GetMonitorInfo(hMon, &mi);
const float dpiFactor = _currentDpi / float(USER_DEFAULT_SCREEN_DPI);
const winrt::Size workingAreaSizeInDips = {
(mi.rcWork.right - mi.rcWork.left) / dpiFactor,
(mi.rcWork.bottom - mi.rcWork.top) / dpiFactor
};
// 确保启动尺寸小于屏幕工作区
if (windowSizeInDips.Width <= 0 ||
windowSizeInDips.Width > workingAreaSizeInDips.Width ||
windowSizeInDips.Height > workingAreaSizeInDips.Height) {
// 默认尺寸
static constexpr winrt::Size DEFAULT_SIZE{ 980.0f, 690.0f };
windowSizeInDips = DEFAULT_SIZE;
if (windowSizeInDips.Width > workingAreaSizeInDips.Width ||
windowSizeInDips.Height > workingAreaSizeInDips.Height) {
// 屏幕太小无法容纳默认尺寸
windowSizeInDips.Width = workingAreaSizeInDips.Width * 0.8f;
windowSizeInDips.Height = windowSizeInDips.Width * DEFAULT_SIZE.Height / DEFAULT_SIZE.Width;
if (windowSizeInDips.Height > workingAreaSizeInDips.Height) {
windowSizeInDips.Height = workingAreaSizeInDips.Height * 0.8f;
windowSizeInDips.Width = windowSizeInDips.Height * DEFAULT_SIZE.Width / DEFAULT_SIZE.Height;
}
}
}
windowSize.cx = std::lroundf(windowSizeInDips.Width * dpiFactor);
windowSize.cy = std::lroundf(windowSizeInDips.Height * dpiFactor);
// 确保启动位置在屏幕工作区内
RECT targetRect;
GetWindowRect(_hWnd, &targetRect);
windowPos.x = std::clamp(targetRect.left, mi.rcWork.left, mi.rcWork.right - windowSize.cx);
windowPos.y = std::clamp(targetRect.top, mi.rcWork.top, mi.rcWork.bottom - windowSize.cy);
return std::make_pair(windowPos, windowSize);
} else {
return {};
}
}
void MainWindow::_UpdateTheme() {

View file

@ -7,7 +7,7 @@ namespace Magpie {
class MainWindow : public XamlWindowT<MainWindow, winrt::Magpie::App::RootPage> {
friend class base_type;
public:
bool Create(HINSTANCE hInstance, const RECT& windowRect, bool isMaximized) noexcept;
bool Create(HINSTANCE hInstance, winrt::Point windowCenter, winrt::Size windowSizeInDips, bool isMaximized) noexcept;
void Show() const noexcept;
@ -15,7 +15,7 @@ protected:
LRESULT _MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noexcept;
private:
void _CreateWindow(HINSTANCE hInstance, const RECT& windowRect) noexcept;
std::pair<POINT, SIZE> _CreateWindow(HINSTANCE hInstance, winrt::Point windowCenter, winrt::Size windowSizeInDips) noexcept;
void _UpdateTheme();

View file

@ -54,12 +54,8 @@ bool XamlApp::Initialize(HINSTANCE hInstance, const wchar_t* arguments) {
return false;
}
_mainWndRect = {
(int)std::lroundf(options.MainWndRect.X),
(int)std::lroundf(options.MainWndRect.Y),
(int)std::lroundf(options.MainWndRect.X + options.MainWndRect.Width),
(int)std::lroundf(options.MainWndRect.Y + options.MainWndRect.Height)
};
_mainWindowCenter = options.MainWindowCenter;
_mainWindowSizeInDips = options.MainWindowSizeInDips;
_isMainWndMaximized = options.IsWndMaximized;
ThemeHelper::Initialize();
@ -137,7 +133,17 @@ void XamlApp::SaveSettings() {
WINDOWPLACEMENT wp{};
wp.length = sizeof(wp);
if (GetWindowPlacement(_mainWindow.Handle(), &wp)) {
_mainWndRect = wp.rcNormalPosition;
_mainWindowCenter = {
(wp.rcNormalPosition.left + wp.rcNormalPosition.right) / 2.0f,
(wp.rcNormalPosition.top + wp.rcNormalPosition.bottom) / 2.0f
};
const float dpiFactor = GetDpiForWindow(_mainWindow.Handle()) / float(USER_DEFAULT_SCREEN_DPI);
_mainWindowSizeInDips = {
(wp.rcNormalPosition.right - wp.rcNormalPosition.left) / dpiFactor,
(wp.rcNormalPosition.bottom - wp.rcNormalPosition.top) / dpiFactor,
};
_isMainWndMaximized = wp.showCmd == SW_MAXIMIZE;
} else {
Logger::Get().Win32Error("GetWindowPlacement 失败");
@ -209,7 +215,7 @@ void XamlApp::_InitializeLogger() {
}
bool XamlApp::_CreateMainWindow() {
if (!_mainWindow.Create(_hInst, _mainWndRect, _isMainWndMaximized)) {
if (!_mainWindow.Create(_hInst, _mainWindowCenter, _mainWindowSizeInDips, _isMainWndMaximized)) {
return false;
}

View file

@ -51,7 +51,8 @@ private:
winrt::Magpie::App::App _uwpApp{ nullptr };
MainWindow _mainWindow;
RECT _mainWndRect{};
winrt::Point _mainWindowCenter{};
winrt::Size _mainWindowSizeInDips{};
bool _isMainWndMaximized = false;
};

View file

@ -1,10 +1,10 @@
{
"version": "0.10.3",
"tag": "v0.10.3",
"version": "0.10.4",
"tag": "v0.10.4",
"binary": {
"x64": {
"url": "https://github.com/Blinue/Magpie/releases/download/v0.10.3/Magpie_v0.10.3.zip",
"hash": "1768d18e72c45a2165d47b4a43a64f8a"
"url": "https://github.com/Blinue/Magpie/releases/download/v0.10.4/Magpie-v0.10.4-x64.zip",
"hash": "18ecf71bc4842902e4f8f0edcd8a5ccf"
}
}
}