Fix Linux desktop title bar theming

This commit is contained in:
YsLtr 2026-03-25 10:47:43 +08:00
parent 005cb620ec
commit c57a142b3b
2 changed files with 158 additions and 1 deletions

View file

@ -8,6 +8,7 @@
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:view="using:v2rayN.Desktop.Views" xmlns:view="using:v2rayN.Desktop.Views"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
x:Name="mainWindow"
Title="v2rayN" Title="v2rayN"
Width="1200" Width="1200"
Height="800" Height="800"
@ -17,11 +18,84 @@
ShowInTaskbar="True" ShowInTaskbar="True"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
mc:Ignorable="d"> mc:Ignorable="d">
<Window.Styles>
<Style Selector="Border.windowTitleBar">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonDefaultTertiaryForeground}" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
</Style>
<Style Selector="Button.windowTitleBarButton">
<Setter Property="Width" Value="36" />
<Setter Property="Height" Value="32" />
<Setter Property="MinWidth" Value="36" />
<Setter Property="Padding" Value="0" />
<Setter Property="Theme" Value="{DynamicResource BorderlessButton}" />
</Style>
<Style Selector="TextBlock.windowTitleBarGlyph">
<Setter Property="FontSize" Value="14" />
<Setter Property="Foreground" Value="{DynamicResource ButtonDefaultTertiaryForeground}" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</Window.Styles>
<dialogHost:DialogHost <dialogHost:DialogHost
Background="Gray" Background="Gray"
CloseOnClickAway="True" CloseOnClickAway="True"
DisableOpeningAnimation="True"> DisableOpeningAnimation="True">
<DockPanel> <DockPanel LastChildFill="True">
<Border
x:Name="linuxTitleBar"
Classes="windowTitleBar"
DockPanel.Dock="Top"
IsVisible="False">
<Grid ColumnDefinitions="Auto,*,Auto" Height="36">
<Border
Grid.ColumnSpan="2"
Background="Transparent"
DoubleTapped="LinuxTitleBar_DoubleTapped"
PointerPressed="LinuxTitleBar_PointerPressed" />
<StackPanel
Grid.Column="0"
Margin="12,0,0,0"
Orientation="Horizontal"
Spacing="8">
<Image
Width="16"
Height="16"
Source="/Assets/NotifyIcon1.ico" />
<TextBlock
VerticalAlignment="Center"
Text="{Binding Title, ElementName=mainWindow}" />
</StackPanel>
<StackPanel
Grid.Column="2"
Orientation="Horizontal">
<Button
x:Name="btnLinuxMinimize"
Classes="windowTitleBarButton"
Click="BtnLinuxMinimize_Click">
<TextBlock Classes="windowTitleBarGlyph" Text="-" />
</Button>
<Button
x:Name="btnLinuxMaximizeRestore"
Classes="windowTitleBarButton"
Click="BtnLinuxMaximizeRestore_Click">
<TextBlock
x:Name="txtLinuxMaximizeRestore"
Classes="windowTitleBarGlyph"
Text="□" />
</Button>
<Button
x:Name="btnLinuxClose"
Classes="windowTitleBarButton"
Click="BtnLinuxClose_Click">
<TextBlock Classes="windowTitleBarGlyph" Text="×" />
</Button>
</StackPanel>
</Grid>
</Border>
<DockPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Top"> <DockPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Top">
<ContentControl x:Name="conTheme" DockPanel.Dock="Right" /> <ContentControl x:Name="conTheme" DockPanel.Dock="Right" />
<Menu Margin="{StaticResource Margin4}"> <Menu Margin="{StaticResource Margin4}">

View file

@ -10,6 +10,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
{ {
private static Config _config; private static Config _config;
private readonly WindowNotificationManager? _manager; private readonly WindowNotificationManager? _manager;
private IDisposable? _linuxWindowStateSubscription;
private CheckUpdateView? _checkUpdateView; private CheckUpdateView? _checkUpdateView;
private BackupAndRestoreView? _backupAndRestoreView; private BackupAndRestoreView? _backupAndRestoreView;
private bool _blCloseByUser = false; private bool _blCloseByUser = false;
@ -162,6 +163,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
else else
{ {
Title = $"{Utils.GetVersion()}"; Title = $"{Utils.GetVersion()}";
ConfigureLinuxTitleBar();
} }
menuAddServerViaScan.IsVisible = false; menuAddServerViaScan.IsVisible = false;
@ -388,6 +390,81 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
await AppManager.Instance.AppExitAsync(true); await AppManager.Instance.AppExitAsync(true);
} }
private void ConfigureLinuxTitleBar()
{
if (!Utils.IsLinux())
{
return;
}
linuxTitleBar.IsVisible = true;
SystemDecorations = SystemDecorations.BorderOnly;
btnLinuxMaximizeRestore.IsVisible = CanResize;
_linuxWindowStateSubscription = this
.GetObservable(WindowStateProperty)
.Subscribe(_ => UpdateLinuxTitleBarWindowState());
UpdateLinuxTitleBarWindowState();
}
private void UpdateLinuxTitleBarWindowState()
{
txtLinuxMaximizeRestore.Text = WindowState == WindowState.Maximized ? "❐" : "□";
}
private void LinuxTitleBar_PointerPressed(object? sender, PointerPressedEventArgs e)
{
if (!Utils.IsLinux() || e.GetCurrentPoint(this).Properties.IsLeftButtonPressed == false)
{
return;
}
BeginMoveDrag(e);
}
private void LinuxTitleBar_DoubleTapped(object? sender, TappedEventArgs e)
{
if (!Utils.IsLinux() || !CanResize)
{
return;
}
ToggleLinuxWindowState();
}
private void BtnLinuxMinimize_Click(object? sender, RoutedEventArgs e)
{
WindowState = WindowState.Minimized;
}
private void BtnLinuxMaximizeRestore_Click(object? sender, RoutedEventArgs e)
{
ToggleLinuxWindowState();
}
private void BtnLinuxClose_Click(object? sender, RoutedEventArgs e)
{
HideToTray();
}
private void ToggleLinuxWindowState()
{
WindowState = WindowState == WindowState.Maximized
? WindowState.Normal
: WindowState.Maximized;
}
private void HideToTray()
{
foreach (var ownedWindow in OwnedWindows)
{
ownedWindow.Close();
}
Hide();
AppManager.Instance.ShowInTaskbar = false;
}
private void Shutdown(bool obj) private void Shutdown(bool obj)
{ {
if (obj is bool b && _blCloseByUser == false) if (obj is bool b && _blCloseByUser == false)
@ -450,6 +527,12 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
RestoreUI(); RestoreUI();
} }
protected override void OnClosed(EventArgs e)
{
_linuxWindowStateSubscription?.Dispose();
base.OnClosed(e);
}
private void RestoreUI() private void RestoreUI()
{ {
if (_config.UiItem.MainGirdHeight1 > 0 && _config.UiItem.MainGirdHeight2 > 0) if (_config.UiItem.MainGirdHeight1 > 0 && _config.UiItem.MainGirdHeight2 > 0)