Skip to content

Commit

Permalink
feat: 应用内消息改为使用 ToastService 实现
Browse files Browse the repository at this point in the history
  • Loading branch information
Blinue committed Oct 22, 2024
1 parent b8206aa commit 06c7c54
Show file tree
Hide file tree
Showing 12 changed files with 46 additions and 94 deletions.
3 changes: 2 additions & 1 deletion src/Magpie.App/AboutPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#endif
#include "Win32Utils.h"
#include "CommonSharedConstants.h"
#include "ToastService.h"

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

Expand All @@ -15,7 +16,7 @@ void AboutPage::VersionTextBlock_DoubleTapped(IInspectable const&, Input::Double

const hstring message = ResourceLoader::GetForCurrentView(CommonSharedConstants::APP_RESOURCE_MAP_ID)
.GetString(L"About_DeveloperModeEnabled");
Application::Current().as<App>().RootPage().ShowToast(message);
ToastService::Get().ShowMessageInApp(message);
}
}

Expand Down
23 changes: 1 addition & 22 deletions src/Magpie.App/IconHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,28 +260,7 @@ SoftwareBitmap IconHelper::ExtractIconFromExe(const wchar_t* fileName, uint32_t

SoftwareBitmap IconHelper::ExtractAppSmallIcon() {
// 小图标在多处使用,应该缓存
static SoftwareBitmap result{ nullptr };

if (!result) {
constexpr int SMALL_ICON_SIZE = 40;

// LoadImage 比 SHDefExtractIcon 快两倍左右
wil::unique_hicon hIcon((HICON)LoadImage(
GetModuleHandle(nullptr),
MAKEINTRESOURCE(CommonSharedConstants::IDI_APP),
IMAGE_ICON,
SMALL_ICON_SIZE,
SMALL_ICON_SIZE,
LR_DEFAULTCOLOR
));
if (!hIcon) {
Logger::Get().Win32Error("提取程序图标失败");
return nullptr;
}

result = HIcon2SoftwareBitmap(hIcon.get());
}

static SoftwareBitmap result = ExtractAppIcon(40);
return result;
}

Expand Down
54 changes: 0 additions & 54 deletions src/Magpie.App/RootPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

#include "XamlUtils.h"
#include "Logger.h"
#include "StrUtils.h"
#include "Win32Utils.h"
#include "AppSettings.h"
#include "ProfileService.h"
Expand Down Expand Up @@ -211,59 +210,6 @@ void RootPage::NavigateToAboutPage() {
nv.SelectedItem(nv.FooterMenuItems().GetAt(0));
}

fire_and_forget RootPage::ShowToast(const hstring& message) {
// !!! HACK !!!
// 重用 TeachingTip 有一个 bug: 前一个 Toast 正在消失时新的 Toast 不会显示。为了
// 规避它,我们每次都创建新的 TeachingTip,但要保留旧对象的引用,因为播放动画时销毁
// 会导致崩溃。oldToastTeachingTip 的生存期可确保动画播放完毕。
MUXC::TeachingTip oldToastTeachingTip = ToastTeachingTip();
if (oldToastTeachingTip) {
UnloadObject(oldToastTeachingTip);
}

weak_ref<MUXC::TeachingTip> weakTeachingTip;
{
// 创建新的 TeachingTip
MUXC::TeachingTip newTeachingTip = FindName(L"ToastTeachingTip").as<MUXC::TeachingTip>();
ToastTextBlock().Text(message);

// !!! HACK !!!
// 移除关闭按钮。必须在模板加载完成后做,TeachingTip 没有 Opening 事件,但可以监听 MessageTextBlock 的
// LayoutUpdated 事件,它在 TeachingTip 显示前必然会被引发。
ToastTextBlock().LayoutUpdated([weak(weak_ref(newTeachingTip))](IInspectable const&, IInspectable const&) {
auto toastTeachingTip = weak.get();
if (!toastTeachingTip) {
return;
}

IControlProtected protectedAccessor = toastTeachingTip.as<IControlProtected>();

// 隐藏关闭按钮
if (DependencyObject closeButton = protectedAccessor.GetTemplateChild(L"AlternateCloseButton")) {
closeButton.as<FrameworkElement>().Visibility(Visibility::Collapsed);
}
});

newTeachingTip.IsOpen(true);

weakTeachingTip = newTeachingTip;
}

auto weakThis = get_weak();
CoreDispatcher dispatcher = Dispatcher();
// 显示时长固定 2 秒
co_await 2s;
co_await dispatcher;

if (weakThis.get()) {
MUXC::TeachingTip curTeachingTip = ToastTeachingTip();
if (curTeachingTip == weakTeachingTip.get()) {
// 如果已经显示新的 Toast 则无需关闭,因为 newTeachingTip 已被卸载(但仍在生存期内)
curTeachingTip.IsOpen(false);
}
}
}

static Color Win32ColorToWinRTColor(COLORREF color) {
return { 255, GetRValue(color), GetGValue(color), GetBValue(color) };
}
Expand Down
2 changes: 0 additions & 2 deletions src/Magpie.App/RootPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ struct RootPage : RootPageT<RootPage> {

void NavigateToAboutPage();

fire_and_forget ShowToast(const hstring& message);

private:
void _UpdateTheme(bool updateIcons = true);

Expand Down
2 changes: 0 additions & 2 deletions src/Magpie.App/RootPage.idl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,5 @@ namespace Magpie.App {
NewProfileViewModel NewProfileViewModel { get; };

void NavigateToAboutPage();

void ShowToast(String message);
}
}
6 changes: 0 additions & 6 deletions src/Magpie.App/RootPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@
Loaded="Loaded"
mc:Ignorable="d">
<Grid>
<muxc:TeachingTip x:Name="ToastTeachingTip"
x:Load="False">
<!-- Title 属性有主题错误 -->
<TextBlock x:Name="ToastTextBlock" />
</muxc:TeachingTip>

<local:TitleBarControl x:Name="TitleBar"
Canvas.ZIndex="1" />

Expand Down
18 changes: 17 additions & 1 deletion src/Magpie.App/ToastPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ static void UpdateToastPosition(HWND hwndToast, const RECT& frameRect) noexcept
);
}

