mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
contextmenu
This commit is contained in:
@@ -44,8 +44,6 @@ public sealed partial class App : Application
|
||||
private readonly IActivation activation;
|
||||
private readonly ILogger<App> logger;
|
||||
|
||||
private NotifyIconController? notifyIconController;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Abstraction;
|
||||
|
||||
namespace Snap.Hutao.Core.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// 对象扩展
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal static class CastServiceExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// <see langword="as"/> 的链式调用扩展
|
||||
/// </summary>
|
||||
/// <typeparam name="T">目标转换类型</typeparam>
|
||||
/// <param name="service">对象</param>
|
||||
/// <returns>转换类型后的对象</returns>
|
||||
[Obsolete("Not useful anymore")]
|
||||
public static T? As<T>(this ICastService service)
|
||||
where T : class
|
||||
{
|
||||
return service as T;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using CommunityToolkit.WinUI.Notifications;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Core.Windowing.HotKey;
|
||||
@@ -63,6 +64,7 @@ internal sealed partial class Activation : IActivation, IDisposable
|
||||
serviceProvider.GetRequiredService<HotKeyOptions>().RegisterAll();
|
||||
if (LocalSetting.Get(SettingKeys.IsNotifyIconEnabled, true))
|
||||
{
|
||||
serviceProvider.GetRequiredService<App>().DispatcherShutdownMode = DispatcherShutdownMode.OnExplicitShutdown;
|
||||
_ = serviceProvider.GetRequiredService<NotifyIconController>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,18 @@ namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
internal sealed class ConsoleWindowLifeTime : IDisposable
|
||||
{
|
||||
public const bool DebugModeEnabled =
|
||||
#if IS_ALPHA_BUILD
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
private readonly bool consoleWindowAllocated;
|
||||
|
||||
public ConsoleWindowLifeTime()
|
||||
{
|
||||
if (LocalSetting.Get(SettingKeys.IsAllocConsoleDebugModeEnabled, false))
|
||||
if (LocalSetting.Get(SettingKeys.IsAllocConsoleDebugModeEnabled, DebugModeEnabled))
|
||||
{
|
||||
consoleWindowAllocated = AllocConsole();
|
||||
if (consoleWindowAllocated)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.Backdrop;
|
||||
|
||||
internal interface IWindowNeedEraseBackground;
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Composition;
|
||||
using Microsoft.UI.Composition.SystemBackdrops;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.Backdrop;
|
||||
|
||||
// https://github.com/microsoft/microsoft-ui-xaml/blob/winui3/release/1.5-stable/controls/dev/Materials/DesktopAcrylicBackdrop/DesktopAcrylicBackdrop.cpp
|
||||
internal sealed class InputActiveDesktopAcrylicBackdrop : SystemBackdrop
|
||||
{
|
||||
private readonly ConcurrentDictionary<ICompositionSupportsSystemBackdrop, DesktopAcrylicController> controllers = [];
|
||||
|
||||
protected override void OnTargetConnected(ICompositionSupportsSystemBackdrop target, XamlRoot xamlRoot)
|
||||
{
|
||||
base.OnTargetConnected(target, xamlRoot);
|
||||
|
||||
DesktopAcrylicController newController = new();
|
||||
SystemBackdropConfiguration configuration = GetDefaultSystemBackdropConfiguration(target, xamlRoot);
|
||||
|
||||
configuration.IsInputActive = true;
|
||||
|
||||
newController.AddSystemBackdropTarget(target);
|
||||
newController.SetSystemBackdropConfiguration(configuration);
|
||||
controllers.TryAdd(target, newController);
|
||||
}
|
||||
|
||||
protected override void OnTargetDisconnected(ICompositionSupportsSystemBackdrop target)
|
||||
{
|
||||
base.OnTargetDisconnected(target);
|
||||
|
||||
if (controllers.TryRemove(target, out DesktopAcrylicController? controller))
|
||||
{
|
||||
controller.RemoveSystemBackdropTarget(target);
|
||||
controller.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<Flyout
|
||||
x:Class="Snap.Hutao.Core.Windowing.NotifyIcon.NotifyIconContextMenu"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
xmlns:shcwb="using:Snap.Hutao.Core.Windowing.Backdrop"
|
||||
xmlns:shv="using:Snap.Hutao.ViewModel"
|
||||
ShouldConstrainToRootBounds="False"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Flyout.SystemBackdrop>
|
||||
<shcwb:InputActiveDesktopAcrylicBackdrop/>
|
||||
</Flyout.SystemBackdrop>
|
||||
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
|
||||
<Grid
|
||||
x:Name="Root"
|
||||
d:DataContext="{d:DesignInstance shv:NotifyIconViewModel}"
|
||||
Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Margin="8" Text="{Binding Title}"/>
|
||||
<Grid Grid.Row="1" Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}">
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<AppBarButton
|
||||
Margin="4,0"
|
||||
Command="{Binding ExitCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="退出"
|
||||
Style="{ThemeResource DefaultAppBarButtonStyle}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Flyout>
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.ViewModel;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||
|
||||
internal sealed partial class NotifyIconContextMenu : Flyout
|
||||
{
|
||||
public NotifyIconContextMenu()
|
||||
{
|
||||
AllowFocusOnInteraction = false;
|
||||
InitializeComponent();
|
||||
Root.DataContext = Ioc.Default.GetRequiredService<NotifyIconViewModel>();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -13,6 +16,8 @@ namespace Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed class NotifyIconController : IDisposable
|
||||
{
|
||||
private readonly LazySlim<NotifyIconContextMenu> lazyMenu = new(() => new());
|
||||
private readonly NotifyIconXamlHostWindow xamlHostWindow;
|
||||
private readonly NotifyIconMessageWindow messageWindow;
|
||||
private readonly System.Drawing.Icon icon;
|
||||
|
||||
@@ -21,6 +26,8 @@ internal sealed class NotifyIconController : IDisposable
|
||||
StorageFile iconFile = StorageFile.GetFileFromApplicationUriAsync("ms-appx:///Assets/Logo.ico".ToUri()).AsTask().GetAwaiter().GetResult();
|
||||
icon = new(iconFile.Path);
|
||||
|
||||
xamlHostWindow = new();
|
||||
|
||||
messageWindow = new()
|
||||
{
|
||||
TaskbarCreated = window =>
|
||||
@@ -31,10 +38,15 @@ internal sealed class NotifyIconController : IDisposable
|
||||
HutaoException.InvalidOperation("Failed to recreate NotifyIcon");
|
||||
}
|
||||
},
|
||||
ContextMenuRequested = (window, point) =>
|
||||
{
|
||||
Flyout flyout = lazyMenu.Value;
|
||||
RECT iconRect = NotifyIconMethods.GetRect(Id, window.HWND);
|
||||
xamlHostWindow.ShowFlyoutAt(flyout, new Windows.Foundation.Point(point.X, point.Y), iconRect);
|
||||
},
|
||||
};
|
||||
|
||||
NotifyIconMethods.Delete(Id);
|
||||
|
||||
if (!NotifyIconMethods.Add(Id, messageWindow.HWND, "Snap Hutao", NotifyIconMessageWindow.WM_NOTIFYICON_CALLBACK, (HICON)icon.Handle))
|
||||
{
|
||||
HutaoException.InvalidOperation("Failed to create NotifyIcon");
|
||||
@@ -61,5 +73,7 @@ internal sealed class NotifyIconController : IDisposable
|
||||
messageWindow.Dispose();
|
||||
NotifyIconMethods.Delete(Id);
|
||||
icon.Dispose();
|
||||
|
||||
xamlHostWindow.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Snap.Hutao.Core.Windowing.Backdrop;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.Collections.Concurrent;
|
||||
@@ -64,6 +67,8 @@ internal sealed class NotifyIconMessageWindow : IDisposable
|
||||
|
||||
public Action<NotifyIconMessageWindow>? TaskbarCreated { get; set; }
|
||||
|
||||
public Action<NotifyIconMessageWindow, PointUInt16>? ContextMenuRequested { get; set; }
|
||||
|
||||
public HWND HWND { get; }
|
||||
|
||||
public void Dispose()
|
||||
@@ -100,7 +105,7 @@ internal sealed class NotifyIconMessageWindow : IDisposable
|
||||
if (uMsg is WM_NOTIFYICON_CALLBACK)
|
||||
{
|
||||
LPARAM2 lParam2 = *(LPARAM2*)&lParam;
|
||||
WPARAM2 wParam2 = *(WPARAM2*)&wParam;
|
||||
PointUInt16 wParam2 = *(PointUInt16*)&wParam;
|
||||
|
||||
switch (lParam2.Low)
|
||||
{
|
||||
@@ -123,6 +128,7 @@ internal sealed class NotifyIconMessageWindow : IDisposable
|
||||
case WM_RBUTTONUP:
|
||||
break;
|
||||
case WM_CONTEXTMENU:
|
||||
window.ContextMenuRequested?.Invoke(window, wParam2);
|
||||
Debug.WriteLine($"[uMsg: 0x{uMsg:X8}] [X: {wParam2.X} Y: {wParam2.Y}] [Low: WM_CONTEXTMENU High: 0x{lParam2.High:X8}]");
|
||||
break;
|
||||
default:
|
||||
@@ -152,24 +158,4 @@ internal sealed class NotifyIconMessageWindow : IDisposable
|
||||
public readonly uint Low;
|
||||
public readonly uint High;
|
||||
}
|
||||
|
||||
private readonly struct WPARAM2
|
||||
{
|
||||
public readonly ushort X;
|
||||
public readonly ushort Y;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class NotifyIconXamlHostWindow : Window
|
||||
{
|
||||
public NotifyIconXamlHostWindow()
|
||||
{
|
||||
Content = new Border();
|
||||
|
||||
OverlappedPresenter presenter = OverlappedPresenter.Create();
|
||||
presenter.SetBorderAndTitleBar(false, false);
|
||||
presenter.IsAlwaysOnTop = true;
|
||||
presenter.IsResizable = false;
|
||||
AppWindow.SetPresenter(presenter);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.Shell;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Snap.Hutao.Win32.Shell32;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||
@@ -52,6 +53,20 @@ internal sealed class NotifyIconMethods
|
||||
return Delete(in data);
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
public static unsafe RECT GetRect(Guid id, HWND hWND)
|
||||
{
|
||||
NOTIFYICONIDENTIFIER identifier = new()
|
||||
{
|
||||
cbSize = (uint)sizeof(NOTIFYICONIDENTIFIER),
|
||||
hWnd = hWND,
|
||||
guidItem = id,
|
||||
};
|
||||
|
||||
Marshal.ThrowExceptionForHR(Shell_NotifyIconGetRect(ref identifier, out RECT rect));
|
||||
return rect;
|
||||
}
|
||||
|
||||
public static BOOL SetFocus(ref readonly NOTIFYICONDATAW data)
|
||||
{
|
||||
return Shell_NotifyIconW(NOTIFY_ICON_MESSAGE.NIM_SETFOCUS, in data);
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Snap.Hutao.Core.Windowing.Backdrop;
|
||||
using Snap.Hutao.Win32;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using Windows.Foundation;
|
||||
using WinRT.Interop;
|
||||
using static Snap.Hutao.Win32.User32;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||
|
||||
internal sealed class NotifyIconXamlHostWindow : Window, IDisposable, IWindowNeedEraseBackground
|
||||
{
|
||||
private readonly XamlWindowSubclass subclass;
|
||||
|
||||
public NotifyIconXamlHostWindow()
|
||||
{
|
||||
Content = new Border();
|
||||
|
||||
this.SetLayeredWindow();
|
||||
|
||||
AppWindow.Title = "SnapHutaoNotifyIconXamlHost";
|
||||
AppWindow.IsShownInSwitchers = false;
|
||||
if (AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
presenter.IsMaximizable = false;
|
||||
presenter.IsMinimizable = false;
|
||||
presenter.IsResizable = false;
|
||||
presenter.IsAlwaysOnTop = true;
|
||||
presenter.SetBorderAndTitleBar(false, false);
|
||||
}
|
||||
|
||||
XamlWindowOptions options = new(this, default!, default);
|
||||
subclass = new(this, options);
|
||||
subclass.Initialize();
|
||||
|
||||
Activate();
|
||||
}
|
||||
|
||||
public void ShowFlyoutAt(FlyoutBase flyout, Point point, RECT icon)
|
||||
{
|
||||
icon.left -= 8;
|
||||
icon.top -= 8;
|
||||
icon.right += 8;
|
||||
icon.bottom += 8;
|
||||
|
||||
HWND hwnd = WindowNative.GetWindowHandle(this);
|
||||
ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_NORMAL);
|
||||
SetForegroundWindow(hwnd);
|
||||
|
||||
AppWindow.MoveAndResize(StructMarshal.RectInt32(icon));
|
||||
flyout.ShowAt(Content, new()
|
||||
{
|
||||
Placement = FlyoutPlacementMode.Auto,
|
||||
ShowMode = FlyoutShowMode.Transient,
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
subclass.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||
|
||||
internal readonly struct PointUInt16
|
||||
{
|
||||
public readonly ushort X;
|
||||
public readonly ushort Y;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.Runtime.CompilerServices;
|
||||
using WinRT.Interop;
|
||||
using static Snap.Hutao.Win32.Macros;
|
||||
using static Snap.Hutao.Win32.User32;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
@@ -27,5 +28,6 @@ internal static class WindowExtension
|
||||
nint style = GetWindowLongPtrW(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
|
||||
style |= (nint)WINDOW_EX_STYLE.WS_EX_LAYERED;
|
||||
SetWindowLongPtrW(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, style);
|
||||
SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_COLORKEY | LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_ALPHA);
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,12 @@
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Windowing.Backdrop;
|
||||
using Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||
using Snap.Hutao.Win32;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.Shell;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Snap.Hutao.Win32.ComCtl32;
|
||||
@@ -36,9 +38,7 @@ internal sealed class XamlWindowSubclass : IDisposable
|
||||
{
|
||||
windowProc = SUBCLASSPROC.Create(&OnSubclassProcedure);
|
||||
unmanagedAccess = UnmanagedAccess.Create(this);
|
||||
bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, unmanagedAccess);
|
||||
|
||||
return windowHooked;
|
||||
return SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, unmanagedAccess);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -75,7 +75,7 @@ internal sealed class XamlWindowSubclass : IDisposable
|
||||
|
||||
case WM_ERASEBKGND:
|
||||
{
|
||||
if (state.window.SystemBackdrop is IBackdropNeedEraseBackground)
|
||||
if (state.window is IWindowNeedEraseBackground || state.window.SystemBackdrop is IBackdropNeedEraseBackground)
|
||||
{
|
||||
return (LRESULT)(int)BOOL.TRUE;
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
<None Remove="Control\Theme\TransitionCollection.xaml" />
|
||||
<None Remove="Control\Theme\Uri.xaml" />
|
||||
<None Remove="Control\Theme\WindowOverride.xaml" />
|
||||
<None Remove="Core\Windowing\NotifyIcon\NotifyIconContextMenu.xaml" />
|
||||
<None Remove="GuideWindow.xaml" />
|
||||
<None Remove="IdentifyMonitorWindow.xaml" />
|
||||
<None Remove="IdentityStructs.json" />
|
||||
@@ -355,6 +356,11 @@
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'">
|
||||
<ProjectCapability Include="Msix" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Core\Windowing\NotifyIcon\NotifyIconContextMenu.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Dialog\SpiralAbyssUploadRecordHomaNotLoginDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
43
src/Snap.Hutao/Snap.Hutao/ViewModel/NotifyIconViewModel.cs
Normal file
43
src/Snap.Hutao/Snap.Hutao/ViewModel/NotifyIconViewModel.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Core;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace Snap.Hutao.ViewModel;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class NotifyIconViewModel : ObservableObject
|
||||
{
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
private readonly App app;
|
||||
|
||||
public string Title
|
||||
{
|
||||
[SuppressMessage("", "IDE0027")]
|
||||
get
|
||||
{
|
||||
string name = new StringBuilder()
|
||||
.Append("App")
|
||||
.AppendIf(runtimeOptions.IsElevated, "Elevated")
|
||||
#if DEBUG
|
||||
.Append("Dev")
|
||||
#endif
|
||||
.Append("NameAndVersion")
|
||||
.ToString();
|
||||
|
||||
string? format = SH.GetString(CultureInfo.CurrentCulture, name);
|
||||
ArgumentException.ThrowIfNullOrEmpty(format);
|
||||
return string.Format(CultureInfo.CurrentCulture, format, runtimeOptions.Version);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("ExitCommand")]
|
||||
private void Exit()
|
||||
{
|
||||
app.Exit();
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using Microsoft.Windows.AppLifecycle;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.Caching;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Core.Shell;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
@@ -154,7 +155,7 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
|
||||
public bool IsAllocConsoleDebugModeEnabled
|
||||
{
|
||||
get => LocalSetting.Get(SettingKeys.IsAllocConsoleDebugModeEnabled, false);
|
||||
get => LocalSetting.Get(SettingKeys.IsAllocConsoleDebugModeEnabled, ConsoleWindowLifeTime.DebugModeEnabled);
|
||||
set
|
||||
{
|
||||
if (IsViewDisposed)
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Snap.Hutao.Web.Request.Builder;
|
||||
|
||||
internal static class HttpRequestMessageBuilderExtension
|
||||
{
|
||||
private const string RequestErrorMessage = "请求异常已忽略";
|
||||
private const string RequestErrorMessage = "请求异常已忽略: {0}";
|
||||
|
||||
internal static async ValueTask<TResult?> TryCatchSendAsync<TResult>(this HttpRequestMessageBuilder builder, HttpClient httpClient, ILogger logger, CancellationToken token)
|
||||
where TResult : class
|
||||
@@ -29,7 +29,7 @@ internal static class HttpRequestMessageBuilderExtension
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
logger.LogWarning(ex, RequestErrorMessage);
|
||||
logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri);
|
||||
|
||||
if (ex.StatusCode is HttpStatusCode.BadGateway)
|
||||
{
|
||||
@@ -50,22 +50,22 @@ internal static class HttpRequestMessageBuilderExtension
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.LogWarning(ex, RequestErrorMessage);
|
||||
logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri);
|
||||
return default;
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
logger.LogWarning(ex, RequestErrorMessage);
|
||||
logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri);
|
||||
return default;
|
||||
}
|
||||
catch (HttpContentSerializationException ex)
|
||||
{
|
||||
logger.LogWarning(ex, RequestErrorMessage);
|
||||
logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri);
|
||||
return default;
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
logger.LogWarning(ex, RequestErrorMessage);
|
||||
logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -80,23 +80,23 @@ internal static class HttpRequestMessageBuilderExtension
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
logger.LogWarning(ex, RequestErrorMessage);
|
||||
logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.LogWarning(ex, RequestErrorMessage);
|
||||
logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
logger.LogWarning(ex, RequestErrorMessage);
|
||||
logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri);
|
||||
}
|
||||
catch (HttpContentSerializationException ex)
|
||||
{
|
||||
logger.LogWarning(ex, RequestErrorMessage);
|
||||
logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
logger.LogWarning(ex, RequestErrorMessage);
|
||||
logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/Snap.Hutao/Snap.Hutao/Win32/Foundation/COLORREF.cs
Normal file
9
src/Snap.Hutao/Snap.Hutao/Win32/Foundation/COLORREF.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Win32.Foundation;
|
||||
|
||||
internal readonly struct COLORREF
|
||||
{
|
||||
public readonly uint Value;
|
||||
}
|
||||
@@ -4,10 +4,10 @@
|
||||
namespace Snap.Hutao.Win32.Foundation;
|
||||
|
||||
[SuppressMessage("", "SA1307")]
|
||||
internal readonly struct RECT
|
||||
internal struct RECT
|
||||
{
|
||||
public readonly int left;
|
||||
public readonly int top;
|
||||
public readonly int right;
|
||||
public readonly int bottom;
|
||||
public int left;
|
||||
public int top;
|
||||
public int right;
|
||||
public int bottom;
|
||||
}
|
||||
@@ -28,4 +28,10 @@ internal static class Macros
|
||||
// 0x80000000 or 0x80070000 | LOWBYTE(x)
|
||||
return x <= 0 ? (int)x : (int)(((uint)x & 0x0000FFFFU) | ((uint)FACILITY_CODE.FACILITY_WIN32 << 16) | 0x80000000U);
|
||||
}
|
||||
|
||||
public static unsafe COLORREF RGB(byte r, byte g, byte b)
|
||||
{
|
||||
uint value = (ushort)(r | g << 8) | (uint)b << 16;
|
||||
return *(COLORREF*)&value;
|
||||
}
|
||||
}
|
||||
@@ -129,8 +129,14 @@ internal sealed partial class RegistryWatcher : IDisposable
|
||||
|
||||
if (!disposed)
|
||||
{
|
||||
// Before exiting, signal the Dispose method.
|
||||
disposeEvent.Reset();
|
||||
try
|
||||
{
|
||||
// Before exiting, signal the Dispose method.
|
||||
disposeEvent.Reset();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
||||
@@ -34,6 +34,21 @@ internal static class Shell32
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("SHELL32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
|
||||
[SupportedOSPlatform("windows6.1")]
|
||||
public static unsafe extern HRESULT Shell_NotifyIconGetRect(NOTIFYICONIDENTIFIER* identifier, RECT* iconLocation);
|
||||
|
||||
public static unsafe HRESULT Shell_NotifyIconGetRect(ref readonly NOTIFYICONIDENTIFIER identifier, out RECT iconLocation)
|
||||
{
|
||||
fixed (NOTIFYICONIDENTIFIER* p = &identifier)
|
||||
{
|
||||
fixed (RECT* pRect = &iconLocation)
|
||||
{
|
||||
return Shell_NotifyIconGetRect(p, pRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("SHELL32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
|
||||
[SupportedOSPlatform("windows5.1.2600")]
|
||||
public static unsafe extern BOOL Shell_NotifyIconW(NOTIFY_ICON_MESSAGE dwMessage, NOTIFYICONDATAW* lpData);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using System.Buffers.Binary;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -32,6 +33,11 @@ internal static class StructMarshal
|
||||
return new(0, 0, size.X, size.Y);
|
||||
}
|
||||
|
||||
public static RectInt32 RectInt32(RECT rect)
|
||||
{
|
||||
return new(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
|
||||
}
|
||||
|
||||
public static RectInt32 RectInt32(SizeInt32 size)
|
||||
{
|
||||
return new(0, 0, size.Width, size.Height);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
|
||||
namespace Snap.Hutao.Win32.UI.Shell;
|
||||
|
||||
[SuppressMessage("", "SA1307")]
|
||||
internal struct NOTIFYICONIDENTIFIER
|
||||
{
|
||||
public uint cbSize;
|
||||
public HWND hWnd;
|
||||
public uint uID;
|
||||
public Guid guidItem;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
[Flags]
|
||||
internal enum LAYERED_WINDOW_ATTRIBUTES_FLAGS : uint
|
||||
{
|
||||
LWA_ALPHA = 2u,
|
||||
LWA_COLORKEY = 1u,
|
||||
}
|
||||
@@ -163,6 +163,10 @@ internal static class User32
|
||||
[SupportedOSPlatform("windows5.0")]
|
||||
public static extern BOOL SetForegroundWindow(HWND hWnd);
|
||||
|
||||
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
|
||||
[SupportedOSPlatform("windows5.0")]
|
||||
public static extern BOOL SetLayeredWindowAttributes(HWND hwnd, COLORREF crKey, byte bAlpha, LAYERED_WINDOW_ATTRIBUTES_FLAGS dwFlags);
|
||||
|
||||
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
|
||||
[SupportedOSPlatform("windows5.0")]
|
||||
public static extern BOOL SetPropW(HWND hWnd, PCWSTR lpString, [AllowNull] HANDLE hData);
|
||||
@@ -180,6 +184,10 @@ internal static class User32
|
||||
[SupportedOSPlatform("windows5.0")]
|
||||
public static extern nint SetWindowLongPtrW(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, nint dwNewLong);
|
||||
|
||||
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
|
||||
[SupportedOSPlatform("windows5.0")]
|
||||
public static extern BOOL ShowWindow(HWND hWnd, SHOW_WINDOW_CMD nCmdShow);
|
||||
|
||||
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
|
||||
[SupportedOSPlatform("windows5.0")]
|
||||
public static extern BOOL UnregisterHotKey([AllowNull] HWND hWnd, int id);
|
||||
|
||||
Reference in New Issue
Block a user