窗口失去焦点时使标题和标题栏按钮变灰 (#635)

* feat: 窗口失去焦点时使标题和标题栏按钮便灰

* chore: 删除一个 hack,增加一个 hack

* UI: 微调 ExpandedModeThresholdWidth

* fix: 修复不显示边框颜色时深色模式的边框是纯黑而不是半透明的问题

* chore: Debug 配置允许有编译警告
便于实验和调试
This commit is contained in:
刘旭 2023-06-01 18:44:37 +08:00 committed by GitHub
commit 6f88d9d153
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 78 additions and 19 deletions

View file

@ -12,7 +12,6 @@
<SDLCheck>true</SDLCheck>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<!-- 'type' : class 'type1' needs to have dll-interface to be used by clients of class 'type2' -->
<DisableSpecificWarnings>4251</DisableSpecificWarnings>
<!-- /utf-8: 源代码使用 UTF-8 格式 -->

View file

@ -29,12 +29,13 @@ void CaptionButtonsControl::HoverButton(CaptionButton button) {
} else {
_allInNormal = false;
const wchar_t* activeState = _isWindowActive ? L"Normal" : L"NotActive";
VisualStateManager::GoToState(MinimizeButton(),
button == CaptionButton::Minimize ? L"PointerOver" : L"Normal", false);
button == CaptionButton::Minimize ? L"PointerOver" : activeState, false);
VisualStateManager::GoToState(MaximizeButton(),
button == CaptionButton::Maximize ? L"PointerOver" : L"Normal", false);
VisualStateManager::GoToState(CloseButton(),
button == CaptionButton::Close ? L"PointerOver" : L"Normal", false);
button == CaptionButton::Maximize ? L"PointerOver" : activeState, false);
VisualStateManager::GoToState(CloseButton(),
button == CaptionButton::Close ? L"PointerOver" : activeState, false);
}
}
@ -114,9 +115,10 @@ void CaptionButtonsControl::LeaveButtons() {
}
_allInNormal = true;
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
const wchar_t* activeState = _isWindowActive ? L"Normal" : L"NotActive";
VisualStateManager::GoToState(MinimizeButton(), activeState, true);
VisualStateManager::GoToState(MaximizeButton(), activeState, true);
VisualStateManager::GoToState(CloseButton(), activeState, true);
}
void CaptionButtonsControl::IsWindowMaximized(bool value) {
@ -125,10 +127,18 @@ void CaptionButtonsControl::IsWindowMaximized(bool value) {
}
if (VisualStateManager::GoToState(MaximizeButton(),
value ? L"WindowStateMaximized" : L"WindowStateNormal", false))
{
value ? L"WindowStateMaximized" : L"WindowStateNormal", false)) {
_isWindowMaximized = value;
}
}
void CaptionButtonsControl::IsWindowActive(bool value) {
_isWindowActive = value;
const wchar_t* activeState = value ? L"Normal" : L"NotActive";
VisualStateManager::GoToState(MinimizeButton(), activeState, false);
VisualStateManager::GoToState(MaximizeButton(), activeState, false);
VisualStateManager::GoToState(CloseButton(), activeState, false);
}
}

View file

@ -19,12 +19,14 @@ struct CaptionButtonsControl : CaptionButtonsControlT<CaptionButtonsControl> {
void LeaveButtons();
void IsWindowMaximized(bool value);
void IsWindowActive(bool value);
private:
std::optional<CaptionButton> _pressedButton;
// 用于避免重复设置状态
bool _allInNormal = true;
bool _isWindowMaximized = false;
bool _isWindowActive = true;
};
}

View file

@ -18,5 +18,6 @@ namespace Magpie.App {
void LeaveButtons();
void IsWindowMaximized(Boolean value);
void IsWindowActive(Boolean value);
}
}

View file

@ -20,6 +20,9 @@
<SolidColorBrush x:Key="CaptionButtonForegroundPressed"
Opacity="0.7"
Color="{StaticResource CaptionButtonForegroundColor}" />
<SolidColorBrush x:Key="CaptionButtonForegroundNotActive"
Opacity="0.38"
Color="{StaticResource CaptionButtonForegroundColor}" />
<SolidColorBrush x:Key="CaptionButtonBackgroundPointerOver"
Opacity="0.06"
Color="{StaticResource CaptionButtonForegroundColor}" />
@ -37,6 +40,9 @@
<SolidColorBrush x:Key="CaptionButtonForegroundPressed"
Opacity="0.7"
Color="{StaticResource CaptionButtonForegroundColor}" />
<SolidColorBrush x:Key="CaptionButtonForegroundNotActive"
Opacity="0.35"
Color="{StaticResource CaptionButtonForegroundColor}" />
<SolidColorBrush x:Key="CaptionButtonBackgroundPointerOver"
Opacity="0.06"
Color="{StaticResource CaptionButtonForegroundColor}" />
@ -105,7 +111,11 @@
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled" />
<VisualState x:Name="NotActive">
<VisualState.Setters>
<Setter Target="ButtonIcon.Foreground" Value="{ThemeResource CaptionButtonForegroundNotActive}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="MinMaxStates">
@ -117,7 +127,6 @@
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>