fire_and_forget ToastPage::ShowMessageOnWindow(hstring message, uint64_t hwndTarget) {
fire_and_forget ToastPage::ShowMessageOnWindow(hstring message, uint64_t hwndTarget, bool inApp) {
// !!! HACK !!!
// 重用 TeachingTip 有一个 bug: 前一个 Toast 正在消失时新的 Toast 不会显示。为了
// 规避它,我们每次都创建新的 TeachingTip,但要保留旧对象的引用,因为播放动画时销毁
Expand Down Expand Up @@ -127,6 +127,9 @@ fire_and_forget ToastPage::ShowMessageOnWindow(hstring message, uint64_t hwndTar
}
});

// 应用内消息无需显示 logo
_IsLogoShown(!inApp);

curTeachingTip.IsOpen(true);

// 第三个参数用于延长 oldTeachingTip 的生存期,确保关闭动画播放完毕后再析构。
Expand Down Expand Up @@ -174,4 +177,17 @@ fire_and_forget ToastPage::ShowMessageOnWindow(hstring message, uint64_t hwndTar
} while (curTeachingTip.IsLoaded() && curTeachingTip.IsOpen());
}

void ToastPage::ShowMessageInApp(hstring message) {
ShowMessageOnWindow(message, Application::Current().as<App>().HwndMain(), true);
}

void ToastPage::_IsLogoShown(bool value) {
if (_isLogoShown == value) {
return;
}

_isLogoShown = value;
RaisePropertyChanged(L"IsLogoShown");
}

}
11 changes: 10 additions & 1 deletion src/Magpie.App/ToastPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,21 @@ struct ToastPage : ToastPageT<ToastPage>,
return _logo;
}

