Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

自定义标题栏 #624

Merged
merged 47 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
cc1e03e
feat: 去除标题栏
Blinue May 19, 2023
6fd1ec1
chore: 添加注释
Blinue May 19, 2023
84ea1d1
feat: 保存最大化状态
Blinue May 19, 2023
688cc07
fix: 优化最大化状态
Blinue May 19, 2023
0227eaa
Merge branch 'dev' into frameless
Blinue May 20, 2023
4ecd18b
feat: Win11 无需绘制上边框
Blinue May 21, 2023
7032099
feat: 添加 TitlebarControl
Blinue May 21, 2023
54c6532
UI: 更改主界面样式
Blinue May 21, 2023
d8e8f36
Merge branch 'dev' into frameless
Blinue May 21, 2023
499e077
fix: 修复一个隐蔽的bug
Blinue May 22, 2023
a3f277a
feat: 添加 CaptionButtonsControl
Blinue May 22, 2023
1b3a28c
feat: 优化上边框颜色
Blinue May 22, 2023
c72560c
feat: 实现标题栏的 UI
Blinue May 22, 2023
ad6a3b5
fix: 修复标题栏按钮不跟随主题的问题
Blinue May 22, 2023
e290166
fix: 优化主窗口最小尺寸
Blinue May 22, 2023
e62c85d
Merge branch 'dev' into frameless
Blinue May 23, 2023
621966a
fix: 修复上边框绘制错误
Blinue May 23, 2023
64879a4
UI: 优化样式
Blinue May 23, 2023
80c1275
UI: 稍微优化标题按钮样式
Blinue May 23, 2023
42faf4b
UI: 优化标题按钮样式
Blinue May 24, 2023
649c0a7
UI: 优化标题按钮样式
Blinue May 24, 2023
21b780c
UI: 优化标题栏样式
Blinue May 24, 2023
cad091f
feat: 实现拖拽功能
Blinue May 24, 2023
0fef9c0
fix: 修复调整窗口大小时闪烁的问题
Blinue May 25, 2023
6bc4e3e
fix: 更改上边框的实现方式
Blinue May 27, 2023
c80285e
feat: 实现上边框调整尺寸和支持 Win11 的贴靠布局
Blinue May 27, 2023
f9856b2
feat: 实现标题栏按钮的 hover
Blinue May 27, 2023
d378895
feat: 实现标题栏按钮的功能
Blinue May 27, 2023
c0f9621
perf: 优化性能和添加注释
Blinue May 28, 2023
5fe41fb
fix: 修复一个小错误
Blinue May 28, 2023
44579f0
fix: 小修复
Blinue May 28, 2023
153befc
fix: 优化最大化状态
Blinue May 28, 2023
733c14b
fix: 修复标题栏上右键菜单
Blinue May 28, 2023
d10a795
chore: 添加注释
Blinue May 28, 2023
636bb98
fix: 修复 Win10 中以最大化启动时一瞬间显示主题色背景的问题
Blinue May 29, 2023
c57227c
UI: 更新 ToggleSwitch 样式
Blinue May 29, 2023
f041bd8
fix: 修复以最大化显示时的窗口动画
Blinue May 29, 2023
a2bfe09
fix: 修复 Win11 21H1/21H2 的背景
Blinue May 30, 2023
9aa0fda
chore: 优化注释
Blinue May 30, 2023
51c9725
feat: 在标题栏显示图标
Blinue May 30, 2023
758e595
UI: 为标题栏添加动画
Blinue May 30, 2023
eb758a8
fix: 修复导航栏菜单覆盖标题栏的问题
Blinue May 30, 2023
cc57959
fix: 修复标题按钮下方的可拖动区域
Blinue May 30, 2023
aae0b2b
feat: 导航栏不再支持 Minimal 状态
Blinue May 31, 2023
a54b8a3
chore: 删除不再需要的代码
Blinue May 31, 2023
e5afab2
UI: 修正配置文件页面图标位置
Blinue May 31, 2023
29f6849
docs: 更新主窗口截图
Blinue May 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified img/Main window.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified img/主窗口.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/Magpie.App/App.idl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "ScalingConfigurationPage.idl"
#include "ProfilePage.idl"
#include "SettingsPage.idl"
#include "CaptionButtonsControl.idl"
#include "TitleBarControl.idl"

namespace Magpie.App {
enum ShortcutAction {
Expand Down
625 changes: 623 additions & 2 deletions src/Magpie.App/App.xaml

Large diffs are not rendered by default.

134 changes: 134 additions & 0 deletions src/Magpie.App/CaptionButtonsControl.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 37 additions & 0 deletions src/Magpie.App/CaptionButtonsControl.h
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions src/Magpie.App/CaptionButtonsControl.idl
Original file line number Diff line number Diff line change
@@ -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 changes: 162 additions & 0 deletions src/Magpie.App/CaptionButtonsControl.xaml
Original file line number Diff line number Diff line change
@@ -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">&#xE8BB;</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="&#xE923;" />
</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">&#xE921;</x:String>
</Button.Resources>
</Button>
<Button x:Name="MaximizeButton"
Style="{StaticResource CaptionButton}">
<Button.Resources>
<x:String x:Key="CaptionButtonGlyph">&#xE922;</x:String>
</Button.Resources>
</Button>
<Button x:Name="CloseButton"
Style="{StaticResource CaptionButton}">
<Button.Resources>
<ResourceDictionary>
<x:String x:Key="CaptionButtonGlyph">&#xE8BB;</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>
1 change: 0 additions & 1 deletion src/Magpie.App/IconHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));

{
Expand Down
Loading