Enhance TitleBar and CaptionButtons to respect latest Window.CanMaximize and Window.CanMinimize (#815)
* fix: enhance window resizing logic to include maximization check * fix: update CaptionButtons to control button visibility based on window properties * fix: update obsolescence messages for minimize and restore button properties * chore: update comment.
This commit is contained in:
@@ -6,12 +6,12 @@ using Irihi.Avalonia.Shared.Helpers;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
[TemplatePart(PART_CloseButton, typeof (Button))]
|
||||
[TemplatePart(PART_RestoreButton, typeof (Button))]
|
||||
[TemplatePart(PART_MinimizeButton, typeof (Button))]
|
||||
[TemplatePart(PART_FullScreenButton, typeof (Button))]
|
||||
[TemplatePart(PART_CloseButton, typeof(Button))]
|
||||
[TemplatePart(PART_RestoreButton, typeof(Button))]
|
||||
[TemplatePart(PART_MinimizeButton, typeof(Button))]
|
||||
[TemplatePart(PART_FullScreenButton, typeof(Button))]
|
||||
[PseudoClasses(":minimized", ":normal", ":maximized", ":fullscreen")]
|
||||
public class CaptionButtons: Avalonia.Controls.Chrome.CaptionButtons
|
||||
public class CaptionButtons : Avalonia.Controls.Chrome.CaptionButtons
|
||||
{
|
||||
private const string PART_CloseButton = "PART_CloseButton";
|
||||
private const string PART_RestoreButton = "PART_RestoreButton";
|
||||
@@ -19,18 +19,12 @@ public class CaptionButtons: Avalonia.Controls.Chrome.CaptionButtons
|
||||
private const string PART_FullScreenButton = "PART_FullScreenButton";
|
||||
|
||||
private Button? _closeButton;
|
||||
private Button? _restoreButton;
|
||||
private Button? _minimizeButton;
|
||||
private Button? _fullScreenButton;
|
||||
|
||||
private IDisposable? _windowStateSubscription;
|
||||
private IDisposable? _fullScreenSubscription;
|
||||
private IDisposable? _minimizeSubscription;
|
||||
private IDisposable? _restoreSubscription;
|
||||
private IDisposable? _closeSubscription;
|
||||
private Button? _minimizeButton;
|
||||
private Button? _restoreButton;
|
||||
|
||||
/// <summary>
|
||||
/// 切换进入全屏前 窗口的状态
|
||||
/// Stores the previous window state before entering full-screen mode.
|
||||
/// </summary>
|
||||
private WindowState? _oldWindowState;
|
||||
|
||||
@@ -45,10 +39,8 @@ public class CaptionButtons: Avalonia.Controls.Chrome.CaptionButtons
|
||||
Button.ClickEvent.AddHandler((_, _) => OnMinimize(), _minimizeButton);
|
||||
Button.ClickEvent.AddHandler((_, _) => OnToggleFullScreen(), _fullScreenButton);
|
||||
|
||||
if (HostWindow is not null && !HostWindow.CanResize)
|
||||
{
|
||||
if (HostWindow is not null && (!HostWindow.CanResize || !HostWindow.CanMaximize))
|
||||
_restoreButton.IsEnabled = false;
|
||||
}
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
@@ -57,58 +49,59 @@ public class CaptionButtons: Avalonia.Controls.Chrome.CaptionButtons
|
||||
if (HostWindow != null)
|
||||
{
|
||||
if (HostWindow.WindowState != WindowState.FullScreen)
|
||||
{
|
||||
HostWindow.WindowState = WindowState.FullScreen;
|
||||
}
|
||||
else
|
||||
{
|
||||
HostWindow.WindowState = _oldWindowState ?? WindowState.Normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Attach(Window? hostWindow)
|
||||
{
|
||||
if (hostWindow is null) return;
|
||||
base.Attach(hostWindow);
|
||||
_windowStateSubscription = HostWindow?.GetPropertyChangedObservable(Window.WindowStateProperty).Subscribe(OnHostWindowStateChanged);
|
||||
Action<bool> a = _ => UpdateVisibility();
|
||||
_fullScreenSubscription = HostWindow?.GetObservable(UrsaWindow.IsFullScreenButtonVisibleProperty).Subscribe(a);
|
||||
_minimizeSubscription = HostWindow?.GetObservable(UrsaWindow.IsMinimizeButtonVisibleProperty).Subscribe(a);
|
||||
_restoreSubscription = HostWindow?.GetObservable(UrsaWindow.IsRestoreButtonVisibleProperty).Subscribe(a);
|
||||
_closeSubscription = HostWindow?.GetObservable(UrsaWindow.IsCloseButtonVisibleProperty).Subscribe(a);
|
||||
if (HostWindow is not null) HostWindow.PropertyChanged += OnWindowPropertyChanged;
|
||||
}
|
||||
|
||||
private void OnHostWindowStateChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
private void OnWindowPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == Window.WindowStateProperty)
|
||||
{
|
||||
UpdateVisibility();
|
||||
if (e.GetNewValue<WindowState>() == WindowState.FullScreen)
|
||||
{
|
||||
_oldWindowState = e.GetOldValue<WindowState>();
|
||||
if (e.GetNewValue<WindowState>() == WindowState.FullScreen) _oldWindowState = e.GetOldValue<WindowState>();
|
||||
}
|
||||
|
||||
if (e.Property == UrsaWindow.IsFullScreenButtonVisibleProperty
|
||||
|| e.Property == UrsaWindow.IsMinimizeButtonVisibleProperty
|
||||
|| e.Property == UrsaWindow.IsRestoreButtonVisibleProperty
|
||||
|| e.Property == UrsaWindow.IsCloseButtonVisibleProperty
|
||||
|| e.Property == Window.CanMaximizeProperty
|
||||
|| e.Property == Window.CanMinimizeProperty)
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
private void UpdateVisibility()
|
||||
{
|
||||
if (HostWindow is not UrsaWindow u)
|
||||
if (HostWindow is UrsaWindow u)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsVisibleProperty.SetValue(u.IsCloseButtonVisible, _closeButton);
|
||||
IsVisibleProperty.SetValue(u.WindowState != WindowState.FullScreen && u.IsRestoreButtonVisible,
|
||||
IsVisibleProperty.SetValue(
|
||||
u.CanMaximize && u.WindowState != WindowState.FullScreen && u.IsRestoreButtonVisible,
|
||||
_restoreButton);
|
||||
IsVisibleProperty.SetValue(u.WindowState != WindowState.FullScreen && u.IsMinimizeButtonVisible,
|
||||
IsVisibleProperty.SetValue(
|
||||
u.CanMinimize && u.WindowState != WindowState.FullScreen && u.IsMinimizeButtonVisible,
|
||||
_minimizeButton);
|
||||
IsVisibleProperty.SetValue(u.IsFullScreenButtonVisible, _fullScreenButton);
|
||||
}
|
||||
else if (HostWindow is { } s)
|
||||
{
|
||||
IsVisibleProperty.SetValue(s.CanMaximize && s.WindowState != WindowState.FullScreen, _restoreButton);
|
||||
IsVisibleProperty.SetValue(s.CanMinimize && s.WindowState != WindowState.FullScreen, _minimizeButton);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Detach()
|
||||
{
|
||||
if (HostWindow is not null) HostWindow.PropertyChanged -= OnWindowPropertyChanged;
|
||||
base.Detach();
|
||||
_windowStateSubscription?.Dispose();
|
||||
_fullScreenSubscription?.Dispose();
|
||||
_minimizeSubscription?.Dispose();
|
||||
_restoreSubscription?.Dispose();
|
||||
_closeSubscription?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,8 @@ public class WindowThumb: Control
|
||||
|
||||
private void OnTapped(object? sender, TappedEventArgs e)
|
||||
{
|
||||
if (this.VisualRoot is not Window window) return;
|
||||
if (!window.CanResize) return;
|
||||
if (VisualRoot is not Window window) return;
|
||||
if (!window.CanResize || !window.CanMaximize) return;
|
||||
if ( window.WindowState == WindowState.FullScreen) return;
|
||||
window.WindowState = window.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ public class UrsaWindow : Window
|
||||
/// <summary>
|
||||
/// Defines the visibility of the minimize button.
|
||||
/// </summary>
|
||||
[Obsolete("Will be removed in Ursa 2.0. Use Window.CanMinimize property instead.")]
|
||||
public static readonly StyledProperty<bool> IsMinimizeButtonVisibleProperty =
|
||||
AvaloniaProperty.Register<UrsaWindow, bool>(
|
||||
nameof(IsMinimizeButtonVisible), true);
|
||||
@@ -31,6 +32,7 @@ public class UrsaWindow : Window
|
||||
/// <summary>
|
||||
/// Defines the visibility of the restore button.
|
||||
/// </summary>
|
||||
[Obsolete("Will be removed in Ursa 2.0. Use Window.CanMaximize property instead.")]
|
||||
public static readonly StyledProperty<bool> IsRestoreButtonVisibleProperty =
|
||||
AvaloniaProperty.Register<UrsaWindow, bool>(
|
||||
nameof(IsRestoreButtonVisible), true);
|
||||
@@ -101,6 +103,7 @@ public class UrsaWindow : Window
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the minimize button is visible.
|
||||
/// </summary>
|
||||
[Obsolete("Will be removed in Ursa 2.0. Use Window.CanMinimize property instead.")]
|
||||
public bool IsMinimizeButtonVisible
|
||||
{
|
||||
get => GetValue(IsMinimizeButtonVisibleProperty);
|
||||
@@ -110,6 +113,7 @@ public class UrsaWindow : Window
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the restore button is visible.
|
||||
/// </summary>
|
||||
[Obsolete("Will be removed in Ursa 2.0. Use Window.CanMaximize property instead.")]
|
||||
public bool IsRestoreButtonVisible
|
||||
{
|
||||
get => GetValue(IsRestoreButtonVisibleProperty);
|
||||
|
||||
140
tests/HeadlessTest.Ursa/Controls/CaptionButtonsTests/Test.cs
Normal file
140
tests/HeadlessTest.Ursa/Controls/CaptionButtonsTests/Test.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Headless.XUnit;
|
||||
using Avalonia.VisualTree;
|
||||
using Ursa.Controls;
|
||||
|
||||
namespace HeadlessTest.Ursa.Controls.CaptionButtonsTests;
|
||||
|
||||
public class Test
|
||||
{
|
||||
[AvaloniaTheory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(false, false)]
|
||||
public void UrsaWindow_IsRestoreButtonVisible_Should_Control_RestoreButton_Visibility(bool canMaximize, bool expectedVisibility)
|
||||
{
|
||||
var window = new UrsaWindow();
|
||||
var caption = new CaptionButtons();
|
||||
window.Content = caption;
|
||||
caption.Attach(window);
|
||||
window.Show();
|
||||
window.IsRestoreButtonVisible = canMaximize;
|
||||
var restoreButton = caption.GetVisualDescendants().OfType<Button>().FirstOrDefault(b => b.Name == "PART_RestoreButton");
|
||||
Assert.NotNull(restoreButton);
|
||||
Assert.Equal(expectedVisibility, restoreButton.IsVisible);
|
||||
window.IsRestoreButtonVisible = !canMaximize;
|
||||
Assert.Equal(!expectedVisibility, restoreButton.IsVisible);
|
||||
}
|
||||
|
||||
[AvaloniaTheory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(false, false)]
|
||||
public void UrsaWindow_IsMinimizeButtonVisible_Should_Control_MinimizeButton_Visibility(bool canMinimize, bool expectedVisibility)
|
||||
{
|
||||
var window = new UrsaWindow();
|
||||
var caption = new CaptionButtons();
|
||||
window.Content = caption;
|
||||
caption.Attach(window);
|
||||
window.Show();
|
||||
window.IsMinimizeButtonVisible = canMinimize;
|
||||
var minimizeButton = caption.GetVisualDescendants().OfType<Button>().FirstOrDefault(b => b.Name == "PART_MinimizeButton");
|
||||
Assert.NotNull(minimizeButton);
|
||||
Assert.Equal(expectedVisibility, minimizeButton.IsVisible);
|
||||
window.IsMinimizeButtonVisible = !canMinimize;
|
||||
Assert.Equal(!expectedVisibility, minimizeButton.IsVisible);
|
||||
}
|
||||
|
||||
[AvaloniaTheory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(false, false)]
|
||||
public void UrsaWindow_IsCloseButtonVisible_Should_Control_CloseButton_Visibility(bool isVisible,
|
||||
bool expectedVisibility)
|
||||
{
|
||||
var window = new UrsaWindow();
|
||||
var caption = new CaptionButtons();
|
||||
window.Content = caption;
|
||||
caption.Attach(window);
|
||||
window.Show();
|
||||
window.IsCloseButtonVisible = isVisible;
|
||||
var closeButton = caption.GetVisualDescendants().OfType<Button>()
|
||||
.FirstOrDefault(b => b.Name == "PART_CloseButton");
|
||||
Assert.NotNull(closeButton);
|
||||
Assert.Equal(expectedVisibility, closeButton.IsVisible);
|
||||
window.IsCloseButtonVisible = !isVisible;
|
||||
Assert.Equal(!expectedVisibility, closeButton.IsVisible);
|
||||
}
|
||||
|
||||
[AvaloniaTheory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(false, false)]
|
||||
public void UrsaWindow_IsFullScreenButtonVisible_Should_Control_FullScreenButton_Visibility
|
||||
(bool isVisible,
|
||||
bool expectedVisibility)
|
||||
{
|
||||
var window = new UrsaWindow();
|
||||
var caption = new CaptionButtons();
|
||||
window.Content = caption;
|
||||
caption.Attach(window);
|
||||
window.Show();
|
||||
window.IsFullScreenButtonVisible = isVisible;
|
||||
var fullScreenButton = caption.GetVisualDescendants().OfType<Button>()
|
||||
.FirstOrDefault(b => b.Name == "PART_FullScreenButton");
|
||||
Assert.NotNull(fullScreenButton);
|
||||
Assert.Equal(expectedVisibility, fullScreenButton.IsVisible);
|
||||
window.IsFullScreenButtonVisible = !isVisible;
|
||||
Assert.Equal(!expectedVisibility, fullScreenButton.IsVisible);
|
||||
}
|
||||
|
||||
[AvaloniaTheory]
|
||||
[InlineData(WindowState.Normal)]
|
||||
[InlineData(WindowState.Maximized)]
|
||||
public void CaptionButtons_ToggleFullScreen_Should_Set_WindowState(WindowState initialState)
|
||||
{
|
||||
var window = new UrsaWindow();
|
||||
var caption = new CaptionButtons();
|
||||
window.Content = caption;
|
||||
caption.Attach(window);
|
||||
window.Show();
|
||||
window.WindowState = initialState;
|
||||
var fullScreenButton = caption.GetVisualDescendants().OfType<Button>()
|
||||
.FirstOrDefault(b => b.Name == "PART_FullScreenButton");
|
||||
Assert.NotNull(fullScreenButton);
|
||||
fullScreenButton.RaiseEvent(new Avalonia.Interactivity.RoutedEventArgs(Button.ClickEvent));
|
||||
Assert.Equal(WindowState.FullScreen, window.WindowState);
|
||||
fullScreenButton.RaiseEvent(new Avalonia.Interactivity.RoutedEventArgs(Button.ClickEvent));
|
||||
Assert.Equal(initialState, window.WindowState);
|
||||
}
|
||||
|
||||
[AvaloniaTheory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(false, false)]
|
||||
public void Window_CanMaximize_Should_Update_RestoreButton_Visibility(bool canMaximize, bool expectedVisibility)
|
||||
{
|
||||
var window = new UrsaWindow();
|
||||
var caption = new CaptionButtons();
|
||||
window.Content = caption;
|
||||
caption.Attach(window);
|
||||
window.Show();
|
||||
window.CanMaximize = canMaximize;
|
||||
var restoreButton = caption.GetVisualDescendants().OfType<Button>()
|
||||
.FirstOrDefault(b => b.Name == "PART_RestoreButton");
|
||||
Assert.NotNull(restoreButton);
|
||||
Assert.Equal(expectedVisibility, restoreButton.IsVisible);
|
||||
}
|
||||
|
||||
[AvaloniaTheory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(false, false)]
|
||||
public void Window_CanMinimize_Should_Update_MinimizeButton_Visibility(bool canMinimize , bool expectedVisibility)
|
||||
{
|
||||
var window = new UrsaWindow();
|
||||
var caption = new CaptionButtons();
|
||||
window.Content = caption;
|
||||
caption.Attach(window);
|
||||
window.Show();
|
||||
window.CanMinimize = canMinimize;
|
||||
var minimizeButton = caption.GetVisualDescendants().OfType<Button>()
|
||||
.FirstOrDefault(b => b.Name == "PART_MinimizeButton");
|
||||
Assert.NotNull(minimizeButton);
|
||||
Assert.Equal(expectedVisibility, minimizeButton.IsVisible);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user