fire_and_forget ShowMessageOnWindow(hstring message, uint64_t hwndTarget);
bool IsLogoShown() const noexcept {
return _isLogoShown;
}

fire_and_forget ShowMessageOnWindow(hstring message, uint64_t hwndTarget, bool inApp = false);

void ShowMessageInApp(hstring message);

private:
void _IsLogoShown(bool value);

Imaging::SoftwareBitmapSource _logo{ nullptr };
HWND _hwndToast;
MUXC::TeachingTip _oldTeachingTip{ nullptr };
bool _isLogoShown = true;
};

}
Expand Down
2 changes: 2 additions & 0 deletions src/Magpie.App/ToastPage.idl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ namespace Magpie.App {
ToastPage(UInt64 hwndToast);

Windows.UI.Xaml.Media.Imaging.SoftwareBitmapSource Logo { get; };
Boolean IsLogoShown { get; };

void ShowMessageOnWindow(String message, UInt64 hwndTarget);
void ShowMessageInApp(String message);

// https://github.com/microsoft/microsoft-ui-xaml/issues/7579
void UnloadObject(Windows.UI.Xaml.DependencyObject object);
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 @@ -15,7 +15,8 @@
Height="16"
Margin="0,0,8,0"
VerticalAlignment="Center"
Source="{x:Bind Logo, Mode=OneWay}" />
Source="{x:Bind Logo, Mode=OneWay}"
Visibility="{x:Bind IsLogoShown, Mode=OneWay}" />
<TextBlock x:Name="MessageTextBlock"
TextWrapping="WrapWholeWords" />
</local:SimpleStackPanel>
Expand Down
10 changes: 8 additions & 2 deletions src/Magpie.App/ToastService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,18 @@ void ToastService::Uninitialize() noexcept {
_toastThread.join();
}

void ToastService::ShowMessageOnWindow(std::wstring_view message, HWND hwndTarget) noexcept {
void ToastService::ShowMessageOnWindow(std::wstring_view message, HWND hwndTarget) const noexcept {
_Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this, message(std::wstring(message)), hwndTarget]() {
_toastPage.ShowMessageOnWindow(message, (uint64_t)hwndTarget);
});
}

void ToastService::ShowMessageInApp(std::wstring_view message) const noexcept {
_Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this, message(std::wstring(message))]() {
_toastPage.ShowMessageInApp(message);
});
}

void ToastService::_ToastThreadProc() noexcept {
#ifdef _DEBUG
SetThreadDescription(GetCurrentThread(), L"Toast 线程");
Expand Down Expand Up @@ -134,7 +140,7 @@ LRESULT ToastService::_ToastWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM l
return DefWindowProc(hWnd, msg, wParam, lParam);
}

const CoreDispatcher& ToastService::_Dispatcher() noexcept {
const CoreDispatcher& ToastService::_Dispatcher() const noexcept {
_dispatcherInitialized.wait(false, std::memory_order_acquire);
return _dispatcher;
}
Expand Down
6 changes: 4 additions & 2 deletions src/Magpie.App/ToastService.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ class ToastService {

void Uninitialize() noexcept;

void ShowMessageOnWindow(std::wstring_view message, HWND hwndTarget) noexcept;
void ShowMessageOnWindow(std::wstring_view message, HWND hwndTarget) const noexcept;

void ShowMessageInApp(std::wstring_view message) const noexcept;

private:
ToastService() = default;
Expand All @@ -27,7 +29,7 @@ class ToastService {
static LRESULT CALLBACK _ToastWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// 确保 _dispatcher 完成初始化
const CoreDispatcher& _Dispatcher() noexcept;
const CoreDispatcher& _Dispatcher() const noexcept;

std::thread _toastThread;

Expand Down

0 comments on commit 06c7c54

Please sign in to comment.