Skip to content

Commit

Permalink
feat: toast 配置主题变化
Browse files Browse the repository at this point in the history
  • Loading branch information
Blinue committed Oct 25, 2024
1 parent 8bcbfd7 commit 85c86f2
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 38 deletions.
3 changes: 3 additions & 0 deletions src/Magpie.App/Resources.language-en-US.resw
Original file line number Diff line number Diff line change
Expand Up @@ -838,4 +838,7 @@
<data name="Home_Advanced_SimulateExclusiveFullscreen_InfoBar.Title" xml:space="preserve">
<value>This option is not compatible with some older games. Please use it with caution.</value>
</data>
<data name="Message_InvalidScalingMode" xml:space="preserve">
<value>Scaling mode is invalid.</value>
</data>
</root>
3 changes: 3 additions & 0 deletions src/Magpie.App/Resources.language-zh-Hans.resw
Original file line number Diff line number Diff line change
Expand Up @@ -838,4 +838,7 @@
<data name="Home_Advanced_SimulateExclusiveFullscreen_InfoBar.Title" xml:space="preserve">
<value>此选项和一些旧游戏不兼容,请谨慎使用。</value>
</data>
<data name="Message_InvalidScalingMode" xml:space="preserve">
<value>缩放模式无效。</value>
</data>
</root>
41 changes: 21 additions & 20 deletions src/Magpie.App/RootPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include "XamlUtils.h"
#include "Logger.h"
#include "Win32Utils.h"
#include "AppSettings.h"
#include "ProfileService.h"
#include "AppXReader.h"
#include "IconHelper.h"
Expand All @@ -31,9 +30,9 @@ namespace winrt::Magpie::App::implementation {
static constexpr uint32_t FIRST_PROFILE_ITEM_IDX = 4;

RootPage::RootPage() {
_themeChangedRevoker = AppSettings::Get().ThemeChanged(auto_revoke, [this](Theme) { _UpdateTheme(); });
_colorValuesChangedRevoker = _uiSettings.ColorValuesChanged(
auto_revoke, { this, &RootPage::_UISettings_ColorValuesChanged });
_themeChangedRevoker = AppSettings::Get().ThemeChanged(
auto_revoke, { this, &RootPage::_AppSettings_ThemeChanged });
_UpdateColorValuesChangedRevoker();

_displayInformation = DisplayInformation::GetForCurrentView();
_dpiChangedRevoker = _displayInformation.DpiChanged(
Expand Down Expand Up @@ -214,18 +213,13 @@ static Color Win32ColorToWinRTColor(COLORREF color) {
return { 255, GetRValue(color), GetGValue(color), GetBValue(color) };
}

// 来自 https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes#know-when-dark-mode-is-enabled
static bool IsColorLight(const Color& clr) {
return 5 * clr.G + 2 * clr.R + clr.B > 8 * 128;
}

void RootPage::_UpdateTheme(bool updateIcons) {
Theme theme = AppSettings::Get().Theme();

bool isDarkTheme = FALSE;
if (theme == Theme::System) {
// 前景色是亮色表示当前是深色主题
isDarkTheme = IsColorLight(_uiSettings.GetColorValue(UIColorType::Foreground));
isDarkTheme = XamlUtils::IsColorLight(_uiSettings.GetColorValue(UIColorType::Foreground));
} else {
isDarkTheme = theme == Theme::Dark;
}
Expand Down Expand Up @@ -313,19 +307,26 @@ fire_and_forget RootPage::_LoadIcon(MUXC::NavigationViewItem const& item, const
}
}

fire_and_forget RootPage::_UISettings_ColorValuesChanged(Windows::UI::ViewManagement::UISettings const&, IInspectable const&) {
auto weakThis = get_weak();
co_await Dispatcher();

if (!weakThis.get()) {
co_return;
}

void RootPage::_UpdateColorValuesChangedRevoker() {
if (AppSettings::Get().Theme() == Theme::System) {
_UpdateTheme(false);
_colorValuesChangedRevoker = _uiSettings.ColorValuesChanged(
auto_revoke, { this, &RootPage::_UISettings_ColorValuesChanged });
} else {
_colorValuesChangedRevoker.revoke();
}
}

void RootPage::_UISettings_ColorValuesChanged(Windows::UI::ViewManagement::UISettings const&, IInspectable const&) {
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis(get_weak())]() {
if (auto strongThis = weakThis.get()) {
strongThis->_UpdateTheme(true);
}
});
}

_UpdateIcons(true);
void RootPage::_AppSettings_ThemeChanged(Theme) {
_UpdateColorValuesChangedRevoker();
_UpdateTheme(true);
}

void RootPage::_UpdateIcons(bool skipDesktop) {
Expand Down
9 changes: 7 additions & 2 deletions src/Magpie.App/RootPage.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include "RootPage.g.h"
#include "WinRTUtils.h"
#include "AppSettings.h"

namespace winrt::Magpie::App {
struct Profile;
Expand Down Expand Up @@ -39,11 +40,15 @@ struct RootPage : RootPageT<RootPage> {
void NavigateToAboutPage();

private:
void _UpdateTheme(bool updateIcons = true);
void _UpdateTheme(bool updateIcons);

fire_and_forget _LoadIcon(MUXC::NavigationViewItem const& item, const Profile& profile);

fire_and_forget _UISettings_ColorValuesChanged(Windows::UI::ViewManagement::UISettings const&, IInspectable const&);
void _UpdateColorValuesChangedRevoker();

void _UISettings_ColorValuesChanged(Windows::UI::ViewManagement::UISettings const&, IInspectable const&);

void _AppSettings_ThemeChanged(Magpie::App::Theme);

void _UpdateIcons(bool skipDesktop);

Expand Down
20 changes: 17 additions & 3 deletions src/Magpie.App/ScalingService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <Magpie.Core.h>
#include "TouchHelper.h"
#include "ToastService.h"
#include "CommonSharedConstants.h"

using namespace ::Magpie::Core;
using namespace winrt;
Expand Down Expand Up @@ -225,9 +226,20 @@ fire_and_forget ScalingService::_ScalingRuntime_IsRunningChanged(bool isRunning)
}

static void ShowError(HWND hWnd, ScalingError error) noexcept {
static int id = 0;
++id;
ToastService::Get().ShowMessageOnWindow(L"缩放模式无效" + std::to_wstring(id), hWnd);
const wchar_t* key = nullptr;

switch (error) {
case ScalingError::InvalidScalingMode:
key = L"Message_InvalidScalingMode";
break;
case ScalingError::AlreadyScaling:
break;
default:
return;
}

ResourceLoader resourceLoader = ResourceLoader::GetForCurrentView(CommonSharedConstants::APP_RESOURCE_MAP_ID);
ToastService::Get().ShowMessageOnWindow(resourceLoader.GetString(key), hWnd);
Logger::Get().Error(fmt::format("缩放失败\n\t错误码: {}", (int)error));
}

Expand All @@ -240,11 +252,13 @@ void ScalingService::_StartScale(HWND hWnd, const Profile& profile) {
ScalingOptions options;
options.effects = ScalingModesService::Get().GetScalingMode(profile.scalingMode).effects;
if (options.effects.empty()) {
ShowError(hWnd, ScalingError::InvalidScalingMode);
return;
} else {
for (EffectOption& effect : options.effects) {
if (!EffectsService::Get().GetEffect(effect.name)) {
// 存在无法解析的效果
ShowError(hWnd, ScalingError::InvalidScalingMode);
return;
}
}
Expand Down
85 changes: 73 additions & 12 deletions src/Magpie.App/ToastPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
#endif
#include "Win32Utils.h"
#include "IconHelper.h"
#include "LocalizationService.h"
#include "XamlUtils.h"

using namespace winrt;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Media::Imaging;

Expand All @@ -27,6 +30,15 @@ ToastPage::ToastPage(uint64_t hwndToast) : _hwndToast((HWND)hwndToast) {
that->_logo = std::move(bitmap);
that->RaisePropertyChanged(L"Logo");
}(this);

_themeChangedRevoker = AppSettings::Get().ThemeChanged(
auto_revoke, { this, &ToastPage::_AppSettings_ThemeChanged });
_UpdateColorValuesChangedRevoker();
}

void ToastPage::InitializeComponent() {
ToastPageT::InitializeComponent();
_UpdateTheme();
}

static void UpdateToastPosition(HWND hwndToast, const RECT& frameRect, bool updateZOrder) noexcept {
Expand Down Expand Up @@ -78,7 +90,6 @@ fire_and_forget ToastPage::ShowMessageOnWindow(hstring message, uint64_t hwndTar
oldTeachingTip = std::move(_oldTeachingTip);
}

// 备份要使用的变量,后面避免使用 this
CoreDispatcher dispatcher = Dispatcher();
auto weakThis = get_weak();

Expand Down Expand Up @@ -117,6 +128,8 @@ fire_and_forget ToastPage::ShowMessageOnWindow(hstring message, uint64_t hwndTar

// 创建新的 TeachingTip
MUXC::TeachingTip curTeachingTip = FindName(L"MessageTeachingTip").as<MUXC::TeachingTip>();
// 帮助 XAML 选择合适的字体,直接设置 TeachingTip 的 Language 属性没有作用
MessageTeachingTipContent().Language(LocalizationService::Get().Language());
MessageTextBlock().Text(message);

// !!! HACK !!!
Expand Down Expand Up @@ -156,36 +169,35 @@ fire_and_forget ToastPage::ShowMessageOnWindow(hstring message, uint64_t hwndTar
}
}(dispatcher, curTeachingTip, oldTeachingTip);

// 定期更新弹窗位置,这里应避免使用 this
// 定期更新弹窗位置
RECT prevframeRect{};
const HWND hwndToast = _hwndToast;
do {
co_await resume_background();
// 等待一帧的时间可以使弹窗的移动更平滑
DwmFlush();
co_await dispatcher;

if (!IsWindow((HWND)hwndTarget) || !IsWindow(hwndToast)) {
if (!weakThis.get()) {
co_return;
}

if (!IsWindow((HWND)hwndTarget) || !IsWindow(_hwndToast) || !Win32Utils::GetWindowFrameRect((HWND)hwndTarget, frameRect)) {
// 附加的窗口已经关闭,toast 也应关闭,_oldTeachingTip 用于延长生命周期避免崩溃
UnloadObject(curTeachingTip);
_oldTeachingTip = std::move(curTeachingTip);
break;
}

if (!Win32Utils::GetWindowFrameRect((HWND)hwndTarget, frameRect)) {
break;
co_return;
}

if (!isOwned && GetForegroundWindow() == (HWND)hwndTarget) {
// 如果 hwndTarget 位于前台,定期将弹窗置顶
SetWindowPos(hwndToast, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
SetWindowPos(hwndToast, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
SetWindowPos(_hwndToast, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
SetWindowPos(_hwndToast, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
}

// 窗口没有移动则无需更新
if (frameRect != prevframeRect) {
prevframeRect = frameRect;
UpdateToastPosition(hwndToast, frameRect, false);
UpdateToastPosition(_hwndToast, frameRect, false);
}
} while (curTeachingTip.IsLoaded() && curTeachingTip.IsOpen());
}
Expand All @@ -194,6 +206,55 @@ void ToastPage::ShowMessageInApp(hstring message) {
ShowMessageOnWindow(message, Application::Current().as<App>().HwndMain(), true);
}

void ToastPage::_AppSettings_ThemeChanged(Magpie::App::Theme theme) {
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis(get_weak())]() {
if (auto strongThis = weakThis.get()) {
strongThis->_UpdateColorValuesChangedRevoker();
strongThis->_UpdateTheme();
}
});
}

void ToastPage::_UISettings_ColorValuesChanged(Windows::UI::ViewManagement::UISettings const&, IInspectable const&) {
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis(get_weak())]() {
if (auto strongThis = weakThis.get()) {
strongThis->_UpdateTheme();
}
});
}

void ToastPage::_UpdateColorValuesChangedRevoker() {
if (AppSettings::Get().Theme() == Theme::System) {
_colorValuesChangedRevoker = _uiSettings.ColorValuesChanged(
auto_revoke, { this, &ToastPage::_UISettings_ColorValuesChanged });
} else {
_colorValuesChangedRevoker.revoke();
}
}

void ToastPage::_UpdateTheme() {
Theme theme = AppSettings::Get().Theme();

bool isDarkTheme = FALSE;
if (theme == Theme::System) {
// 前景色是亮色表示当前是深色主题
isDarkTheme = XamlUtils::IsColorLight(_uiSettings.GetColorValue(UIColorType::Foreground));
} else {
isDarkTheme = theme == Theme::Dark;
}

if (IsLoaded() && (ActualTheme() == ElementTheme::Dark) == isDarkTheme) {
// 无需切换
return;
}

ElementTheme newTheme = isDarkTheme ? ElementTheme::Dark : ElementTheme::Light;
RequestedTheme(newTheme);

XamlUtils::UpdateThemeOfXamlPopups(XamlRoot(), newTheme);
XamlUtils::UpdateThemeOfTooltips(*this, newTheme);
}

void ToastPage::_IsLogoShown(bool value) {
if (_isLogoShown == value) {
return;
Expand Down
17 changes: 17 additions & 0 deletions src/Magpie.App/ToastPage.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
#pragma once
#include "ToastPage.g.h"
#include "WinRTUtils.h"
#include "AppSettings.h"

namespace winrt::Magpie::App::implementation {

struct ToastPage : ToastPageT<ToastPage>,
wil::notify_property_changed_base<ToastPage> {
ToastPage(uint64_t hwndToast);

void InitializeComponent();

Imaging::SoftwareBitmapSource Logo() const noexcept {
return _logo;
}
Expand All @@ -20,8 +24,21 @@ struct ToastPage : ToastPageT<ToastPage>,
void ShowMessageInApp(hstring message);

private:
void _AppSettings_ThemeChanged(Magpie::App::Theme theme);

void _UISettings_ColorValuesChanged(Windows::UI::ViewManagement::UISettings const&, IInspectable const&);

void _UpdateColorValuesChangedRevoker();

void _UpdateTheme();

void _IsLogoShown(bool value);

WinRTUtils::EventRevoker _themeChangedRevoker;

Windows::UI::ViewManagement::UISettings _uiSettings;
Windows::UI::ViewManagement::UISettings::ColorValuesChanged_revoker _colorValuesChangedRevoker;

Imaging::SoftwareBitmapSource _logo{ nullptr };
HWND _hwndToast;
MUXC::TeachingTip _oldTeachingTip{ nullptr };
Expand Down
3 changes: 2 additions & 1 deletion src/Magpie.App/ToastPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
x:Load="False"
PreferredPlacement="Center"
ShouldConstrainToRootBounds="False">
<local:SimpleStackPanel Orientation="Horizontal">
<local:SimpleStackPanel x:Name="MessageTeachingTipContent"
Orientation="Horizontal">
<Image Width="16"
Height="16"
Margin="0,0,8,0"
Expand Down
2 changes: 2 additions & 0 deletions src/Shared/ScalingError.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

enum class ScalingError {
NoError,

/////////////////////////////////////
//
// 先决条件错误
Expand Down
5 changes: 5 additions & 0 deletions src/Shared/XamlUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ struct XamlUtils {
const winrt::Windows::UI::Xaml::DependencyObject& root,
winrt::Windows::UI::Xaml::ElementTheme theme
);

static bool IsColorLight(const winrt::Windows::UI::Color& clr) noexcept {
// 来自 https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes#know-when-dark-mode-is-enabled
return 5 * clr.G + 2 * clr.R + clr.B > 8 * 128;
}
};

0 comments on commit 85c86f2

Please sign in to comment.