View file

@ -89,10 +89,10 @@ void MainPage::InitializeComponent() {
}
void MainPage::Loaded(IInspectable const&, RoutedEventArgs const&) {
MUXC::NavigationView nv = RootNavigationView();
// 修复 WinUI 的汉堡菜单的尺寸 bug
nv.PaneDisplayMode(MUXC::NavigationViewPaneDisplayMode::Auto);
// 消除焦点框
IsTabStop(true);
Focus(FocusState::Programmatic);
IsTabStop(false);
// 设置 NavigationView 内的 Tooltip 的主题
XamlUtils::UpdateThemeOfTooltips(*this, ActualTheme());

View file

@ -14,6 +14,7 @@
Canvas.ZIndex="0"
CompactModeThresholdWidth="0"
DisplayModeChanged="NavigationView_DisplayModeChanged"
ExpandedModeThresholdWidth="950"
IsBackButtonVisible="Collapsed"
ItemInvoked="NavigationView_ItemInvoked"
PaneClosing="NavigationView_PaneClosing"

View file

@ -38,4 +38,9 @@ void TitleBarControl::Loading(FrameworkElement const&, IInspectable const&) {
});
}
void TitleBarControl::IsWindowActive(bool value) {
VisualStateManager::GoToState(*this, value ? L"Active" : L"NotActive", false);
CaptionButtons().IsWindowActive(value);
}
}

View file

@ -19,6 +19,8 @@ struct TitleBarControl : TitleBarControlT<TitleBarControl> {
_propertyChangedEvent.remove(token);
}
void IsWindowActive(bool value);
private:
Imaging::SoftwareBitmapSource _logo{ nullptr };
event<PropertyChangedEventHandler> _propertyChangedEvent;

View file

@ -4,5 +4,7 @@ namespace Magpie.App {
Windows.UI.Xaml.Media.Imaging.SoftwareBitmapSource Logo { get; };
CaptionButtonsControl CaptionButtons { get; };
void IsWindowActive(Boolean value);
}
}

View file

@ -20,7 +20,8 @@
Height="16"
VerticalAlignment="Center"
Source="{x:Bind Logo, Mode=OneWay}" />
<TextBlock Margin="0,0,0,2"
<TextBlock x:Name="TitleTextBlock"
Margin="0,0,0,2"
VerticalAlignment="Center"
FontSize="12"
Text="Magpie" />
@ -31,7 +32,7 @@
VerticalAlignment="Top" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStates">
<VisualStateGroup x:Name="TitleStates">
<VisualStateGroup.Transitions>
<VisualTransition From="Expanded"
To="Compact">
@ -72,6 +73,14 @@
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ActiveStates">
<VisualState x:Name="Active" />
<VisualState x:Name="NotActive">
<VisualState.Setters>
<Setter Target="TitleTextBlock.Foreground" Value="#8E8E8E" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</UserControl>

View file

@ -188,6 +188,11 @@ LRESULT MainWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noex
}
break;
}
case WM_ACTIVATE:
{
_content.TitleBar().IsWindowActive(LOWORD(wParam) != WA_INACTIVE);
break;
}
case WM_DESTROY:
{
XamlApp::Get().SaveSettings();

View file

@ -503,7 +503,14 @@ private:
//
// 有的软件自己绘制了假的上边框,如 Chromium 系、WinUI 3 等,但窗口失去焦点时边框是半透明的,无法
// 完美模拟。
margins.cxLeftWidth = -1;
//
// 我们选择扩展到标题栏高度,这是最好的选择。一个自然的想法是,既然上边框只有一个像素高,我们扩展一
// 个像素即可,可惜因为 DWM 的 bug这会使窗口失去焦点时上边框变为透明。那么能否传一个负值让边框
// 扩展到整个客户区?这大部分情况下可以工作,有一个小 bug不显示边框颜色的设置下深色模式的边框会变
// 为纯黑而不是半透明。
RECT frame{};
AdjustWindowRectExForDpi(&frame, GetWindowStyle(_hWnd), FALSE, 0, _currentDpi);
margins.cyTopHeight = -frame.top;
}
DwmExtendFrameIntoClientArea(_hWnd, &margins);
}

View file

@ -22,6 +22,13 @@
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<!-- Release 下不允许编译警告 -->
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
</ItemDefinitionGroup>
<!-- Conan 依赖 -->
<ImportGroup Label="PropertySheets">