mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
自定义标题栏 (#624)
* feat: 去除标题栏 * chore: 添加注释 * feat: 保存最大化状态 * fix: 优化最大化状态 * feat: Win11 无需绘制上边框 * feat: 添加 TitlebarControl * UI: 更改主界面样式 * fix: 修复一个隐蔽的bug * feat: 添加 CaptionButtonsControl * feat: 优化上边框颜色 * feat: 实现标题栏的 UI * fix: 修复标题栏按钮不跟随主题的问题 * fix: 优化主窗口最小尺寸 * fix: 修复上边框绘制错误 * UI: 优化样式 * UI: 稍微优化标题按钮样式 * UI: 优化标题按钮样式 * UI: 优化标题按钮样式 * UI: 优化标题栏样式 * feat: 实现拖拽功能 * fix: 修复调整窗口大小时闪烁的问题 * fix: 更改上边框的实现方式 * feat: 实现上边框调整尺寸和支持 Win11 的贴靠布局 * feat: 实现标题栏按钮的 hover * feat: 实现标题栏按钮的功能 * perf: 优化性能和添加注释 * fix: 修复一个小错误 * fix: 小修复 * fix: 优化最大化状态 * fix: 修复标题栏上右键菜单 * chore: 添加注释 * fix: 修复 Win10 中以最大化启动时一瞬间显示主题色背景的问题 * UI: 更新 ToggleSwitch 样式 * fix: 修复以最大化显示时的窗口动画 * fix: 修复 Win11 21H1/21H2 的背景 * chore: 优化注释 * feat: 在标题栏显示图标 * UI: 为标题栏添加动画 * fix: 修复导航栏菜单覆盖标题栏的问题 * fix: 修复标题按钮下方的可拖动区域 * feat: 导航栏不再支持 Minimal 状态 * chore: 删除不再需要的代码 * UI: 修正配置文件页面图标位置 * docs: 更新主窗口截图
This commit is contained in:
parent
d2ccc85674
commit
914c683f98
37 changed files with 2150 additions and 1082 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 137 KiB |
BIN
img/主窗口.png
BIN
img/主窗口.png
Binary file not shown.
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 128 KiB |
|
|
@ -26,6 +26,8 @@
|
|||
#include "ScalingConfigurationPage.idl"
|
||||
#include "ProfilePage.idl"
|
||||
#include "SettingsPage.idl"
|
||||
#include "CaptionButtonsControl.idl"
|
||||
#include "TitleBarControl.idl"
|
||||
|
||||
namespace Magpie.App {
|
||||
enum ShortcutAction {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,628 @@
|
|||
<Setter Property="Width" Value="40" />
|
||||
</Style>
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultToggleSwitchStyle}"
|
||||
TargetType="ToggleSwitch">
|
||||
<Style.Setters>
|
||||
<Setter Property="MinWidth" Value="0" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleSwitch">
|
||||
<Grid VerticalAlignment="Center"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<ContentPresenter x:Name="HeaderContentPresenter"
|
||||
Grid.Row="0"
|
||||
Margin="{ThemeResource ToggleSwitchTopHeaderMargin}"
|
||||
VerticalAlignment="Top"
|
||||
x:DeferLoadStrategy="Lazy"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
Foreground="{ThemeResource ToggleSwitchHeaderForeground}"
|
||||
IsHitTestVisible="False"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="Collapsed" />
|
||||
<Grid Grid.Row="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="{ThemeResource ToggleSwitchPreContentMargin}" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="{ThemeResource ToggleSwitchPostContentMargin}" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="12"
|
||||
MaxWidth="12" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid x:Name="SwitchAreaGrid"
|
||||
Grid.RowSpan="3"
|
||||
Grid.ColumnSpan="3"
|
||||
Margin="0,5"
|
||||
Background="{ThemeResource ToggleSwitchContainerBackground}"
|
||||
Control.IsTemplateFocusTarget="True"
|
||||
CornerRadius="{TemplateBinding CornerRadius}" />
|
||||
<ContentPresenter x:Name="OffContentPresenter"
|
||||
Grid.RowSpan="3"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{TemplateBinding OffContent}"
|
||||
ContentTemplate="{TemplateBinding OffContentTemplate}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0" />
|
||||
<ContentPresenter x:Name="OnContentPresenter"
|
||||
Grid.RowSpan="3"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{TemplateBinding OnContent}"
|
||||
ContentTemplate="{TemplateBinding OnContentTemplate}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0" />
|
||||
<Rectangle x:Name="OuterBorder"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Width="40"
|
||||
Height="20"
|
||||
Fill="{ThemeResource ToggleSwitchFillOff}"
|
||||
RadiusX="10"
|
||||
RadiusY="10"
|
||||
Stroke="{ThemeResource ToggleSwitchStrokeOff}"
|
||||
StrokeThickness="{ThemeResource ToggleSwitchOuterBorderStrokeThickness}" />
|
||||
<Rectangle x:Name="SwitchKnobBounds"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Width="40"
|
||||
Height="20"
|
||||
Fill="{ThemeResource ToggleSwitchFillOn}"
|
||||
Opacity="0"
|
||||
RadiusX="10"
|
||||
RadiusY="10"
|
||||
Stroke="{ThemeResource ToggleSwitchStrokeOn}"
|
||||
StrokeThickness="{ThemeResource ToggleSwitchOnStrokeThickness}" />
|
||||
<Grid x:Name="SwitchKnob"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Width="20"
|
||||
Height="20"
|
||||
HorizontalAlignment="Left">
|
||||
<Border x:Name="SwitchKnobOn"
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="0,0,3,0"
|
||||
HorizontalAlignment="Right"
|
||||
Background="{ThemeResource ToggleSwitchKnobFillOn}"
|
||||
BackgroundSizing="OuterBorderEdge"
|
||||
BorderBrush="{ThemeResource ToggleSwitchKnobStrokeOn}"
|
||||
CornerRadius="7"
|
||||
Opacity="0"
|
||||
RenderTransformOrigin="0.5, 0.5">
|
||||
<Border.RenderTransform>
|
||||
<CompositeTransform />
|
||||
</Border.RenderTransform>
|
||||
</Border>
|
||||
<Rectangle x:Name="SwitchKnobOff"
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="3,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Fill="{ThemeResource ToggleSwitchKnobFillOff}"
|
||||
RadiusX="7"
|
||||
RadiusY="7"
|
||||
RenderTransformOrigin="0.5, 0.5">
|
||||
<Rectangle.RenderTransform>
|
||||
<CompositeTransform />
|
||||
</Rectangle.RenderTransform>
|
||||
</Rectangle>
|
||||
<Grid.RenderTransform>
|
||||
<TranslateTransform x:Name="KnobTranslateTransform" />
|
||||
</Grid.RenderTransform>
|
||||
</Grid>
|
||||
<Thumb x:Name="SwitchThumb"
|
||||
Grid.RowSpan="3"
|
||||
Grid.ColumnSpan="3"
|
||||
AutomationProperties.AccessibilityView="Raw">
|
||||
<Thumb.Template>
|
||||
<ControlTemplate TargetType="Thumb">
|
||||
<Rectangle Fill="Transparent" />
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
</Thumb>
|
||||
</Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOff}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchFillOff}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOff}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOn}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchFillOn}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOn}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchContainerBackground}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOffPointerOver}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchFillOffPointerOver}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOffPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOnPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchFillOnPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOnPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid"
|
||||
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchContainerBackgroundPointerOver}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="SwitchKnobOn.HorizontalAlignment" Value="Right" />
|
||||
<Setter Target="SwitchKnobOn.Margin" Value="0,0,3,0" />
|
||||
<Setter Target="SwitchKnobOff.HorizontalAlignment" Value="Left" />
|
||||
<Setter Target="SwitchKnobOff.Margin" Value="3,0,0,0" />
|
||||
</VisualState.Setters>
|
||||
<Storyboard>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOffPressed}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchFillOffPressed}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchFillOnPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOnPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOffPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOnPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid"
|
||||
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchContainerBackgroundPressed}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="17" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="17" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchHeaderForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OffContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchContentForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OnContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchContentForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOffDisabled}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchFillOffDisabled}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchFillOnDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOnDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOffDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOnDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid"
|
||||
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchContainerBackgroundDisabled}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ToggleStates">
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition x:Name="DraggingToOnTransition"
|
||||
GeneratedDuration="0"
|
||||
From="Dragging"
|
||||
To="On">
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobCurrentToOnOffset}"
|
||||
TargetName="SwitchKnob" />
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="OnToDraggingTransition"
|
||||
GeneratedDuration="0"
|
||||
From="On"
|
||||
To="Dragging">
|
||||
<Storyboard>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="0"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="0"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="0"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="DraggingToOffTransition"
|
||||
GeneratedDuration="0"
|
||||
From="Dragging"
|
||||
To="Off">
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobCurrentToOffOffset}"
|
||||
TargetName="SwitchKnob" />
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="OnToOffTransition"
|
||||
GeneratedDuration="0"
|
||||
From="On"
|
||||
To="Off">
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobOnToOffOffset}"
|
||||
TargetName="SwitchKnob" />
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="OffToOnTransition"
|
||||
GeneratedDuration="0"
|
||||
From="Off"
|
||||
To="On">
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobOffToOnOffset}"
|
||||
TargetName="SwitchKnob" />
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
</VisualStateGroup.Transitions>
|
||||
<VisualState x:Name="Dragging" />
|
||||
<VisualState x:Name="Off" />
|
||||
<VisualState x:Name="On">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="KnobTranslateTransform"
|
||||
Storyboard.TargetProperty="X"
|
||||
To="20"
|
||||
Duration="0" />
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ContentStates">
|
||||
<VisualState x:Name="OffContent">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="OffContentPresenter"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OffContentPresenter"
|
||||
Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="OnContent">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="OnContentPresenter"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OnContentPresenter"
|
||||
Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
|
||||
<x:Double x:Key="SecondaryTextFontSize">12</x:Double>
|
||||
<Style x:Key="SecondaryTextStyle"
|
||||
TargetType="TextBlock">
|
||||
|
|
@ -48,7 +670,7 @@
|
|||
<x:Double x:Key="StandardIconSize">14</x:Double>
|
||||
|
||||
<!-- ComboBox -->
|
||||
<x:Double x:Key="SettingBoxMinWidth">200</x:Double>
|
||||
<x:Double x:Key="SettingBoxMinWidth">220</x:Double>
|
||||
<Style x:Key="ComboBoxSettingStyle"
|
||||
BasedOn="{StaticResource DefaultComboBoxStyle}"
|
||||
TargetType="ComboBox">
|
||||
|
|
@ -157,7 +779,6 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<muxc:XamlControlsResources ControlsResourcesVersion="Version2" />
|
||||
<ResourceDictionary Source="ms-appx:///Button.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///ToggleSwitch.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///KeyVisual.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
|
|
|||
134
src/Magpie.App/CaptionButtonsControl.cpp
Normal file
134
src/Magpie.App/CaptionButtonsControl.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#include "pch.h"
|
||||
#include "CaptionButtonsControl.h"
|
||||
#if __has_include("CaptionButtonsControl.g.cpp")
|
||||
#include "CaptionButtonsControl.g.cpp"
|
||||
#endif
|
||||
|
||||
namespace winrt::Magpie::App::implementation {
|
||||
|
||||
Size CaptionButtonsControl::CaptionButtonSize() const noexcept {
|
||||
ResourceDictionary resources = Resources();
|
||||
return {
|
||||
(float)unbox_value<double>(resources.Lookup(box_value(L"CaptionButtonWidth"))),
|
||||
(float)unbox_value<double>(resources.Lookup(box_value(L"CaptionButtonHeight")))
|
||||
};
|
||||
}
|
||||
|
||||
// 鼠标移动到某个按钮上时调用
|
||||
void CaptionButtonsControl::HoverButton(CaptionButton button) {
|
||||
if (_pressedButton) {
|
||||
bool hoveringOnPressedButton = _pressedButton.value() == button;
|
||||
_allInNormal = !hoveringOnPressedButton;
|
||||
|
||||
VisualStateManager::GoToState(MinimizeButton(),
|
||||
hoveringOnPressedButton && button == CaptionButton::Minimize ? L"Pressed" : L"Normal", false);
|
||||
VisualStateManager::GoToState(MaximizeButton(),
|
||||
hoveringOnPressedButton && button == CaptionButton::Maximize ? L"Pressed" : L"Normal", false);
|
||||
VisualStateManager::GoToState(CloseButton(),
|
||||
hoveringOnPressedButton && button == CaptionButton::Close ? L"Pressed" : L"Normal", false);
|
||||
} else {
|
||||
_allInNormal = false;
|
||||
|
||||
VisualStateManager::GoToState(MinimizeButton(),
|
||||
button == CaptionButton::Minimize ? L"PointerOver" : L"Normal", false);
|
||||
VisualStateManager::GoToState(MaximizeButton(),
|
||||
button == CaptionButton::Maximize ? L"PointerOver" : L"Normal", false);
|
||||
VisualStateManager::GoToState(CloseButton(),
|
||||
button == CaptionButton::Close ? L"PointerOver" : L"Normal", false);
|
||||
}
|
||||
}
|
||||
|
||||
// 在某个按钮上按下鼠标时调用
|
||||
void CaptionButtonsControl::PressButton(CaptionButton button) {
|
||||
_allInNormal = false;
|
||||
_pressedButton = button;
|
||||
|
||||
VisualStateManager::GoToState(MinimizeButton(),
|
||||
button == CaptionButton::Minimize ? L"Pressed" : L"Normal", false);
|
||||
VisualStateManager::GoToState(MaximizeButton(),
|
||||
button == CaptionButton::Maximize ? L"Pressed" : L"Normal", false);
|
||||
VisualStateManager::GoToState(CloseButton(),
|
||||
button == CaptionButton::Close ? L"Pressed" : L"Normal", false);
|
||||
}
|
||||
|
||||
// 在标题栏按钮上释放鼠标时调用
|
||||
void CaptionButtonsControl::ReleaseButton(CaptionButton button) {
|
||||
// 在某个按钮上按下然后释放视为点击,即使中途离开过也是如此,因为 HoverButton 和
|
||||
// LeaveButtons 都不改变 _pressedButton
|
||||
const bool clicked = _pressedButton && _pressedButton.value() == button;
|
||||
|
||||
if (clicked) {
|
||||
// 用户点击了某个按钮
|
||||
HWND hwndMain = (HWND)Application::Current().as<App>().HwndMain();
|
||||
|
||||
switch (_pressedButton.value()) {
|
||||
case CaptionButton::Minimize:
|
||||
PostMessage(hwndMain, WM_SYSCOMMAND, SC_MINIMIZE | HTMINBUTTON, 0);
|
||||
break;
|
||||
case CaptionButton::Maximize:
|
||||
{
|
||||
POINT cursorPos;
|
||||
GetCursorPos(&cursorPos);
|
||||
|
||||
PostMessage(
|
||||
hwndMain,
|
||||
WM_SYSCOMMAND,
|
||||
(_isWindowMaximized ? SC_RESTORE : SC_MAXIMIZE) | HTMAXBUTTON,
|
||||
MAKELPARAM(cursorPos.x, cursorPos.y)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case CaptionButton::Close:
|
||||
PostMessage(hwndMain, WM_SYSCOMMAND, SC_CLOSE, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_pressedButton.reset();
|
||||
|
||||
// 如果点击了某个按钮则清空状态,因为此时窗口状态已改变。如果在某个按钮上按下然后在
|
||||
// 其他按钮上释放,不视为点击,则将当前鼠标所在的按钮状态置为 PointerOver
|
||||
_allInNormal = clicked;
|
||||
VisualStateManager::GoToState(MinimizeButton(),
|
||||
!clicked && button == CaptionButton::Minimize ? L"PointerOver" : L"Normal", false);
|
||||
VisualStateManager::GoToState(MaximizeButton(),
|
||||
!clicked && button == CaptionButton::Maximize ? L"PointerOver" : L"Normal", false);
|
||||
VisualStateManager::GoToState(CloseButton(),
|
||||
!clicked && button == CaptionButton::Close ? L"PointerOver" : L"Normal", false);
|
||||
}
|
||||
|
||||
// 在非标题按钮上释放鼠标时调用
|
||||
void CaptionButtonsControl::ReleaseButtons() {
|
||||
if (!_pressedButton) {
|
||||
return;
|
||||
}
|
||||
_pressedButton.reset();
|
||||
|
||||
LeaveButtons();
|
||||
}
|
||||
|
||||
// 离开标题按钮时调用,不更改 _pressedButton
|
||||
void CaptionButtonsControl::LeaveButtons() {
|
||||
if (_allInNormal) {
|
||||
return;
|
||||
}
|
||||
_allInNormal = true;
|
||||
|
||||
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
|
||||
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
|
||||
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
|
||||
}
|
||||
|
||||
void CaptionButtonsControl::IsWindowMaximized(bool value) {
|
||||
if (_isWindowMaximized == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (VisualStateManager::GoToState(MaximizeButton(),
|
||||
value ? L"WindowStateMaximized" : L"WindowStateNormal", false))
|
||||
{
|
||||
_isWindowMaximized = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
37
src/Magpie.App/CaptionButtonsControl.h
Normal file
37
src/Magpie.App/CaptionButtonsControl.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
#include "CaptionButtonsControl.g.h"
|
||||
|
||||
namespace winrt::Magpie::App::implementation {
|
||||
|
||||
struct CaptionButtonsControl : CaptionButtonsControlT<CaptionButtonsControl> {
|
||||
CaptionButtonsControl() {}
|
||||
|
||||
Size CaptionButtonSize() const noexcept;
|
||||
|
||||
void HoverButton(CaptionButton button);
|
||||
|
||||
void PressButton(CaptionButton button);
|
||||
|
||||
void ReleaseButton(CaptionButton button);
|
||||
|
||||
void ReleaseButtons();
|
||||
|
||||
void LeaveButtons();
|
||||
|
||||
void IsWindowMaximized(bool value);
|
||||
|
||||
private:
|
||||
std::optional<CaptionButton> _pressedButton;
|
||||
// 用于避免重复设置状态
|
||||
bool _allInNormal = true;
|
||||
bool _isWindowMaximized = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::App::factory_implementation {
|
||||
|
||||
struct CaptionButtonsControl : CaptionButtonsControlT<CaptionButtonsControl, implementation::CaptionButtonsControl> {
|
||||
};
|
||||
|
||||
}
|
||||
22
src/Magpie.App/CaptionButtonsControl.idl
Normal file
22
src/Magpie.App/CaptionButtonsControl.idl
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
namespace Magpie.App {
|
||||
// 为简单起见,确保这些值与 WM_NCHITTEST 所使用的值相同
|
||||
enum CaptionButton {
|
||||
Minimize = 8, // HTMINBUTTON
|
||||
Maximize = 9, // HTMAXBUTTON
|
||||
Close = 20 // HTCLOSE
|
||||
};
|
||||
|
||||
runtimeclass CaptionButtonsControl : Windows.UI.Xaml.Controls.StackPanel {
|
||||
CaptionButtonsControl();
|
||||
|
||||
Windows.Foundation.Size CaptionButtonSize { get; };
|
||||
|
||||
void HoverButton(CaptionButton button);
|
||||
void PressButton(CaptionButton button);
|
||||
void ReleaseButton(CaptionButton button);
|
||||
void ReleaseButtons();
|
||||
void LeaveButtons();
|
||||
|
||||
void IsWindowMaximized(Boolean value);
|
||||
}
|
||||
}
|
||||
162
src/Magpie.App/CaptionButtonsControl.xaml
Normal file
162
src/Magpie.App/CaptionButtonsControl.xaml
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
<StackPanel x:Class="Magpie.App.CaptionButtonsControl"
|
||||
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:local="using:Magpie.App"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Background="Transparent"
|
||||
Orientation="Horizontal"
|
||||
mc:Ignorable="d">
|
||||
<StackPanel.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<Color x:Key="CaptionButtonForegroundColor">Black</Color>
|
||||
|
||||
<StaticResource x:Key="CaptionButtonForeground"
|
||||
ResourceKey="CaptionButtonForegroundColor" />
|
||||
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
|
||||
ResourceKey="CaptionButtonForegroundColor" />
|
||||
<SolidColorBrush x:Key="CaptionButtonForegroundPressed"
|
||||
Opacity="0.7"
|
||||
Color="{StaticResource CaptionButtonForegroundColor}" />
|
||||
<SolidColorBrush x:Key="CaptionButtonBackgroundPointerOver"
|
||||
Opacity="0.06"
|
||||
Color="{StaticResource CaptionButtonForegroundColor}" />
|
||||
<SolidColorBrush x:Key="CaptionButtonBackgroundPressed"
|
||||
Opacity="0.04"
|
||||
Color="{StaticResource CaptionButtonForegroundColor}" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<Color x:Key="CaptionButtonForegroundColor">White</Color>
|
||||
|
||||
<StaticResource x:Key="CaptionButtonForeground"
|
||||
ResourceKey="CaptionButtonForegroundColor" />
|
||||
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
|
||||
ResourceKey="CaptionButtonForegroundColor" />
|
||||
<SolidColorBrush x:Key="CaptionButtonForegroundPressed"
|
||||
Opacity="0.7"
|
||||
Color="{StaticResource CaptionButtonForegroundColor}" />
|
||||
<SolidColorBrush x:Key="CaptionButtonBackgroundPointerOver"
|
||||
Opacity="0.06"
|
||||
Color="{StaticResource CaptionButtonForegroundColor}" />
|
||||
<SolidColorBrush x:Key="CaptionButtonBackgroundPressed"
|
||||
Opacity="0.04"
|
||||
Color="{StaticResource CaptionButtonForegroundColor}" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Color x:Key="CaptionButtonBackground">Transparent</Color>
|
||||
<x:Double x:Key="CaptionButtonWidth">46</x:Double>
|
||||
<x:Double x:Key="CaptionButtonHeight">32</x:Double>
|
||||
|
||||
<!--
|
||||
Initializes the string to the close button glyph.
|
||||
Each specific button overrides it as needed.
|
||||
-->
|
||||
<x:String x:Key="CaptionButtonGlyph"></x:String>
|
||||
|
||||
<Style x:Key="CaptionButton"
|
||||
TargetType="Button">
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Background" Value="{StaticResource CaptionButtonBackground}" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="Width" Value="{StaticResource CaptionButtonWidth}" />
|
||||
<Setter Property="MinWidth" Value="{StaticResource CaptionButtonWidth}" />
|
||||
<Setter Property="Height" Value="{StaticResource CaptionButtonHeight}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="ButtonBaseElement"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BackgroundSizing="{TemplateBinding BackgroundSizing}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<Viewbox Width="10"
|
||||
Height="10">
|
||||
<FontIcon x:Name="ButtonIcon"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
Foreground="{ThemeResource CaptionButtonForeground}"
|
||||
Glyph="{ThemeResource CaptionButtonGlyph}" />
|
||||
</Viewbox>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ButtonBaseElement.Background" Value="{StaticResource CaptionButtonBackground}" />
|
||||
<Setter Target="ButtonIcon.Foreground" Value="{ThemeResource CaptionButtonForeground}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ButtonBaseElement.Background" Value="{ThemeResource CaptionButtonBackgroundPointerOver}" />
|
||||
<Setter Target="ButtonIcon.Foreground" Value="{ThemeResource CaptionButtonForegroundPointerOver}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ButtonBaseElement.Background" Value="{ThemeResource CaptionButtonBackgroundPressed}" />
|
||||
<Setter Target="ButtonIcon.Foreground" Value="{ThemeResource CaptionButtonForegroundPressed}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Disabled" />
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="MinMaxStates">
|
||||
<VisualState x:Name="WindowStateNormal" />
|
||||
|
||||
<VisualState x:Name="WindowStateMaximized">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ButtonIcon.Glyph" Value="" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</StackPanel.Resources>
|
||||
|
||||
<Button x:Name="MinimizeButton"
|
||||
Style="{StaticResource CaptionButton}">
|
||||
<Button.Resources>
|
||||
<x:String x:Key="CaptionButtonGlyph"></x:String>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
<Button x:Name="MaximizeButton"
|
||||
Style="{StaticResource CaptionButton}">
|
||||
<Button.Resources>
|
||||
<x:String x:Key="CaptionButtonGlyph"></x:String>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
<Button x:Name="CloseButton"
|
||||
Style="{StaticResource CaptionButton}">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
<x:String x:Key="CaptionButtonGlyph"></x:String>
|
||||
|
||||
<Color x:Key="CloseButtonColor">#C42B1C</Color>
|
||||
<SolidColorBrush x:Key="CaptionButtonBackgroundPointerOver"
|
||||
Color="{StaticResource CloseButtonColor}" />
|
||||
<SolidColorBrush x:Key="CaptionButtonBackgroundPressed"
|
||||
Opacity="0.9"
|
||||
Color="{StaticResource CloseButtonColor}" />
|
||||
<SolidColorBrush x:Key="CaptionButtonForegroundPointerOver"
|
||||
Color="White" />
|
||||
<SolidColorBrush x:Key="CaptionButtonForegroundPressed"
|
||||
Opacity="0.7"
|
||||
Color="White" />
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
|
@ -185,7 +185,6 @@ SoftwareBitmap IconHelper::ExtractIconFormWnd(HWND hWnd, uint32_t preferredSize,
|
|||
}
|
||||
|
||||
SoftwareBitmap IconHelper::ExtractIconFromExe(const wchar_t* fileName, uint32_t preferredSize, uint32_t dpi) {
|
||||
preferredSize = (preferredSize + 15) / 16 * 16;
|
||||
preferredSize = (uint32_t)std::lround(preferredSize * dpi / double(USER_DEFAULT_SCREEN_DPI));
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -128,6 +128,10 @@
|
|||
<DependentUpon>CandidateWindowItem.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CaptionButtonsControl.h">
|
||||
<DependentUpon>CaptionButtonsControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ComboBoxHelper.h" />
|
||||
<ClInclude Include="ContentDialogHelper.h" />
|
||||
<ClInclude Include="EffectHelper.h" />
|
||||
|
|
@ -235,6 +239,10 @@
|
|||
<DependentUpon>TextBlockHelper.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TitleBarControl.h">
|
||||
<DependentUpon>TitleBarControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UpdateService.h" />
|
||||
<ClInclude Include="WrapPanel.h">
|
||||
<DependentUpon>WrapPanel.idl</DependentUpon>
|
||||
|
|
@ -275,6 +283,10 @@
|
|||
<DependentUpon>CandidateWindowItem.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CaptionButtonsControl.cpp">
|
||||
<DependentUpon>CaptionButtonsControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ContentDialogHelper.cpp" />
|
||||
<ClCompile Include="EffectParametersViewModel.cpp">
|
||||
<DependentUpon>EffectParametersViewModel.idl</DependentUpon>
|
||||
|
|
@ -381,6 +393,10 @@
|
|||
<DependentUpon>TextBlockHelper.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TitleBarControl.cpp">
|
||||
<DependentUpon>TitleBarControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UpdateService.cpp" />
|
||||
<ClCompile Include="WrapPanel.cpp">
|
||||
<DependentUpon>WrapPanel.idl</DependentUpon>
|
||||
|
|
@ -388,6 +404,14 @@
|
|||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CaptionButtonsControl.idl">
|
||||
<DependentUpon>CaptionButtonsControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</None>
|
||||
<None Include="TitleBarControl.idl">
|
||||
<DependentUpon>TitleBarControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</None>
|
||||
<None Include="KeyVisualState.idl">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
|
|
@ -500,6 +524,9 @@
|
|||
<Page Include="Button.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="CaptionButtonsControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="ProfilePage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
|
|
@ -533,7 +560,7 @@
|
|||
<Page Include="ShortcutDialog.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="ToggleSwitch.xaml">
|
||||
<Page Include="TitleBarControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -224,9 +224,6 @@
|
|||
<Page Include="SettingsGroup.xaml">
|
||||
<Filter>Controls</Filter>
|
||||
</Page>
|
||||
<Page Include="ToggleSwitch.xaml">
|
||||
<Filter>Styles</Filter>
|
||||
</Page>
|
||||
<Page Include="KeyVisual.xaml">
|
||||
<Filter>Controls</Filter>
|
||||
</Page>
|
||||
|
|
@ -245,6 +242,12 @@
|
|||
<Page Include="Button.xaml">
|
||||
<Filter>Styles</Filter>
|
||||
</Page>
|
||||
<Page Include="TitleBarControl.xaml">
|
||||
<Filter>Controls</Filter>
|
||||
</Page>
|
||||
<Page Include="CaptionButtonsControl.xaml">
|
||||
<Filter>Controls</Filter>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ void MainPage::InitializeComponent() {
|
|||
MUXC::BackdropMaterial::SetApplyToRootOrPageBackground(*this, true);
|
||||
}
|
||||
|
||||
IVector<IInspectable> navMenuItems = __super::RootNavigationView().MenuItems();
|
||||
IVector<IInspectable> navMenuItems = RootNavigationView().MenuItems();
|
||||
for (const Profile& profile : AppSettings::Get().Profiles()) {
|
||||
MUXC::NavigationViewItem item;
|
||||
item.Content(box_value(profile.name));
|
||||
|
|
@ -86,28 +86,14 @@ void MainPage::InitializeComponent() {
|
|||
|
||||
navMenuItems.InsertAt(navMenuItems.Size() - 1, item);
|
||||
}
|
||||
|
||||
// Win10 里启动时有一个 ToggleSwitch 的动画 bug,这里展示页面切换动画掩盖
|
||||
if (!osVersion.IsWin11()) {
|
||||
ContentFrame().Navigate(winrt::xaml_typename<Controls::Page>());
|
||||
}
|
||||
}
|
||||
|
||||
void MainPage::Loaded(IInspectable const&, RoutedEventArgs const&) {
|
||||
MUXC::NavigationView nv = __super::RootNavigationView();
|
||||
|
||||
if (nv.DisplayMode() == MUXC::NavigationViewDisplayMode::Minimal) {
|
||||
nv.IsPaneOpen(true);
|
||||
}
|
||||
MUXC::NavigationView nv = RootNavigationView();
|
||||
|
||||
// 修复 WinUI 的汉堡菜单的尺寸 bug
|
||||
nv.PaneDisplayMode(MUXC::NavigationViewPaneDisplayMode::Auto);
|
||||
|
||||
// 消除焦点框
|
||||
IsTabStop(true);
|
||||
Focus(FocusState::Programmatic);
|
||||
IsTabStop(false);
|
||||
|
||||
// 设置 NavigationView 内的 Tooltip 的主题
|
||||
XamlUtils::UpdateThemeOfTooltips(*this, ActualTheme());
|
||||
}
|
||||
|
|
@ -119,7 +105,7 @@ void MainPage::NavigationView_SelectionChanged(
|
|||
auto contentFrame = ContentFrame();
|
||||
|
||||
if (args.IsSettingsSelected()) {
|
||||
contentFrame.Navigate(winrt::xaml_typename<SettingsPage>());
|
||||
contentFrame.Navigate(xaml_typename<SettingsPage>());
|
||||
} else {
|
||||
IInspectable selectedItem = args.SelectedItem();
|
||||
if (!selectedItem) {
|
||||
|
|
@ -132,22 +118,22 @@ void MainPage::NavigationView_SelectionChanged(
|
|||
hstring tagStr = unbox_value<hstring>(tag);
|
||||
Interop::TypeName typeName;
|
||||
if (tagStr == L"Home") {
|
||||
typeName = winrt::xaml_typename<HomePage>();
|
||||
typeName = xaml_typename<HomePage>();
|
||||
} else if (tagStr == L"ScalingConfiguration") {
|
||||
typeName = winrt::xaml_typename<ScalingConfigurationPage>();
|
||||
typeName = xaml_typename<ScalingConfigurationPage>();
|
||||
} else if (tagStr == L"About") {
|
||||
typeName = winrt::xaml_typename<AboutPage>();
|
||||
typeName = xaml_typename<AboutPage>();
|
||||
} else {
|
||||
typeName = winrt::xaml_typename<HomePage>();
|
||||
typeName = xaml_typename<HomePage>();
|
||||
}
|
||||
|
||||
contentFrame.Navigate(typeName);
|
||||
} else {
|
||||
// 缩放配置页面
|
||||
MUXC::NavigationView nv = __super::RootNavigationView();
|
||||
MUXC::NavigationView nv = RootNavigationView();
|
||||
uint32_t index;
|
||||
if (nv.MenuItems().IndexOf(nv.SelectedItem(), index)) {
|
||||
contentFrame.Navigate(winrt::xaml_typename<ProfilePage>(), box_value((int)index - 4));
|
||||
contentFrame.Navigate(xaml_typename<ProfilePage>(), box_value((int)index - 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +149,7 @@ void MainPage::NavigationView_PaneOpening(MUXC::NavigationView const&, IInspecta
|
|||
|
||||
// UpdateThemeOfTooltips 中使用的 hack 会使 NavigationViewItem 在展开时不会自动删除 Tooltip
|
||||
// 因此这里手动删除
|
||||
const MUXC::NavigationView& nv = __super::RootNavigationView();
|
||||
const MUXC::NavigationView& nv = RootNavigationView();
|
||||
for (const IInspectable& item : nv.MenuItems()) {
|
||||
ToolTipService::SetToolTip(item.as<DependencyObject>(), nullptr);
|
||||
}
|
||||
|
|
@ -176,7 +162,18 @@ void MainPage::NavigationView_PaneClosing(MUXC::NavigationView const&, MUXC::Nav
|
|||
XamlUtils::UpdateThemeOfTooltips(*this, ActualTheme());
|
||||
}
|
||||
|
||||
void MainPage::NavigationView_DisplayModeChanged(MUXC::NavigationView const&, MUXC::NavigationViewDisplayModeChangedEventArgs const&) {
|
||||
void MainPage::NavigationView_DisplayModeChanged(MUXC::NavigationView const& nv, MUXC::NavigationViewDisplayModeChangedEventArgs const&) {
|
||||
bool isExpanded = nv.DisplayMode() == MUXC::NavigationViewDisplayMode::Expanded;
|
||||
nv.IsPaneToggleButtonVisible(!isExpanded);
|
||||
if (isExpanded) {
|
||||
nv.IsPaneOpen(true);
|
||||
}
|
||||
|
||||
// HACK!
|
||||
// 使导航栏的可滚动区域不会覆盖标题栏
|
||||
FrameworkElement menuItemsScrollViewer = nv.GetTemplateChild(L"MenuItemsScrollViewer").as<FrameworkElement>();
|
||||
menuItemsScrollViewer.Margin({ 0,isExpanded ? TitleBar().ActualHeight() : 0.0,0,0});
|
||||
|
||||
XamlUtils::UpdateThemeOfTooltips(*this, ActualTheme());
|
||||
}
|
||||
|
||||
|
|
@ -188,8 +185,6 @@ fire_and_forget MainPage::NavigationView_ItemInvoked(MUXC::NavigationView const&
|
|||
|
||||
// 同步调用 ShowAt 有时会失败
|
||||
co_await Dispatcher().TryRunAsync(CoreDispatcherPriority::Normal, [this]() {
|
||||
// 仅限 Win10:导航栏处于 Minimal 状态时会导致 Flyout 不在正确位置弹出
|
||||
// 有一个修复方法,但会导致性能损失
|
||||
NewProfileFlyout().ShowAt(NewProfileNavigationViewItem());
|
||||
});
|
||||
}
|
||||
|
|
@ -354,7 +349,7 @@ void MainPage::_ProfileService_ProfileAdded(Profile& profile) {
|
|||
item.Icon(FontIcon());
|
||||
_LoadIcon(item, profile);
|
||||
|
||||
IVector<IInspectable> navMenuItems = __super::RootNavigationView().MenuItems();
|
||||
IVector<IInspectable> navMenuItems = RootNavigationView().MenuItems();
|
||||
navMenuItems.InsertAt(navMenuItems.Size() - 1, item);
|
||||
RootNavigationView().SelectedItem(item);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ struct MainPage : MainPageT<MainPage> {
|
|||
|
||||
void NavigationView_PaneClosing(MUXC::NavigationView const&, MUXC::NavigationViewPaneClosingEventArgs const&);
|
||||
|
||||
void NavigationView_DisplayModeChanged(MUXC::NavigationView const&, MUXC::NavigationViewDisplayModeChangedEventArgs const&);
|
||||
void NavigationView_DisplayModeChanged(MUXC::NavigationView const& nv, MUXC::NavigationViewDisplayModeChangedEventArgs const&);
|
||||
|
||||
fire_and_forget NavigationView_ItemInvoked(MUXC::NavigationView const&, MUXC::NavigationViewItemInvokedEventArgs const& args);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ namespace Magpie.App {
|
|||
MainPage();
|
||||
|
||||
Microsoft.UI.Xaml.Controls.NavigationView RootNavigationView { get; };
|
||||
TitleBarControl TitleBar { get; };
|
||||
|
||||
NewProfileViewModel NewProfileViewModel { get; };
|
||||
|
||||
void NavigateToAboutPage();
|
||||
|
|
|
|||
|
|
@ -7,151 +7,155 @@
|
|||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
Loaded="Loaded"
|
||||
mc:Ignorable="d">
|
||||
<muxc:NavigationView Name="RootNavigationView"
|
||||
DisplayModeChanged="NavigationView_DisplayModeChanged"
|
||||
IsBackButtonVisible="Collapsed"
|
||||
ItemInvoked="NavigationView_ItemInvoked"
|
||||
PaneClosing="NavigationView_PaneClosing"
|
||||
PaneOpening="NavigationView_PaneOpening"
|
||||
SelectionChanged="NavigationView_SelectionChanged">
|
||||
<muxc:NavigationView.Resources>
|
||||
<SolidColorBrush x:Key="NavigationViewContentBackground"
|
||||
Color="Transparent" />
|
||||
<SolidColorBrush x:Key="NavigationViewContentGridBorderBrush"
|
||||
Color="Transparent" />
|
||||
</muxc:NavigationView.Resources>
|
||||
<Grid>
|
||||
<local:TitleBarControl x:Name="TitleBar"
|
||||
Canvas.ZIndex="1" />
|
||||
<muxc:NavigationView Name="RootNavigationView"
|
||||
Canvas.ZIndex="0"
|
||||
CompactModeThresholdWidth="0"
|
||||
DisplayModeChanged="NavigationView_DisplayModeChanged"
|
||||
IsBackButtonVisible="Collapsed"
|
||||
ItemInvoked="NavigationView_ItemInvoked"
|
||||
PaneClosing="NavigationView_PaneClosing"
|
||||
PaneOpening="NavigationView_PaneOpening"
|
||||
SelectionChanged="NavigationView_SelectionChanged">
|
||||
<muxc:NavigationView.Resources>
|
||||
<CornerRadius x:Key="NavigationViewContentGridCornerRadius">0</CornerRadius>
|
||||
<Thickness x:Key="NavigationViewContentGridBorderThickness">1,0,0,0</Thickness>
|
||||
</muxc:NavigationView.Resources>
|
||||
|
||||
<muxc:NavigationView.MenuItems>
|
||||
<muxc:NavigationViewItem x:Uid="Main_Home"
|
||||
IsSelected="True"
|
||||
Tag="Home">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
<muxc:NavigationViewItem x:Uid="Main_ScalingConfiguration"
|
||||
Tag="ScalingConfiguration">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
<muxc:NavigationViewItemHeader x:Uid="Main_Profiles" />
|
||||
<muxc:NavigationViewItem x:Uid="Main_Defaults">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
<muxc:NavigationViewItem x:Name="NewProfileNavigationViewItem"
|
||||
x:Uid="Main_NewProfile"
|
||||
Icon="Add"
|
||||
SelectsOnInvoked="False">
|
||||
<FlyoutBase.AttachedFlyout>
|
||||
<Flyout x:Name="NewProfileFlyout"
|
||||
Placement="Right">
|
||||
<Grid>
|
||||
<ContentControl Grid.RowSpan="2"
|
||||
Visibility="{x:Bind NewProfileViewModel.IsNoCandidateWindow, Mode=OneWay}"
|
||||
<muxc:NavigationView.MenuItems>
|
||||
<muxc:NavigationViewItem x:Uid="Main_Home"
|
||||
IsSelected="True"
|
||||
Tag="Home">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
<muxc:NavigationViewItem x:Uid="Main_ScalingConfiguration"
|
||||
Tag="ScalingConfiguration">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
<muxc:NavigationViewItemHeader x:Uid="Main_Profiles" />
|
||||
<muxc:NavigationViewItem x:Uid="Main_Defaults">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
<muxc:NavigationViewItem x:Name="NewProfileNavigationViewItem"
|
||||
x:Uid="Main_NewProfile"
|
||||
Icon="Add"
|
||||
SelectsOnInvoked="False">
|
||||
<FlyoutBase.AttachedFlyout>
|
||||
<Flyout x:Name="NewProfileFlyout"
|
||||
Placement="Right">
|
||||
<Grid>
|
||||
<ContentControl Grid.RowSpan="2"
|
||||
MinWidth="240"
|
||||
MinHeight="170"
|
||||
Margin="10"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center">
|
||||
<StackPanel Orientation="Vertical"
|
||||
VerticalContentAlignment="Center"
|
||||
Visibility="{x:Bind NewProfileViewModel.IsNoCandidateWindow, Mode=OneWay}">
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="20">
|
||||
<FontIcon HorizontalAlignment="Center"
|
||||
<FontIcon HorizontalAlignment="Center"
|
||||
FontSize="40"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="Main_NewProfileFlyout_NoCandidateWindow"
|
||||
<TextBlock x:Uid="Main_NewProfileFlyout_NoCandidateWindow"
|
||||
HorizontalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</ContentControl>
|
||||
|
||||
<StackPanel Orientation="Vertical"
|
||||
Visibility="{x:Bind NewProfileViewModel.IsAnyCandidateWindow, Mode=OneWay}">
|
||||
<TextBlock x:Uid="Main_NewProfileFlyout_Title"
|
||||
Margin="0,5,0,20"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold" />
|
||||
<StackPanel Width="280"
|
||||
Orientation="Vertical"
|
||||
Spacing="15">
|
||||
<ComboBox x:Name="CandidateWindowsComboBox"
|
||||
x:Uid="Main_NewProfileFlyout_ComboBox"
|
||||
Margin="0,0,0,10"
|
||||
HorizontalAlignment="Stretch"
|
||||
DropDownOpened="ComboBox_DropDownOpened"
|
||||
ItemsSource="{x:Bind NewProfileViewModel.CandidateWindows, Mode=OneWay}"
|
||||
SelectedIndex="{x:Bind NewProfileViewModel.CandidateWindowIndex, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:CandidateWindowItem">
|
||||
<Grid MaxWidth="450"
|
||||
HorizontalAlignment="Stretch"
|
||||
ColumnSpacing="15">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentPresenter Grid.Column="0"
|
||||
Content="{x:Bind Icon, Mode=OneWay}" />
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
local:TextBlockHelper.IsAutoTooltip="True"
|
||||
Text="{x:Bind Title, Mode=OneWay}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<TextBlock x:Uid="Main_NewProfileFlyout_Name" />
|
||||
<TextBox Height="32"
|
||||
HorizontalAlignment="Stretch"
|
||||
KeyDown="NewProfileNameTextBox_KeyDown"
|
||||
Text="{x:Bind NewProfileViewModel.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<TextBlock x:Uid="Main_NewProfileFlyout_CopyFrom" />
|
||||
<ComboBox x:Name="ProfilesComboBox"
|
||||
</ContentControl>
|
||||
|
||||
<StackPanel Orientation="Vertical"
|
||||
Visibility="{x:Bind NewProfileViewModel.IsAnyCandidateWindow, Mode=OneWay}">
|
||||
<TextBlock x:Uid="Main_NewProfileFlyout_Title"
|
||||
Margin="0,5,0,20"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold" />
|
||||
<StackPanel Width="280"
|
||||
Orientation="Vertical"
|
||||
Spacing="15">
|
||||
<ComboBox x:Name="CandidateWindowsComboBox"
|
||||
x:Uid="Main_NewProfileFlyout_ComboBox"
|
||||
Margin="0,0,0,10"
|
||||
HorizontalAlignment="Stretch"
|
||||
DropDownOpened="ComboBox_DropDownOpened"
|
||||
ItemsSource="{x:Bind NewProfileViewModel.Profiles, Mode=OneWay}"
|
||||
SelectedIndex="{x:Bind NewProfileViewModel.ProfileIndex, Mode=TwoWay}">
|
||||
ItemsSource="{x:Bind NewProfileViewModel.CandidateWindows, Mode=OneWay}"
|
||||
SelectedIndex="{x:Bind NewProfileViewModel.CandidateWindowIndex, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock MaxWidth="300"
|
||||
HorizontalAlignment="Stretch"
|
||||
local:TextBlockHelper.IsAutoTooltip="True"
|
||||
Text="{Binding}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<DataTemplate x:DataType="local:CandidateWindowItem">
|
||||
<Grid MaxWidth="450"
|
||||
HorizontalAlignment="Stretch"
|
||||
ColumnSpacing="15">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentPresenter Grid.Column="0"
|
||||
Content="{x:Bind Icon, Mode=OneWay}" />
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
local:TextBlockHelper.IsAutoTooltip="True"
|
||||
Text="{x:Bind Title, Mode=OneWay}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<TextBlock x:Uid="Main_NewProfileFlyout_Name" />
|
||||
<TextBox Height="32"
|
||||
HorizontalAlignment="Stretch"
|
||||
KeyDown="NewProfileNameTextBox_KeyDown"
|
||||
Text="{x:Bind NewProfileViewModel.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<TextBlock x:Uid="Main_NewProfileFlyout_CopyFrom" />
|
||||
<ComboBox x:Name="ProfilesComboBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
DropDownOpened="ComboBox_DropDownOpened"
|
||||
ItemsSource="{x:Bind NewProfileViewModel.Profiles, Mode=OneWay}"
|
||||
SelectedIndex="{x:Bind NewProfileViewModel.ProfileIndex, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock MaxWidth="300"
|
||||
HorizontalAlignment="Stretch"
|
||||
local:TextBlockHelper.IsAutoTooltip="True"
|
||||
Text="{Binding}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<Button x:Uid="Main_NewProfileFlyout_Create"
|
||||
MinWidth="100"
|
||||
Margin="0,15,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
Click="NewProfileConfirmButton_Click"
|
||||
IsEnabled="{x:Bind NewProfileViewModel.IsConfirmButtonEnabled, Mode=OneWay}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
<Button x:Uid="Main_NewProfileFlyout_Create"
|
||||
MinWidth="100"
|
||||
Margin="0,15,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
Click="NewProfileConfirmButton_Click"
|
||||
IsEnabled="{x:Bind NewProfileViewModel.IsConfirmButtonEnabled, Mode=OneWay}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Flyout>
|
||||
</FlyoutBase.AttachedFlyout>
|
||||
</muxc:NavigationViewItem>
|
||||
</muxc:NavigationView.MenuItems>
|
||||
<muxc:NavigationView.FooterMenuItems>
|
||||
<muxc:NavigationViewItem x:Uid="Main_About"
|
||||
Tag="About">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
</muxc:NavigationView.FooterMenuItems>
|
||||
</Grid>
|
||||
</Flyout>
|
||||
</FlyoutBase.AttachedFlyout>
|
||||
</muxc:NavigationViewItem>
|
||||
</muxc:NavigationView.MenuItems>
|
||||
<muxc:NavigationView.FooterMenuItems>
|
||||
<muxc:NavigationViewItem x:Uid="Main_About"
|
||||
Tag="About">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
</muxc:NavigationView.FooterMenuItems>
|
||||
|
||||
<Frame x:Name="ContentFrame" />
|
||||
</muxc:NavigationView>
|
||||
<Frame x:Name="ContentFrame" />
|
||||
</muxc:NavigationView>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
#include "PageFrame.g.cpp"
|
||||
#endif
|
||||
#include "XamlUtils.h"
|
||||
#include "Win32Utils.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
|
@ -44,14 +43,6 @@ const DependencyProperty PageFrame::MainContentProperty = DependencyProperty::Re
|
|||
|
||||
void PageFrame::Loading(FrameworkElement const&, IInspectable const&) {
|
||||
_Update();
|
||||
|
||||
MainPage mainPage = XamlRoot().Content().as<Magpie::App::MainPage>();
|
||||
_rootNavigationView = mainPage.RootNavigationView();
|
||||
_displayModeChangedRevoker = _rootNavigationView.DisplayModeChanged(
|
||||
auto_revoke,
|
||||
[&](auto const&, auto const&) { _UpdateHeaderStyle(); }
|
||||
);
|
||||
_UpdateHeaderStyle();
|
||||
}
|
||||
|
||||
void PageFrame::Loaded(IInspectable const&, RoutedEventArgs const&) {
|
||||
|
|
@ -68,34 +59,15 @@ void PageFrame::ScrollViewer_ViewChanging(IInspectable const&, ScrollViewerViewC
|
|||
}
|
||||
|
||||
void PageFrame::_Update() {
|
||||
TitleTextBlock().Visibility(Title().empty() ? Visibility::Collapsed : Visibility::Visible);
|
||||
HeaderActionPresenter().Visibility(HeaderAction() ? Visibility::Visible : Visibility::Collapsed);
|
||||
|
||||
if (_rootNavigationView) {
|
||||
_UpdateHeaderStyle();
|
||||
}
|
||||
}
|
||||
|
||||
void PageFrame::_UpdateHeaderStyle() {
|
||||
TextBlock textBlock = TitleTextBlock();
|
||||
|
||||
IconElement icon = Icon();
|
||||
if (icon) {
|
||||
icon.Width(28);
|
||||
icon.Height(28);
|
||||
}
|
||||
|
||||
if (_rootNavigationView.DisplayMode() == MUXC::NavigationViewDisplayMode::Minimal) {
|
||||
HeaderGrid().Margin({ 28, 8, 0, 0 });
|
||||
IconContainer().Visibility(Visibility::Collapsed);
|
||||
textBlock.FontSize(20);
|
||||
HeaderActionPresenter().Margin({ 0,-3,0,-3 });
|
||||
} else {
|
||||
HeaderGrid().Margin({ 0, Win32Utils::GetOSVersion().Is22H2OrNewer() ? 22.0 : 42.0, 0, 0});
|
||||
IconContainer().Visibility(icon ? Visibility::Visible : Visibility::Collapsed);
|
||||
textBlock.FontSize(30);
|
||||
HeaderActionPresenter().Margin({ 0,0,0,-4 });
|
||||
}
|
||||
IconContainer().Visibility(icon ? Visibility::Visible : Visibility::Collapsed);
|
||||
}
|
||||
|
||||
void PageFrame::_OnTitleChanged(DependencyObject const& sender, DependencyPropertyChangedEventArgs const&) {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ struct PageFrame : PageFrameT<PageFrame> {
|
|||
void ScrollViewer_PointerPressed(IInspectable const&, Input::PointerRoutedEventArgs const&);
|
||||
void ScrollViewer_ViewChanging(IInspectable const&, Controls::ScrollViewerViewChangingEventArgs const&);
|
||||
|
||||
event_token PropertyChanged(Data::PropertyChangedEventHandler const& value) {
|
||||
event_token PropertyChanged(PropertyChangedEventHandler const& value) {
|
||||
return _propertyChangedEvent.add(value);
|
||||
}
|
||||
|
||||
|
|
@ -64,12 +64,7 @@ private:
|
|||
|
||||
void _Update();
|
||||
|
||||
void _UpdateHeaderStyle();
|
||||
|
||||
event<Data::PropertyChangedEventHandler> _propertyChangedEvent;
|
||||
|
||||
Microsoft::UI::Xaml::Controls::NavigationView _rootNavigationView{ nullptr };
|
||||
Microsoft::UI::Xaml::Controls::NavigationView::DisplayModeChanged_revoker _displayModeChangedRevoker{};
|
||||
event<PropertyChangedEventHandler> _propertyChangedEvent;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,18 +12,16 @@
|
|||
<x:Double x:Key="PageMaxWidth">1000</x:Double>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Margin="20,0,0,0">
|
||||
<Grid Margin="40,54,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel x:Name="HeaderGrid"
|
||||
Grid.Row="0"
|
||||
VerticalAlignment="Top"
|
||||
ChildrenTransitions="{StaticResource SettingsCardsAnimations}">
|
||||
<StackPanel Grid.Row="0"
|
||||
VerticalAlignment="Top">
|
||||
<Grid MaxWidth="{StaticResource PageMaxWidth}"
|
||||
Margin="0,0,20,16">
|
||||
Margin="0,0,40,16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
|
|
@ -31,12 +29,12 @@
|
|||
</Grid.ColumnDefinitions>
|
||||
<ContentControl x:Name="IconContainer"
|
||||
Grid.Column="0"
|
||||
Margin="0,8,16,0"
|
||||
Margin="0,6.5,16,0"
|
||||
Content="{x:Bind Icon, Mode=OneWay}"
|
||||
IsTabStop="False" />
|
||||
<TextBlock x:Name="TitleTextBlock"
|
||||
Grid.Column="1"
|
||||
<TextBlock Grid.Column="1"
|
||||
local:TextBlockHelper.IsAutoTooltip="True"
|
||||
FontSize="28"
|
||||
FontWeight="SemiBold"
|
||||
Text="{x:Bind Title, Mode=OneWay}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
|
@ -86,10 +84,10 @@
|
|||
PointerPressed="ScrollViewer_PointerPressed"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
ViewChanging="ScrollViewer_ViewChanging">
|
||||
<StackPanel ChildrenTransitions="{StaticResource SettingsCardsAnimations}">
|
||||
<StackPanel>
|
||||
<ContentControl Grid.Column="0"
|
||||
MaxWidth="{StaticResource PageMaxWidth}"
|
||||
Margin="0,0,20,20"
|
||||
Margin="0,0,40,20"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
Content="{x:Bind MainContent, Mode=OneWay}"
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ struct SettingsCard : SettingsCardT<SettingsCard> {
|
|||
void IsEnabledChanged(IInspectable const&, DependencyPropertyChangedEventArgs const&);
|
||||
void Loading(FrameworkElement const&, IInspectable const&);
|
||||
|
||||
event_token PropertyChanged(Data::PropertyChangedEventHandler const& value) {
|
||||
event_token PropertyChanged(PropertyChangedEventHandler const& value) {
|
||||
return _propertyChangedEvent.add(value);
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ private:
|
|||
|
||||
void _SetEnabledState();
|
||||
|
||||
event<Data::PropertyChangedEventHandler> _propertyChangedEvent;
|
||||
event<PropertyChangedEventHandler> _propertyChangedEvent;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
namespace Magpie.App {
|
||||
[Windows.UI.Xaml.Markup.ContentProperty("RawTitle")]
|
||||
[Windows.UI.Xaml.TemplateVisualState("Normal", "CommonStates")]
|
||||
[Windows.UI.Xaml.TemplateVisualState("Disabled", "CommonStates")]
|
||||
runtimeclass SettingsCard : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
SettingsCard();
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ struct SettingsGroup : SettingsGroupT<SettingsGroup> {
|
|||
void IsEnabledChanged(IInspectable const&, DependencyPropertyChangedEventArgs const&);
|
||||
void Loading(FrameworkElement const&, IInspectable const&);
|
||||
|
||||
event_token PropertyChanged(Data::PropertyChangedEventHandler const& value) {
|
||||
event_token PropertyChanged(PropertyChangedEventHandler const& value) {
|
||||
return _propertyChangedEvent.add(value);
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ private:
|
|||
|
||||
void _SetEnabledState();
|
||||
|
||||
event<Data::PropertyChangedEventHandler> _propertyChangedEvent;
|
||||
event<PropertyChangedEventHandler> _propertyChangedEvent;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
namespace Magpie.App {
|
||||
runtimeclass ShortcutControl : Windows.UI.Xaml.Controls.UserControl {
|
||||
runtimeclass ShortcutControl : Windows.UI.Xaml.Controls.Grid {
|
||||
ShortcutControl();
|
||||
|
||||
ShortcutAction Action;
|
||||
|
|
|
|||
|
|
@ -1,66 +1,62 @@
|
|||
<!-- 移植自 https://github.com/microsoft/PowerToys/blob/35bfb0f83e5fc08cc04398e7aa98d77774412d3f/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutControl.xaml -->
|
||||
<UserControl x:Class="Magpie.App.ShortcutControl"
|
||||
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:local="using:Magpie.App"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400"
|
||||
mc:Ignorable="d">
|
||||
<Grid Margin="0,-6,0,-6"
|
||||
HorizontalAlignment="Right">
|
||||
<Button Padding="0"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Click="EditButton_Click">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default" />
|
||||
<ResourceDictionary x:Key="HighContrast" />
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="ButtonBackground"
|
||||
ResourceKey="SubtleFillColorTransparentBrush" />
|
||||
<StaticResource x:Key="ButtonBackgroundPointerOver"
|
||||
ResourceKey="SubtleFillColorSecondaryBrush" />
|
||||
<StaticResource x:Key="ButtonBackgroundPressed"
|
||||
ResourceKey="SubtleFillColorTertiaryBrush" />
|
||||
<StaticResource x:Key="ButtonBorderBrush"
|
||||
ResourceKey="ControlFillColorTransparentBrush" />
|
||||
<StaticResource x:Key="ButtonBorderBrushPointerOver"
|
||||
ResourceKey="ControlFillColorTransparentBrush" />
|
||||
<StaticResource x:Key="ButtonBorderBrushPressed"
|
||||
ResourceKey="ControlFillColorTransparentBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
<StackPanel Margin="6,0,6,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="16">
|
||||
<ItemsControl x:Name="KeysControl"
|
||||
VerticalAlignment="Center"
|
||||
IsTabStop="False">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Spacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:KeyVisualState">
|
||||
<local:KeyVisual Key="{x:Bind Key, Mode=OneTime}"
|
||||
VerticalAlignment="Center"
|
||||
IsError="{x:Bind IsError, Mode=OneTime}"
|
||||
IsTabStop="False"
|
||||
VisualType="Small" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<FontIcon FontSize="16"
|
||||
Glyph="" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
<Grid x:Class="Magpie.App.ShortcutControl"
|
||||
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:local="using:Magpie.App"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<Button Margin="0,-6,0,-6"
|
||||
Padding="0"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Click="EditButton_Click">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default" />
|
||||
<ResourceDictionary x:Key="HighContrast" />
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="ButtonBackground"
|
||||
ResourceKey="SubtleFillColorTransparentBrush" />
|
||||
<StaticResource x:Key="ButtonBackgroundPointerOver"
|
||||
ResourceKey="SubtleFillColorSecondaryBrush" />
|
||||
<StaticResource x:Key="ButtonBackgroundPressed"
|
||||
ResourceKey="SubtleFillColorTertiaryBrush" />
|
||||
<StaticResource x:Key="ButtonBorderBrush"
|
||||
ResourceKey="ControlFillColorTransparentBrush" />
|
||||
<StaticResource x:Key="ButtonBorderBrushPointerOver"
|
||||
ResourceKey="ControlFillColorTransparentBrush" />
|
||||
<StaticResource x:Key="ButtonBorderBrushPressed"
|
||||
ResourceKey="ControlFillColorTransparentBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
<StackPanel Margin="6,0,6,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="16">
|
||||
<ItemsControl x:Name="KeysControl"
|
||||
VerticalAlignment="Center"
|
||||
IsTabStop="False">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Spacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:KeyVisualState">
|
||||
<local:KeyVisual Key="{x:Bind Key, Mode=OneTime}"
|
||||
VerticalAlignment="Center"
|
||||
IsError="{x:Bind IsError, Mode=OneTime}"
|
||||
IsTabStop="False"
|
||||
VisualType="Small" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<FontIcon FontSize="16"
|
||||
Glyph="" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
namespace Magpie.App {
|
||||
runtimeclass ShortcutDialog : Windows.UI.Xaml.Controls.UserControl {
|
||||
runtimeclass ShortcutDialog : Windows.UI.Xaml.Controls.Grid {
|
||||
ShortcutDialog();
|
||||
|
||||
ShortcutError Error;
|
||||
|
|
|
|||
|
|
@ -1,82 +1,82 @@
|
|||
<UserControl x:Class="Magpie.App.ShortcutDialog"
|
||||
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:local="using:Magpie.App"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d">
|
||||
<Grid MinWidth="498"
|
||||
MinHeight="220">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition MinHeight="110" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Class="Magpie.App.ShortcutDialog"
|
||||
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:local="using:Magpie.App"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
MinWidth="498"
|
||||
MinHeight="220"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<TextBlock x:Uid="ShortcutDialog_Description"
|
||||
Grid.Row="0" />
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition MinHeight="110" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ItemsControl x:Name="KeysControl"
|
||||
Grid.Row="1"
|
||||
Height="56"
|
||||
Margin="0,64,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalContentAlignment="Center">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Spacing="8" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:KeyVisualState">
|
||||
<local:KeyVisual Key="{x:Bind Key, Mode=OneTime}"
|
||||
Height="56"
|
||||
IsError="{x:Bind IsError, Mode=OneTime}"
|
||||
IsTabStop="False"
|
||||
VisualType="Large" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<TextBlock x:Uid="ShortcutDialog_Description"
|
||||
Grid.Row="0" />
|
||||
|
||||
<StackPanel Grid.Row="2"
|
||||
Margin="0,24,0,0"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<Grid Height="36">
|
||||
<Border x:Name="WarningBanner"
|
||||
Margin="-2,0,0,0"
|
||||
Padding="8"
|
||||
Background="{ThemeResource InfoBarErrorSeverityBackgroundBrush}"
|
||||
BorderBrush="{ThemeResource InfoBarBorderBrush}"
|
||||
BorderThickness="{ThemeResource InfoBarBorderThickness}"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ItemsControl x:Name="KeysControl"
|
||||
Grid.Row="1"
|
||||
Height="56"
|
||||
Margin="0,64,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalContentAlignment="Center">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Spacing="8" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:KeyVisualState">
|
||||
<local:KeyVisual Key="{x:Bind Key, Mode=OneTime}"
|
||||
Height="56"
|
||||
IsError="{x:Bind IsError, Mode=OneTime}"
|
||||
IsTabStop="False"
|
||||
VisualType="Large" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<muxc:InfoBadge Margin="2,0,12,0"
|
||||
Style="{StaticResource CriticalIconInfoBadgeStyle}" />
|
||||
<StackPanel Grid.Row="2"
|
||||
Margin="0,24,0,0"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<Grid Height="36">
|
||||
<Border x:Name="WarningBanner"
|
||||
Margin="-2,0,0,0"
|
||||
Padding="8"
|
||||
Background="{ThemeResource InfoBarErrorSeverityBackgroundBrush}"
|
||||
BorderBrush="{ThemeResource InfoBarBorderBrush}"
|
||||
BorderThickness="{ThemeResource InfoBarBorderThickness}"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock x:Name="InvalidShortcutWarningLabel"
|
||||
Grid.Column="1"
|
||||
Margin="0,-1,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="{ThemeResource InfoBarTitleFontWeight}"
|
||||
Foreground="{ThemeResource InfoBarTitleForeground}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
<TextBlock x:Uid="ShortcutDialog_Tip"
|
||||
HorizontalAlignment="Left"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
<muxc:InfoBadge Margin="2,0,12,0"
|
||||
Style="{StaticResource CriticalIconInfoBadgeStyle}" />
|
||||
|
||||
<TextBlock x:Name="InvalidShortcutWarningLabel"
|
||||
Grid.Column="1"
|
||||
Margin="0,-1,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="{ThemeResource InfoBarTitleFontWeight}"
|
||||
Foreground="{ThemeResource InfoBarTitleForeground}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
<TextBlock x:Uid="ShortcutDialog_Tip"
|
||||
HorizontalAlignment="Left"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
|
|
|||
41
src/Magpie.App/TitlebarControl.cpp
Normal file
41
src/Magpie.App/TitlebarControl.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#include "pch.h"
|
||||
#include "TitleBarControl.h"
|
||||
#if __has_include("TitleBarControl.g.cpp")
|
||||
#include "TitleBarControl.g.cpp"
|
||||
#endif
|
||||
#include "IconHelper.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Windows::UI::Xaml::Media::Imaging;
|
||||
|
||||
namespace winrt::Magpie::App::implementation {
|
||||
|
||||
TitleBarControl::TitleBarControl() {
|
||||
// 异步加载 Logo
|
||||
[](TitleBarControl* that)->fire_and_forget {
|
||||
wchar_t exePath[MAX_PATH];
|
||||
GetModuleFileName(NULL, exePath, MAX_PATH);
|
||||
|
||||
auto weakThis = that->get_weak();
|
||||
|
||||
SoftwareBitmapSource bitmap;
|
||||
co_await bitmap.SetBitmapAsync(IconHelper::ExtractIconFromExe(exePath, 40, USER_DEFAULT_SCREEN_DPI));
|
||||
|
||||
if (!weakThis.get()) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
that->_logo = std::move(bitmap);
|
||||
that->_propertyChangedEvent(*that, PropertyChangedEventArgs(L"Logo"));
|
||||
}(this);
|
||||
}
|
||||
|
||||
void TitleBarControl::Loading(FrameworkElement const&, IInspectable const&) {
|
||||
MUXC::NavigationView rootNavigationView = Application::Current().as<App>().MainPage().RootNavigationView();
|
||||
rootNavigationView.DisplayModeChanged([this](const auto&, const auto& args) {
|
||||
bool expanded = args.DisplayMode() == MUXC::NavigationViewDisplayMode::Expanded;
|
||||
VisualStateManager::GoToState(*this, expanded ? L"Expanded" : L"Compact", true);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
33
src/Magpie.App/TitlebarControl.h
Normal file
33
src/Magpie.App/TitlebarControl.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
#include "TitleBarControl.g.h"
|
||||
|
||||
namespace winrt::Magpie::App::implementation {
|
||||
struct TitleBarControl : TitleBarControlT<TitleBarControl> {
|
||||
TitleBarControl();
|
||||
|
||||
void Loading(FrameworkElement const&, IInspectable const&);
|
||||
|
||||
Imaging::SoftwareBitmapSource Logo() const noexcept {
|
||||
return _logo;
|
||||
}
|
||||
|
||||
event_token PropertyChanged(PropertyChangedEventHandler const& value) {
|
||||
return _propertyChangedEvent.add(value);
|
||||
}
|
||||
|
||||
void PropertyChanged(event_token const& token) {
|
||||
_propertyChangedEvent.remove(token);
|
||||
}
|
||||
|
||||
private:
|
||||
Imaging::SoftwareBitmapSource _logo{ nullptr };
|
||||
event<PropertyChangedEventHandler> _propertyChangedEvent;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::App::factory_implementation {
|
||||
|
||||
struct TitleBarControl : TitleBarControlT<TitleBarControl, implementation::TitleBarControl> {
|
||||
};
|
||||
|
||||
}
|
||||
8
src/Magpie.App/TitlebarControl.idl
Normal file
8
src/Magpie.App/TitlebarControl.idl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace Magpie.App {
|
||||
runtimeclass TitleBarControl : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged {
|
||||
TitleBarControl();
|
||||
|
||||
Windows.UI.Xaml.Media.Imaging.SoftwareBitmapSource Logo { get; };
|
||||
CaptionButtonsControl CaptionButtons { get; };
|
||||
}
|
||||
}
|
||||
77
src/Magpie.App/TitlebarControl.xaml
Normal file
77
src/Magpie.App/TitlebarControl.xaml
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<UserControl x:Class="Magpie.App.TitleBarControl"
|
||||
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:local="using:Magpie.App"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
x:Name="Root"
|
||||
Height="40"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
mc:Ignorable="d">
|
||||
<Grid Loading="Loading">
|
||||
<StackPanel Margin="16,10,0,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<StackPanel.RenderTransform>
|
||||
<TranslateTransform x:Name="TitleTranslation" />
|
||||
</StackPanel.RenderTransform>
|
||||
<Image Width="16"
|
||||
Height="16"
|
||||
VerticalAlignment="Center"
|
||||
Source="{x:Bind Logo, Mode=OneWay}" />
|
||||
<TextBlock Margin="0,0,0,2"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="Magpie" />
|
||||
</StackPanel>
|
||||
|
||||
<local:CaptionButtonsControl x:Name="CaptionButtons"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top" />
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="VisualStates">
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition From="Expanded"
|
||||
To="Compact">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="TitleTranslation"
|
||||
Storyboard.TargetProperty="X"
|
||||
From="0"
|
||||
To="45"
|
||||
Duration="0:0:0.22">
|
||||
<DoubleAnimation.EasingFunction>
|
||||
<ExponentialEase EasingMode="EaseOut"
|
||||
Exponent="7" />
|
||||
</DoubleAnimation.EasingFunction>
|
||||
</DoubleAnimation>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition From="Compact"
|
||||
To="Expanded">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="TitleTranslation"
|
||||
Storyboard.TargetProperty="X"
|
||||
From="45"
|
||||
To="0"
|
||||
Duration="0:0:0.22">
|
||||
<DoubleAnimation.EasingFunction>
|
||||
<ExponentialEase EasingMode="EaseOut"
|
||||
Exponent="7" />
|
||||
</DoubleAnimation.EasingFunction>
|
||||
</DoubleAnimation>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
</VisualStateGroup.Transitions>
|
||||
|
||||
<VisualState x:Name="Expanded" />
|
||||
<VisualState x:Name="Compact">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.Margin" Value="45,0,0,0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
@ -1,654 +0,0 @@
|
|||
<!-- Copied from: https://github.com/microsoft/PowerToys/blob/35bfb0f83e5fc08cc04398e7aa98d77774412d3f/src/settings-ui/Settings.UI/Styles/Button.xaml#L96 -->
|
||||
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- This style overrides the default style so that all ToggleSwitches are right aligned, with the label on the left -->
|
||||
<Style TargetType="ToggleSwitch">
|
||||
<Setter Property="Foreground" Value="{ThemeResource ToggleSwitchContentForeground}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="ManipulationMode" Value="System,TranslateX" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="FocusVisualMargin" Value="-7,-3,-7,-3" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleSwitch">
|
||||
<Grid Margin="0,-4,0,-4"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ContentPresenter x:Name="HeaderContentPresenter"
|
||||
Grid.Row="0"
|
||||
Margin="{ThemeResource ToggleSwitchTopHeaderMargin}"
|
||||
VerticalAlignment="Top"
|
||||
x:DeferLoadStrategy="Lazy"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
Foreground="{ThemeResource ToggleSwitchHeaderForeground}"
|
||||
IsHitTestVisible="False"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="Collapsed" />
|
||||
<Grid Grid.Row="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="{ThemeResource ToggleSwitchPreContentMargin}" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="{ThemeResource ToggleSwitchPostContentMargin}" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="12"
|
||||
MaxWidth="12" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid x:Name="SwitchAreaGrid"
|
||||
Grid.RowSpan="3"
|
||||
Grid.ColumnSpan="3"
|
||||
Margin="0,5"
|
||||
Background="{ThemeResource ToggleSwitchContainerBackground}"
|
||||
Control.IsTemplateFocusTarget="True"
|
||||
CornerRadius="{TemplateBinding CornerRadius}" />
|
||||
<ContentPresenter x:Name="OffContentPresenter"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{TemplateBinding OffContent}"
|
||||
ContentTemplate="{TemplateBinding OffContentTemplate}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0" />
|
||||
<ContentPresenter x:Name="OnContentPresenter"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{TemplateBinding OnContent}"
|
||||
ContentTemplate="{TemplateBinding OnContentTemplate}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0" />
|
||||
<Rectangle x:Name="OuterBorder"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Width="40"
|
||||
Height="20"
|
||||
Fill="{ThemeResource ToggleSwitchFillOff}"
|
||||
RadiusX="10"
|
||||
RadiusY="10"
|
||||
Stroke="{ThemeResource ToggleSwitchStrokeOff}"
|
||||
StrokeThickness="{ThemeResource ToggleSwitchOuterBorderStrokeThickness}" />
|
||||
<Rectangle x:Name="SwitchKnobBounds"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Width="40"
|
||||
Height="20"
|
||||
Fill="{ThemeResource ToggleSwitchFillOn}"
|
||||
Opacity="0"
|
||||
RadiusX="10"
|
||||
RadiusY="10"
|
||||
Stroke="{ThemeResource ToggleSwitchStrokeOn}"
|
||||
StrokeThickness="{ThemeResource ToggleSwitchOnStrokeThickness}" />
|
||||
<Grid x:Name="SwitchKnob"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Width="20"
|
||||
Height="20"
|
||||
HorizontalAlignment="Left">
|
||||
<Border x:Name="SwitchKnobOn"
|
||||
Grid.Column="2"
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="0,0,1,0"
|
||||
HorizontalAlignment="Center"
|
||||
Background="{ThemeResource ToggleSwitchKnobFillOn}"
|
||||
BackgroundSizing="OuterBorderEdge"
|
||||
BorderBrush="{ThemeResource ToggleSwitchKnobStrokeOn}"
|
||||
CornerRadius="7"
|
||||
Opacity="0"
|
||||
RenderTransformOrigin="0.5, 0.5">
|
||||
<Border.RenderTransform>
|
||||
<CompositeTransform />
|
||||
</Border.RenderTransform>
|
||||
</Border>
|
||||
<Rectangle x:Name="SwitchKnobOff"
|
||||
Grid.Column="2"
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="-1,0,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Fill="{ThemeResource ToggleSwitchKnobFillOff}"
|
||||
RadiusX="7"
|
||||
RadiusY="7"
|
||||
RenderTransformOrigin="0.5, 0.5">
|
||||
<Rectangle.RenderTransform>
|
||||
<CompositeTransform />
|
||||
</Rectangle.RenderTransform>
|
||||
</Rectangle>
|
||||
<Grid.RenderTransform>
|
||||
<TranslateTransform x:Name="KnobTranslateTransform" />
|
||||
</Grid.RenderTransform>
|
||||
</Grid>
|
||||
<Thumb x:Name="SwitchThumb"
|
||||
Grid.RowSpan="3"
|
||||
Grid.ColumnSpan="3"
|
||||
AutomationProperties.AccessibilityView="Raw">
|
||||
<Thumb.Template>
|
||||
<ControlTemplate TargetType="Thumb">
|
||||
<Rectangle Fill="Transparent" />
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
</Thumb>
|
||||
|
||||
</Grid>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOff}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchFillOff}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOff}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOn}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchFillOn}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOn}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchContainerBackground}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOffPointerOver}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchFillOffPointerOver}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOffPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOnPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchFillOnPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOnPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid"
|
||||
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchContainerBackgroundPointerOver}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
|
||||
<VisualState.Setters>
|
||||
<Setter Target="SwitchKnobOn.HorizontalAlignment" Value="Right" />
|
||||
<Setter Target="SwitchKnobOn.Margin" Value="0,0,3,0" />
|
||||
<Setter Target="SwitchKnobOff.HorizontalAlignment" Value="Left" />
|
||||
<Setter Target="SwitchKnobOff.Margin" Value="3,0,0,0" />
|
||||
</VisualState.Setters>
|
||||
|
||||
<Storyboard>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOffPressed}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchFillOffPressed}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchFillOnPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOnPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOffPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOnPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid"
|
||||
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchContainerBackgroundPressed}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="17" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="17" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchHeaderForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OffContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchContentForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OnContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchContentForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOffDisabled}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchFillOffDisabled}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchFillOnDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchStrokeOnDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOffDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ToggleSwitchKnobFillOnDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid"
|
||||
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="{ThemeResource ToggleSwitchContainerBackgroundDisabled}" />
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ToggleStates">
|
||||
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition x:Name="DraggingToOnTransition"
|
||||
GeneratedDuration="0"
|
||||
From="Dragging"
|
||||
To="On">
|
||||
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobCurrentToOnOffset}"
|
||||
TargetName="SwitchKnob" />
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="OnToDraggingTransition"
|
||||
GeneratedDuration="0"
|
||||
From="On"
|
||||
To="Dragging">
|
||||
<Storyboard>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="0"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="0"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="0"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="DraggingToOffTransition"
|
||||
GeneratedDuration="0"
|
||||
From="Dragging"
|
||||
To="Off">
|
||||
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobCurrentToOffOffset}"
|
||||
TargetName="SwitchKnob" />
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="OnToOffTransition"
|
||||
GeneratedDuration="0"
|
||||
From="On"
|
||||
To="Off">
|
||||
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobOnToOffOffset}"
|
||||
TargetName="SwitchKnob" />
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="OffToOnTransition"
|
||||
GeneratedDuration="0"
|
||||
From="Off"
|
||||
To="On">
|
||||
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobOffToOnOffset}"
|
||||
TargetName="SwitchKnob" />
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
</VisualStateGroup.Transitions>
|
||||
<VisualState x:Name="Dragging" />
|
||||
<VisualState x:Name="Off" />
|
||||
<VisualState x:Name="On">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="KnobTranslateTransform"
|
||||
Storyboard.TargetProperty="X"
|
||||
To="20"
|
||||
Duration="0" />
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ContentStates">
|
||||
<VisualState x:Name="OffContent">
|
||||
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="OffContentPresenter"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OffContentPresenter"
|
||||
Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="OnContent">
|
||||
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="OnContentPresenter"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OnContentPresenter"
|
||||
Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -425,8 +425,10 @@ bool MagApp::_CreateHostWnd() {
|
|||
}
|
||||
}
|
||||
|
||||
// WS_EX_NOREDIRECTIONBITMAP 可以避免 WS_EX_LAYERED 导致的额外内存开销
|
||||
_hwndHost = CreateWindowEx(
|
||||
(_options.IsDebugMode() ? 0 : WS_EX_TOPMOST) | WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
|
||||
(_options.IsDebugMode() ? 0 : WS_EX_TOPMOST) | WS_EX_NOACTIVATE
|
||||
| WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
|
||||
HOST_WINDOW_CLASS_NAME,
|
||||
NULL, // 标题为空,否则会被添加新配置页面列为候选窗口
|
||||
WS_POPUP,
|
||||
|
|
|
|||
|
|
@ -8,15 +8,24 @@
|
|||
namespace Magpie {
|
||||
|
||||
bool MainWindow::Create(HINSTANCE hInstance, const RECT& windowRect, bool isMaximized) noexcept {
|
||||
WNDCLASSEXW wcex{};
|
||||
wcex.cbSize = sizeof(wcex);
|
||||
wcex.lpfnWndProc = _WndProc;
|
||||
wcex.hInstance = hInstance;
|
||||
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(CommonSharedConstants::IDI_APP));
|
||||
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wcex.lpszClassName = CommonSharedConstants::MAIN_WINDOW_CLASS_NAME;
|
||||
static const int _ = [](HINSTANCE hInstance) {
|
||||
WNDCLASSEXW wcex{};
|
||||
wcex.cbSize = sizeof(wcex);
|
||||
wcex.lpfnWndProc = _WndProc;
|
||||
wcex.hInstance = hInstance;
|
||||
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(CommonSharedConstants::IDI_APP));
|
||||
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wcex.lpszClassName = CommonSharedConstants::MAIN_WINDOW_CLASS_NAME;
|
||||
RegisterClassEx(&wcex);
|
||||
|
||||
RegisterClassEx(&wcex);
|
||||
wcex.style = CS_DBLCLKS;
|
||||
wcex.lpfnWndProc = _TitleBarWndProc;
|
||||
wcex.hIcon = NULL;
|
||||
wcex.lpszClassName = CommonSharedConstants::TITLE_BAR_WINDOW_CLASS_NAME;
|
||||
RegisterClassEx(&wcex);
|
||||
|
||||
return 0;
|
||||
}(hInstance);
|
||||
|
||||
// Win11 22H2 中为了使用 Mica 背景需指定 WS_EX_NOREDIRECTIONBITMAP
|
||||
CreateWindowEx(
|
||||
|
|
@ -25,8 +34,8 @@ bool MainWindow::Create(HINSTANCE hInstance, const RECT& windowRect, bool isMaxi
|
|||
L"Magpie",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
|
||||
nullptr,
|
||||
nullptr,
|
||||
NULL,
|
||||
NULL,
|
||||
hInstance,
|
||||
this
|
||||
);
|
||||
|
|
@ -37,22 +46,87 @@ bool MainWindow::Create(HINSTANCE hInstance, const RECT& windowRect, bool isMaxi
|
|||
|
||||
_SetContent(winrt::Magpie::App::MainPage());
|
||||
|
||||
// Xaml 控件加载完成后显示主窗口
|
||||
_content.Loaded([this, isMaximized](winrt::IInspectable const&, winrt::RoutedEventArgs const&) -> winrt::IAsyncAction {
|
||||
co_await _content.Dispatcher().RunAsync(winrt::CoreDispatcherPriority::Normal, [hWnd(_hWnd), isMaximized]() {
|
||||
// 防止窗口显示时背景闪烁
|
||||
// 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);
|
||||
ShowWindow(hWnd, isMaximized ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL);
|
||||
Win32Utils::SetForegroundWindow(hWnd);
|
||||
});
|
||||
});
|
||||
|
||||
_content.ActualThemeChanged([this](winrt::FrameworkElement const&, winrt::IInspectable const&) {
|
||||
_UpdateTheme();
|
||||
});
|
||||
_UpdateTheme();
|
||||
|
||||
// 窗口尚未显示无法最大化,所以我们设置 _isMaximized 使 XamlWindow 估计 XAML Islands 窗口尺寸。
|
||||
// 否则在显示窗口时可能会看到 NavigationView 的导航栏的展开动画。
|
||||
_isMaximized = isMaximized;
|
||||
|
||||
// 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);
|
||||
|
||||
// Xaml 控件加载完成后显示主窗口
|
||||
_content.Loaded([this, isMaximized](winrt::IInspectable const&, winrt::RoutedEventArgs const&) {
|
||||
if (isMaximized) {
|
||||
// ShowWindow(_hWnd, SW_SHOWMAXIMIZED) 会显示错误的动画。因此我们以窗口化显示,
|
||||
// 但位置和大小都和最大化相同,显示完毕后将状态设为最大化。
|
||||
//
|
||||
// 在此过程中,_isMaximized 始终是 true。
|
||||
|
||||
// 保存原始窗口化位置
|
||||
WINDOWPLACEMENT wp{};
|
||||
wp.length = sizeof(wp);
|
||||
GetWindowPlacement(_hWnd, &wp);
|
||||
|
||||
// 查询最大化窗口位置
|
||||
if (HMONITOR hMon = MonitorFromWindow(_hWnd, MONITOR_DEFAULTTONEAREST)) {
|
||||
MONITORINFO mi{};
|
||||
mi.cbSize = sizeof(mi);
|
||||
GetMonitorInfo(hMon, &mi);
|
||||
|
||||
// 播放窗口显示动画
|
||||
SetWindowPos(
|
||||
_hWnd,
|
||||
NULL,
|
||||
mi.rcWork.left,
|
||||
mi.rcWork.top,
|
||||
mi.rcMonitor.right - mi.rcMonitor.left,
|
||||
mi.rcMonitor.bottom - mi.rcMonitor.top,
|
||||
SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW
|
||||
);
|
||||
}
|
||||
|
||||
// 将状态设为最大化,也还原了原始的窗口化位置
|
||||
wp.showCmd = SW_SHOWMAXIMIZED;
|
||||
SetWindowPlacement(_hWnd, &wp);
|
||||
} else {
|
||||
ShowWindow(_hWnd, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
Win32Utils::SetForegroundWindow(_hWnd);
|
||||
|
||||
_isWindowShown = true;
|
||||
});
|
||||
|
||||
// 创建标题栏窗口,它是主窗口的子窗口。我们将它置于 XAML Islands 窗口之上以防止鼠标事件被吞掉
|
||||
//
|
||||
// 出于未知的原因,必须添加 WS_EX_LAYERED 样式才能发挥作用,见
|
||||
// https://github.com/microsoft/terminal/blob/0ee2c74cd432eda153f3f3e77588164cde95044f/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L79
|
||||
// WS_EX_NOREDIRECTIONBITMAP 可以避免 WS_EX_LAYERED 导致的额外内存开销
|
||||
//
|
||||
// WS_MINIMIZEBOX 和 WS_MAXIMIZEBOX 使得鼠标悬停时显示文字提示,Win11 的贴靠布局不依赖它们
|
||||
CreateWindowEx(
|
||||
WS_EX_LAYERED | WS_EX_NOPARENTNOTIFY | WS_EX_NOREDIRECTIONBITMAP | WS_EX_NOACTIVATE,
|
||||
CommonSharedConstants::TITLE_BAR_WINDOW_CLASS_NAME,
|
||||
L"",
|
||||
WS_CHILD | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
|
||||
0, 0, 0, 0,
|
||||
_hWnd,
|
||||
nullptr,
|
||||
hInstance,
|
||||
this
|
||||
);
|
||||
SetLayeredWindowAttributes(_hwndTitleBar, 0, 255, LWA_ALPHA);
|
||||
|
||||
_content.TitleBar().SizeChanged([this](winrt::IInspectable const&, winrt::SizeChangedEventArgs const&) {
|
||||
_ResizeTitleBarWindow();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -66,16 +140,59 @@ void MainWindow::Show() const noexcept {
|
|||
|
||||
LRESULT MainWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noexcept {
|
||||
switch (msg) {
|
||||
case WM_SIZE:
|
||||
{
|
||||
LRESULT ret = base_type::_MessageHandler(WM_SIZE, wParam, lParam);
|
||||
_ResizeTitleBarWindow();
|
||||
_content.TitleBar().CaptionButtons().IsWindowMaximized(_isMaximized);
|
||||
return ret;
|
||||
}
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
// 设置窗口最小尺寸
|
||||
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
|
||||
mmi->ptMinTrackSize = { 500,300 };
|
||||
mmi->ptMinTrackSize = {
|
||||
std::lround(550 * _currentDpi / double(USER_DEFAULT_SCREEN_DPI)),
|
||||
std::lround(300 * _currentDpi / double(USER_DEFAULT_SCREEN_DPI))
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
case WM_NCRBUTTONUP:
|
||||
{
|
||||
// 我们自己处理标题栏右键,不知为何 DefWindowProc 没有作用
|
||||
if (wParam == HTCAPTION) {
|
||||
HMENU systemMenu = GetSystemMenu(_hWnd, FALSE);
|
||||
|
||||
// 根据窗口状态更新选项
|
||||
MENUITEMINFO mii{};
|
||||
mii.cbSize = sizeof(MENUITEMINFO);
|
||||
mii.fMask = MIIM_STATE;
|
||||
mii.fType = MFT_STRING;
|
||||
auto setState = [&](UINT item, bool enabled) {
|
||||
mii.fState = enabled ? MF_ENABLED : MF_DISABLED;
|
||||
SetMenuItemInfo(systemMenu, item, FALSE, &mii);
|
||||
};
|
||||
setState(SC_RESTORE, _isMaximized);
|
||||
setState(SC_MOVE, !_isMaximized);
|
||||
setState(SC_SIZE, !_isMaximized);
|
||||
setState(SC_MINIMIZE, true);
|
||||
setState(SC_MAXIMIZE, !_isMaximized);
|
||||
setState(SC_CLOSE, true);
|
||||
SetMenuDefaultItem(systemMenu, UINT_MAX, FALSE);
|
||||
|
||||
BOOL cmd = TrackPopupMenu(systemMenu, TPM_RETURNCMD,
|
||||
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, _hWnd, nullptr);
|
||||
if (cmd != 0) {
|
||||
PostMessage(_hWnd, WM_SYSCOMMAND, cmd, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_DESTROY:
|
||||
{
|
||||
XamlApp::Get().SaveSettings();
|
||||
_hwndTitleBar = NULL;
|
||||
_trackingMouse = false;
|
||||
break;
|
||||
}
|
||||
case CommonSharedConstants::WM_QUIT_MAGPIE:
|
||||
|
|
@ -93,27 +210,227 @@ LRESULT MainWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noex
|
|||
}
|
||||
|
||||
void MainWindow::_UpdateTheme() {
|
||||
const bool isDarkTheme = _content.ActualTheme() == winrt::ElementTheme::Dark;
|
||||
XamlWindowT::_SetTheme(_content.ActualTheme() == winrt::ElementTheme::Dark);
|
||||
}
|
||||
|
||||
if (Win32Utils::GetOSVersion().Is22H2OrNewer()) {
|
||||
// 设置 Mica 背景
|
||||
DWM_SYSTEMBACKDROP_TYPE value = DWMSBT_MAINWINDOW;
|
||||
DwmSetWindowAttribute(_hWnd, DWMWA_SYSTEMBACKDROP_TYPE, &value, sizeof(value));
|
||||
} else {
|
||||
// 更改背景色以配合主题
|
||||
// 背景色在更改窗口大小时会短暂可见
|
||||
HBRUSH hbrOld = (HBRUSH)SetClassLongPtr(
|
||||
_hWnd,
|
||||
GCLP_HBRBACKGROUND,
|
||||
(INT_PTR)CreateSolidBrush(isDarkTheme ?
|
||||
CommonSharedConstants::DARK_TINT_COLOR : CommonSharedConstants::LIGHT_TINT_COLOR));
|
||||
if (hbrOld) {
|
||||
DeleteObject(hbrOld);
|
||||
}
|
||||
InvalidateRect(_hWnd, nullptr, TRUE);
|
||||
LRESULT MainWindow::_TitleBarWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) noexcept {
|
||||
if (msg == WM_NCCREATE) {
|
||||
MainWindow* that = (MainWindow*)(((CREATESTRUCT*)lParam)->lpCreateParams);
|
||||
assert(that && !that->_hwndTitleBar);
|
||||
that->_hwndTitleBar = hWnd;
|
||||
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)that);
|
||||
} else if (MainWindow* that = (MainWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA)) {
|
||||
return that->_TitleBarMessageHandler(msg, wParam, lParam);
|
||||
}
|
||||
|
||||
ThemeHelper::SetWindowTheme(_hWnd, isDarkTheme);
|
||||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT MainWindow::_TitleBarMessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noexcept {
|
||||
switch (msg) {
|
||||
case WM_NCHITTEST:
|
||||
{
|
||||
POINT cursorPos{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) };
|
||||
ScreenToClient(_hwndTitleBar, &cursorPos);
|
||||
|
||||
RECT titleBarClientRect;
|
||||
GetClientRect(_hwndTitleBar, &titleBarClientRect);
|
||||
if (!PtInRect(&titleBarClientRect, cursorPos)) {
|
||||
// 先检查鼠标是否在窗口内。在标题栏按钮上按下鼠标时我们会捕获光标,从而收到 WM_MOUSEMOVE 和 WM_LBUTTONUP 消息。
|
||||
// 它们使用 WM_NCHITTEST 测试鼠标位于哪个区域
|
||||
return HTNOWHERE;
|
||||
}
|
||||
|
||||
if (!_isMaximized && cursorPos.y + (int)_GetTopBorderHeight() < _GetResizeHandleHeight()) {
|
||||
// 鼠标位于上边框
|
||||
return HTTOP;
|
||||
}
|
||||
|
||||
static const winrt::Size buttonSizeInDips = [this]() {
|
||||
return _content.TitleBar().CaptionButtons().CaptionButtonSize();
|
||||
}();
|
||||
|
||||
const float buttonWidthInPixels = buttonSizeInDips.Width * _currentDpi / USER_DEFAULT_SCREEN_DPI;
|
||||
const float buttonHeightInPixels = buttonSizeInDips.Height * _currentDpi / USER_DEFAULT_SCREEN_DPI;
|
||||
|
||||
if (cursorPos.y >= buttonHeightInPixels) {
|
||||
// 鼠标位于标题按钮下方,如果标题栏很宽,这里也可以拖动
|
||||
return HTCAPTION;
|
||||
}
|
||||
|
||||
// 从右向左检查鼠标是否位于某个标题栏按钮上
|
||||
const LONG cursorToRight = titleBarClientRect.right - cursorPos.x;
|
||||
if (cursorToRight < buttonWidthInPixels) {
|
||||
return HTCLOSE;
|
||||
} else if (cursorToRight < buttonWidthInPixels * 2) {
|
||||
// 支持 Win11 的贴靠布局
|
||||
// FIXME: 最大化时贴靠布局的位置不对,目前没有找到解决方案。似乎只适配了系统原生框架和 UWP
|
||||
return HTMAXBUTTON;
|
||||
} else if (cursorToRight < buttonWidthInPixels * 3) {
|
||||
return HTMINBUTTON;
|
||||
} else {
|
||||
// 不在任何标题栏按钮上则在可拖拽区域
|
||||
return HTCAPTION;
|
||||
}
|
||||
}
|
||||
// 在捕获光标时会收到
|
||||
case WM_MOUSEMOVE:
|
||||
{
|
||||
POINT cursorPos{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) };
|
||||
ClientToScreen(_hwndTitleBar, &cursorPos);
|
||||
wParam = SendMessage(_hwndTitleBar, WM_NCHITTEST, 0, MAKELPARAM(cursorPos.x, cursorPos.y));
|
||||
}
|
||||
[[fallthrough]];
|
||||
case WM_NCMOUSEMOVE:
|
||||
{
|
||||
auto captionButtons = _content.TitleBar().CaptionButtons();
|
||||
|
||||
// 将 hover 状态通知 CaptionButtons。标题栏窗口拦截了 XAML Islands 中的标题栏
|
||||
// 控件的鼠标消息,标题栏按钮的状态由我们手动控制。
|
||||
switch (wParam) {
|
||||
case HTTOP:
|
||||
case HTCAPTION:
|
||||
{
|
||||
captionButtons.LeaveButtons();
|
||||
|
||||
// 将 HTTOP 传给主窗口才能通过上边框调整窗口高度
|
||||
return SendMessage(_hWnd, msg, wParam, lParam);
|
||||
}
|
||||
case HTMINBUTTON:
|
||||
case HTMAXBUTTON:
|
||||
case HTCLOSE:
|
||||
captionButtons.HoverButton((winrt::Magpie::App::CaptionButton)wParam);
|
||||
|
||||
// 追踪鼠标以确保鼠标离开标题栏时我们能收到 WM_NCMOUSELEAVE 消息,否则无法
|
||||
// 可靠的收到这个消息,尤其是在用户快速移动鼠标的时候。
|
||||
if (!_trackingMouse && msg == WM_NCMOUSEMOVE) {
|
||||
TRACKMOUSEEVENT ev{};
|
||||
ev.cbSize = sizeof(TRACKMOUSEEVENT);
|
||||
ev.dwFlags = TME_LEAVE | TME_NONCLIENT;
|
||||
ev.hwndTrack = _hwndTitleBar;
|
||||
ev.dwHoverTime = HOVER_DEFAULT; // 不关心 HOVER 消息
|
||||
TrackMouseEvent(&ev);
|
||||
_trackingMouse = true;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
captionButtons.LeaveButtons();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_NCMOUSELEAVE:
|
||||
case WM_MOUSELEAVE:
|
||||
{
|
||||
// 我们需要检查鼠标是否**真的**离开了标题栏按钮,因为在某些情况下 OS 会错误汇报。
|
||||
// 比如:鼠标在关闭按钮上停留了一段时间,系统会显示文字提示,这时按下左键,便会收
|
||||
// 到 WM_NCMOUSELEAVE,但此时鼠标并没有离开标题栏按钮
|
||||
POINT cursorPos;
|
||||
GetCursorPos(&cursorPos);
|
||||
// 先检查鼠标是否在主窗口上,如果正在显示文字提示,会返回 _hwndTitleBar
|
||||
HWND hwndUnderCursor = WindowFromPoint(cursorPos);
|
||||
if (hwndUnderCursor != _hWnd && hwndUnderCursor != _hwndTitleBar) {
|
||||
_content.TitleBar().CaptionButtons().LeaveButtons();
|
||||
} else {
|
||||
// 然后检查鼠标在标题栏上的位置
|
||||
LRESULT hit = SendMessage(_hwndTitleBar, WM_NCHITTEST, 0, MAKELPARAM(cursorPos.x, cursorPos.y));
|
||||
if (hit != HTMINBUTTON && hit != HTMAXBUTTON && hit != HTCLOSE) {
|
||||
_content.TitleBar().CaptionButtons().LeaveButtons();
|
||||
}
|
||||
}
|
||||
|
||||
_trackingMouse = false;
|
||||
break;
|
||||
}
|
||||
case WM_NCLBUTTONDOWN:
|
||||
case WM_NCLBUTTONDBLCLK:
|
||||
{
|
||||
// 手动处理标题栏上的点击。如果在标题栏按钮上,则通知 CaptionButtons,否则将消息传递
|
||||
// 给主窗口。
|
||||
switch (wParam) {
|
||||
case HTTOP:
|
||||
case HTCAPTION:
|
||||
{
|
||||
// 将 HTTOP 传给主窗口才能通过上边框调整窗口高度
|
||||
return SendMessage(_hWnd, msg, wParam, lParam);
|
||||
}
|
||||
case HTMINBUTTON:
|
||||
case HTMAXBUTTON:
|
||||
case HTCLOSE:
|
||||
_content.TitleBar().CaptionButtons().PressButton((winrt::Magpie::App::CaptionButton)wParam);
|
||||
// 在标题栏按钮上按下左键后我们便捕获光标,这样才能在释放时得到通知。注意捕获光标后
|
||||
// 便不会再收到 NC 族消息,这就是为什么我们要处理 WM_MOUSEMOVE 和 WM_LBUTTONUP
|
||||
SetCapture(_hwndTitleBar);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// 在捕获光标时会收到
|
||||
case WM_LBUTTONUP:
|
||||
{
|
||||
ReleaseCapture();
|
||||
|
||||
POINT cursorPos{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) };
|
||||
ClientToScreen(_hwndTitleBar, &cursorPos);
|
||||
wParam = SendMessage(_hwndTitleBar, WM_NCHITTEST, 0, MAKELPARAM(cursorPos.x, cursorPos.y));
|
||||
}
|
||||
[[fallthrough]];
|
||||
case WM_NCLBUTTONUP:
|
||||
{
|
||||
// 处理鼠标在标题栏上释放。如果位于标题栏按钮上,则传递给 CaptionButtons,不在则将消息传递给主窗口
|
||||
switch (wParam) {
|
||||
case HTTOP:
|
||||
case HTCAPTION:
|
||||
{
|
||||
// 在可拖拽区域或上边框释放左键,将此消息传递给主窗口
|
||||
_content.TitleBar().CaptionButtons().ReleaseButtons();
|
||||
return SendMessage(_hWnd, msg, wParam, lParam);
|
||||
}
|
||||
case HTMINBUTTON:
|
||||
case HTMAXBUTTON:
|
||||
case HTCLOSE:
|
||||
// 在标题栏按钮上释放左键
|
||||
_content.TitleBar().CaptionButtons().ReleaseButton((winrt::Magpie::App::CaptionButton)wParam);
|
||||
break;
|
||||
default:
|
||||
_content.TitleBar().CaptionButtons().ReleaseButtons();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_NCRBUTTONDOWN:
|
||||
case WM_NCRBUTTONDBLCLK:
|
||||
case WM_NCRBUTTONUP:
|
||||
// 不关心右键,将它们传递给主窗口
|
||||
return SendMessage(_hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
return DefWindowProc(_hwndTitleBar, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
void MainWindow::_ResizeTitleBarWindow() noexcept {
|
||||
if (!_hwndTitleBar) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto titleBar = _content.TitleBar();
|
||||
|
||||
// 获取标题栏的边框矩形
|
||||
winrt::Rect rect{0.0f, 0.0f, (float)titleBar.ActualWidth(), (float)titleBar.ActualHeight()};
|
||||
rect = titleBar.TransformToVisual(_content).TransformBounds(rect);
|
||||
|
||||
const float dpiScale = _currentDpi / float(USER_DEFAULT_SCREEN_DPI);
|
||||
|
||||
// 将标题栏窗口置于 XAML Islands 窗口上方
|
||||
SetWindowPos(
|
||||
_hwndTitleBar,
|
||||
HWND_TOP,
|
||||
(int)std::floorf(rect.X * dpiScale),
|
||||
(int)std::floorf(rect.Y * dpiScale) + _GetTopBorderHeight(),
|
||||
(int)std::ceilf(rect.Width * dpiScale),
|
||||
(int)std::floorf(rect.Height * dpiScale + 1), // 不知为何,直接向上取整有时无法遮盖 TitleBarControl
|
||||
SWP_SHOWWINDOW
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,14 @@ protected:
|
|||
private:
|
||||
void _UpdateTheme();
|
||||
|
||||
bool _isMainWndMaximized = false;
|
||||
static LRESULT CALLBACK _TitleBarWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) noexcept;
|
||||
|
||||
LRESULT _TitleBarMessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noexcept;
|
||||
|
||||
void _ResizeTitleBarWindow() noexcept;
|
||||
|
||||
HWND _hwndTitleBar = NULL;
|
||||
bool _trackingMouse = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,17 +45,17 @@ void ThemeHelper::Initialize() noexcept {
|
|||
RefreshImmersiveColorPolicyState();
|
||||
}
|
||||
|
||||
void ThemeHelper::SetWindowTheme(HWND hWnd, bool isDark) noexcept {
|
||||
void ThemeHelper::SetWindowTheme(HWND hWnd, bool darkBorder, bool darkMenu) noexcept {
|
||||
InitApis();
|
||||
|
||||
SetPreferredAppMode(isDark ? PreferredAppMode::ForceDark : PreferredAppMode::ForceLight);
|
||||
AllowDarkModeForWindow(hWnd, isDark);
|
||||
SetPreferredAppMode(darkMenu ? PreferredAppMode::ForceDark : PreferredAppMode::ForceLight);
|
||||
AllowDarkModeForWindow(hWnd, darkMenu);
|
||||
|
||||
// 使标题栏适应黑暗模式
|
||||
// build 18985 之前 DWMWA_USE_IMMERSIVE_DARK_MODE 的值不同
|
||||
// https://github.com/MicrosoftDocs/sdk-api/pull/966/files
|
||||
constexpr const DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
|
||||
BOOL value = isDark;
|
||||
BOOL value = darkBorder;
|
||||
DwmSetWindowAttribute(
|
||||
hWnd,
|
||||
Win32Utils::GetOSVersion().Is20H1OrNewer() ? DWMWA_USE_IMMERSIVE_DARK_MODE : DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
|
||||
|
|
@ -65,19 +65,6 @@ void ThemeHelper::SetWindowTheme(HWND hWnd, bool isDark) noexcept {
|
|||
|
||||
RefreshImmersiveColorPolicyState();
|
||||
FlushMenuThemes();
|
||||
|
||||
const Win32Utils::OSVersion& osVersion = Win32Utils::GetOSVersion();
|
||||
if (osVersion.Is22H2OrNewer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LONG_PTR style = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
|
||||
if (!osVersion.IsWin11()) {
|
||||
// 在 Win10 上需要更多 hack
|
||||
SetWindowLongPtr(hWnd, GWL_EXSTYLE, style | WS_EX_LAYERED);
|
||||
SetLayeredWindowAttributes(hWnd, 0, 254, LWA_ALPHA);
|
||||
}
|
||||
SetWindowLongPtr(hWnd, GWL_EXSTYLE, style);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ namespace Magpie {
|
|||
struct ThemeHelper {
|
||||
// 应用程序启动时调用一次
|
||||
static void Initialize() noexcept;
|
||||
static void SetWindowTheme(HWND hWnd, bool isDark) noexcept;
|
||||
static void SetWindowTheme(HWND hWnd, bool darkBorder, bool darkMenu) noexcept;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
|
||||
#include <CoreWindow.h>
|
||||
#include "XamlUtils.h"
|
||||
#include "Win32Utils.h"
|
||||
#include "ThemeHelper.h"
|
||||
#include "CommonSharedConstants.h"
|
||||
|
||||
#pragma comment(lib, "uxtheme.lib")
|
||||
|
||||
namespace Magpie {
|
||||
|
||||
|
|
@ -92,13 +97,212 @@ protected:
|
|||
sender.NavigateFocus(args.Request());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 防止第一次收到 WM_SIZE 消息时 MainPage 尺寸为 0
|
||||
_OnResize();
|
||||
void _SetTheme(bool isDarkTheme) noexcept {
|
||||
_isDarkTheme = isDarkTheme;
|
||||
|
||||
// Win10 中即使在亮色主题下我们也使用暗色边框,这也是 UWP 窗口的行为
|
||||
ThemeHelper::SetWindowTheme(
|
||||
_hWnd,
|
||||
Win32Utils::GetOSVersion().IsWin11() ? isDarkTheme : true,
|
||||
isDarkTheme
|
||||
);
|
||||
|
||||
if (Win32Utils::GetOSVersion().Is22H2OrNewer()) {
|
||||
// 设置 Mica 背景
|
||||
DWM_SYSTEMBACKDROP_TYPE value = DWMSBT_MAINWINDOW;
|
||||
DwmSetWindowAttribute(_hWnd, DWMWA_SYSTEMBACKDROP_TYPE, &value, sizeof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Win32Utils::GetOSVersion().IsWin11()) {
|
||||
// Win11 21H1/21H2 对 Mica 的支持不完善,改为使用纯色背景。Win10 在 WM_PAINT 中
|
||||
// 绘制背景。背景色在更改窗口大小时会短暂可见。
|
||||
HBRUSH hbrOld = (HBRUSH)SetClassLongPtr(
|
||||
_hWnd,
|
||||
GCLP_HBRBACKGROUND,
|
||||
(INT_PTR)CreateSolidBrush(isDarkTheme ?
|
||||
CommonSharedConstants::DARK_TINT_COLOR : CommonSharedConstants::LIGHT_TINT_COLOR));
|
||||
if (hbrOld) {
|
||||
DeleteObject(hbrOld);
|
||||
}
|
||||
}
|
||||
|
||||
// 立即重新绘制
|
||||
InvalidateRect(_hWnd, nullptr, FALSE);
|
||||
UpdateWindow(_hWnd);
|
||||
}
|
||||
|
||||
LRESULT _MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noexcept {
|
||||
switch (msg) {
|
||||
case WM_CREATE:
|
||||
{
|
||||
_currentDpi = GetDpiForWindow(_hWnd);
|
||||
|
||||
_UpdateFrameMargins();
|
||||
|
||||
if (!Win32Utils::GetOSVersion().IsWin11()) {
|
||||
// 初始化双缓冲绘图
|
||||
static const int _ = []() {
|
||||
BufferedPaintInit();
|
||||
return 0;
|
||||
}();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case WM_NCCALCSIZE:
|
||||
{
|
||||
// 移除标题栏的逻辑基本来自 Windows Terminal
|
||||
// https://github.com/microsoft/terminal/blob/0ee2c74cd432eda153f3f3e77588164cde95044f/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
|
||||
|
||||
if (!wParam) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NCCALCSIZE_PARAMS* params = (NCCALCSIZE_PARAMS*)lParam;
|
||||
RECT& clientRect = params->rgrc[0];
|
||||
|
||||
// 保存原始上边框位置
|
||||
const LONG originalTop = clientRect.top;
|
||||
|
||||
// 应用默认边框
|
||||
LRESULT ret = DefWindowProc(_hWnd, WM_NCCALCSIZE, wParam, lParam);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 重新应用原始上边框,因此我们完全移除了默认边框中的上边框和标题栏,但保留了其他方向的边框
|
||||
clientRect.top = originalTop;
|
||||
|
||||
// WM_NCCALCSIZE 在 WM_SIZE 前
|
||||
_UpdateMaximizedState();
|
||||
|
||||
if (_isMaximized) {
|
||||
// 最大化的窗口的实际尺寸比屏幕的工作区更大一点,这是为了将可调整窗口大小的区域隐藏在屏幕外面
|
||||
clientRect.top += _GetResizeHandleHeight();
|
||||
|
||||
// 如果有自动隐藏的任务栏,我们在它的方向稍微减小客户区,这样用户就可以用鼠标呼出任务栏
|
||||
if (HMONITOR hMon = MonitorFromWindow(_hWnd, MONITOR_DEFAULTTONEAREST)) {
|
||||
MONITORINFO monInfo{};
|
||||
monInfo.cbSize = sizeof(MONITORINFO);
|
||||
GetMonitorInfo(hMon, &monInfo);
|
||||
|
||||
// 检查是否有自动隐藏的任务栏
|
||||
APPBARDATA appBarData{};
|
||||
appBarData.cbSize = sizeof(appBarData);
|
||||
if (SHAppBarMessage(ABM_GETSTATE, &appBarData) & ABS_AUTOHIDE) {
|
||||
// 检查显示器的一条边
|
||||
auto hasAutohideTaskbar = [&monInfo](UINT edge) -> bool {
|
||||
APPBARDATA data{};
|
||||
data.cbSize = sizeof(data);
|
||||
data.uEdge = edge;
|
||||
data.rc = monInfo.rcMonitor;
|
||||
HWND hTaskbar = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAREX, &data);
|
||||
return hTaskbar != nullptr;
|
||||
};
|
||||
|
||||
static constexpr int AUTO_HIDE_TASKBAR_HEIGHT = 2;
|
||||
|
||||
if (hasAutohideTaskbar(ABE_TOP)) {
|
||||
clientRect.top += AUTO_HIDE_TASKBAR_HEIGHT;
|
||||
}
|
||||
if (hasAutohideTaskbar(ABE_BOTTOM)) {
|
||||
clientRect.bottom -= AUTO_HIDE_TASKBAR_HEIGHT;
|
||||
}
|
||||
if (hasAutohideTaskbar(ABE_LEFT)) {
|
||||
clientRect.left += AUTO_HIDE_TASKBAR_HEIGHT;
|
||||
}
|
||||
if (hasAutohideTaskbar(ABE_RIGHT)) {
|
||||
clientRect.right -= AUTO_HIDE_TASKBAR_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_NCHITTEST:
|
||||
{
|
||||
// 让 OS 处理左右下三边,由于我们移除了标题栏,上边框会被视为客户区
|
||||
LRESULT originalRet = DefWindowProc(_hWnd, WM_NCHITTEST, 0, lParam);
|
||||
if (originalRet != HTCLIENT) {
|
||||
return originalRet;
|
||||
}
|
||||
|
||||
// XAML Islands 和它上面的标题栏窗口都会吞掉鼠标事件,因此能到达这里的唯一机会
|
||||
// 是上边框。保险起见做一些额外检查。
|
||||
|
||||
if (!_isMaximized) {
|
||||
RECT rcWindow;
|
||||
GetWindowRect(_hWnd, &rcWindow);
|
||||
|
||||
if (GET_Y_LPARAM(lParam) < rcWindow.top + _GetResizeHandleHeight()) {
|
||||
return HTTOP;
|
||||
}
|
||||
}
|
||||
|
||||
return HTCAPTION;
|
||||
}
|
||||
case WM_PAINT:
|
||||
{
|
||||
if (Win32Utils::GetOSVersion().IsWin11()) {
|
||||
break;
|
||||
}
|
||||
|
||||
PAINTSTRUCT ps{ 0 };
|
||||
HDC hdc = BeginPaint(_hWnd, &ps);
|
||||
if (!hdc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int topBorderHeight = (int)_GetTopBorderHeight();
|
||||
|
||||
// 在顶部绘制黑色实线以显示系统原始边框,见 _UpdateFrameMargins
|
||||
if (ps.rcPaint.top < topBorderHeight) {
|
||||
RECT rcTopBorder = ps.rcPaint;
|
||||
rcTopBorder.bottom = topBorderHeight;
|
||||
|
||||
static HBRUSH hBrush = GetStockBrush(BLACK_BRUSH);
|
||||
FillRect(hdc, &rcTopBorder, hBrush);
|
||||
}
|
||||
|
||||
// 绘制客户区,它会在调整窗口尺寸时短暂可见
|
||||
if (ps.rcPaint.bottom > topBorderHeight) {
|
||||
RECT rcRest = ps.rcPaint;
|
||||
rcRest.top = topBorderHeight;
|
||||
|
||||
static bool isDarkBrush = _isDarkTheme;
|
||||
static HBRUSH backgroundBrush = CreateSolidBrush(isDarkBrush ?
|
||||
CommonSharedConstants::DARK_TINT_COLOR : CommonSharedConstants::LIGHT_TINT_COLOR);
|
||||
|
||||
if (isDarkBrush != _isDarkTheme) {
|
||||
isDarkBrush = _isDarkTheme;
|
||||
DeleteBrush(backgroundBrush);
|
||||
backgroundBrush = CreateSolidBrush(isDarkBrush ?
|
||||
CommonSharedConstants::DARK_TINT_COLOR : CommonSharedConstants::LIGHT_TINT_COLOR);
|
||||
}
|
||||
|
||||
if (isDarkBrush) {
|
||||
// 这里我们想要黑色背景而不是原始边框
|
||||
// hack 来自 https://github.com/microsoft/terminal/blob/0ee2c74cd432eda153f3f3e77588164cde95044f/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L1030-L1047
|
||||
HDC opaqueDc;
|
||||
BP_PAINTPARAMS params = { sizeof(params), BPPF_NOCLIP | BPPF_ERASE };
|
||||
HPAINTBUFFER buf = BeginBufferedPaint(hdc, &rcRest, BPBF_TOPDOWNDIB, ¶ms, &opaqueDc);
|
||||
if (buf && opaqueDc) {
|
||||
FillRect(opaqueDc, &rcRest, backgroundBrush);
|
||||
BufferedPaintSetAlpha(buf, nullptr, 255);
|
||||
EndBufferedPaint(buf, TRUE);
|
||||
}
|
||||
} else {
|
||||
FillRect(hdc, &rcRest, backgroundBrush);
|
||||
}
|
||||
}
|
||||
|
||||
EndPaint(_hWnd, &ps);
|
||||
return 0;
|
||||
}
|
||||
case WM_SHOWWINDOW:
|
||||
{
|
||||
if (wParam == TRUE) {
|
||||
|
|
@ -123,6 +327,8 @@ protected:
|
|||
}
|
||||
case WM_DPICHANGED:
|
||||
{
|
||||
_currentDpi = HIWORD(wParam);
|
||||
|
||||
RECT* newRect = (RECT*)lParam;
|
||||
SetWindowPos(_hWnd,
|
||||
NULL,
|
||||
|
|
@ -172,8 +378,10 @@ protected:
|
|||
}
|
||||
case WM_SIZE:
|
||||
{
|
||||
_UpdateMaximizedState();
|
||||
|
||||
if (wParam != SIZE_MINIMIZED) {
|
||||
_OnResize();
|
||||
_UpdateIslandPosition(LOWORD(lParam), HIWORD(lParam));
|
||||
|
||||
if (_hwndXamlIsland) {
|
||||
// 使 ContentDialog 跟随窗口尺寸调整
|
||||
|
|
@ -192,6 +400,8 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
_UpdateFrameMargins();
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_DESTROY:
|
||||
|
|
@ -205,6 +415,10 @@ protected:
|
|||
_xamlSource = nullptr;
|
||||
_hwndXamlIsland = NULL;
|
||||
|
||||
_isMaximized = false;
|
||||
_isWindowShown = false;
|
||||
_isDarkTheme = false;
|
||||
|
||||
_content = nullptr;
|
||||
|
||||
_destroyedEvent();
|
||||
|
|
@ -216,14 +430,82 @@ protected:
|
|||
return DefWindowProc(_hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
uint32_t _GetTopBorderHeight() const noexcept {
|
||||
static constexpr uint32_t TOP_BORDER_HEIGHT = 1;
|
||||
|
||||
// Win11 或最大化时没有上边框
|
||||
return (Win32Utils::GetOSVersion().IsWin11() || _isMaximized) ? 0 : TOP_BORDER_HEIGHT;
|
||||
}
|
||||
|
||||
int _GetResizeHandleHeight() noexcept {
|
||||
// 没有 SM_CYPADDEDBORDER
|
||||
return GetSystemMetricsForDpi(SM_CXPADDEDBORDER, _currentDpi) +
|
||||
GetSystemMetricsForDpi(SM_CYSIZEFRAME, _currentDpi);
|
||||
}
|
||||
|
||||
HWND _hWnd = NULL;
|
||||
C _content{ nullptr };
|
||||
|
||||
uint32_t _currentDpi = USER_DEFAULT_SCREEN_DPI;
|
||||
bool _isMaximized = false;
|
||||
bool _isWindowShown = false;
|
||||
bool _isDarkTheme = false;
|
||||
|
||||
private:
|
||||
void _OnResize() noexcept {
|
||||
RECT clientRect;
|
||||
GetClientRect(_hWnd, &clientRect);
|
||||
SetWindowPos(_hwndXamlIsland, NULL, 0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, SWP_SHOWWINDOW | SWP_NOACTIVATE);
|
||||
void _UpdateIslandPosition(int width, int height) const noexcept {
|
||||
if (!IsWindowVisible(_hWnd) && _isMaximized) {
|
||||
// 初始化过程中此函数会被调用两次。如果窗口以最大化显示,则两次传入的尺寸不一致。第一次
|
||||
// 调用此函数时主窗口尚未显示,因此无法最大化,我们必须估算最大化窗口的尺寸。不执行这个
|
||||
// 操作可能导致窗口显示时展示 NavigationView 导航展开的动画。
|
||||
if (HMONITOR hMon = MonitorFromWindow(_hWnd, MONITOR_DEFAULTTONEAREST)) {
|
||||
MONITORINFO monInfo{};
|
||||
monInfo.cbSize = sizeof(MONITORINFO);
|
||||
GetMonitorInfo(hMon, &monInfo);
|
||||
|
||||
// 最大化窗口的尺寸为当前屏幕工作区的尺寸
|
||||
width = monInfo.rcWork.right - monInfo.rcMonitor.left;
|
||||
height = monInfo.rcWork.bottom - monInfo.rcMonitor.top;
|
||||
}
|
||||
}
|
||||
|
||||
int topBorderHeight = _GetTopBorderHeight();
|
||||
|
||||
// SWP_NOZORDER 确保 XAML Islands 窗口始终在标题栏窗口下方,否则主窗口在调整大小时会闪烁
|
||||
SetWindowPos(
|
||||
_hwndXamlIsland,
|
||||
NULL,
|
||||
0,
|
||||
topBorderHeight,
|
||||
width,
|
||||
height - topBorderHeight,
|
||||
SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW
|
||||
);
|
||||
}
|
||||
|
||||
void _UpdateMaximizedState() noexcept {
|
||||
// 如果窗口尚未显示,不碰 _isMaximized
|
||||
if (_isWindowShown) {
|
||||
_isMaximized = IsMaximized(_hWnd);
|
||||
}
|
||||
}
|
||||
|
||||
void _UpdateFrameMargins() const noexcept {
|
||||
if (Win32Utils::GetOSVersion().IsWin11()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MARGINS margins{};
|
||||
if (_GetTopBorderHeight() > 0) {
|
||||
// 在 Win10 中,移除标题栏时上边框也被没了。我们的解决方案是:使用 DwmExtendFrameIntoClientArea
|
||||
// 将边框扩展到客户区,然后在顶部绘制了一个黑色实线来显示系统原始边框(这种情况下操作系统将黑色视
|
||||
// 为透明)。因此我们有**完美**的上边框!
|
||||
// 见 https://docs.microsoft.com/en-us/windows/win32/dwm/customframe#extending-the-client-frame
|
||||
//
|
||||
// 有的软件自己绘制了假的上边框,如 Chromium 系、WinUI 3 等,但窗口失去焦点时边框是半透明的,无法
|
||||
// 完美模拟。
|
||||
margins.cxLeftWidth = -1;
|
||||
}
|
||||
DwmExtendFrameIntoClientArea(_hWnd, &margins);
|
||||
}
|
||||
|
||||
winrt::event<winrt::delegate<>> _destroyedEvent;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
struct CommonSharedConstants {
|
||||
static constexpr const wchar_t* MAIN_WINDOW_CLASS_NAME = L"Magpie_Main";
|
||||
static constexpr const wchar_t* TITLE_BAR_WINDOW_CLASS_NAME = L"Magpie_TitleBar";
|
||||
static constexpr const wchar_t* NOTIFY_ICON_WINDOW_CLASS_NAME = L"Magpie_NotifyIcon";
|
||||
static constexpr const wchar_t* HOTKEY_WINDOW_CLASS_NAME = L"Magpie_Hotkey";
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue