Add Window.CanMinimize/CanMaximize (#18117)

* Add CanMinimize and CanMaximize to Window

* Win32 impl for CanMinimize/CanMaximize

* Add CanResize/CanMinimize/CanMaximize samples to control catalog

* X11 impl for CanMinimize/CanMaximize

* macOS impl for CanMinimize/CanMaximize

* Win32: don't allow restore when the window isn't resizable

* Additional documentation for CanMinimize/CanMaximize

* Add CanMinimize/CanMaximize logic to CaptionButtons

* Use START_COM_ARP_CALL

* Added CanMinimize/CanMaximize integration tests

* Fixed CanMaximize tests on macOS
This commit is contained in:
Julien Lebosquain
2025-08-13 10:35:52 +02:00
committed by GitHub
parent df578394f0
commit 1f8701a2fc
22 changed files with 421 additions and 57 deletions
+7 -1
View File
@@ -45,6 +45,10 @@ BEGIN_INTERFACE_MAP()
void DoZoom();
virtual HRESULT SetCanResize(bool value) override;
virtual HRESULT SetCanMinimize(bool value) override;
virtual HRESULT SetCanMaximize(bool value) override;
virtual HRESULT SetDecorations(SystemDecorations value) override;
@@ -82,7 +86,7 @@ BEGIN_INTERFACE_MAP()
bool CanBecomeKeyWindow ();
bool CanZoom() override { return _isEnabled && _canResize; }
bool CanZoom() override { return _isEnabled && _canMaximize; }
protected:
virtual NSWindowStyleMask CalculateStyleMask() override;
@@ -94,6 +98,8 @@ private:
NSString *_lastTitle;
bool _isEnabled;
bool _canResize;
bool _canMinimize;
bool _canMaximize;
bool _fullScreenActive;
SystemDecorations _decorations;
AvnWindowState _lastWindowState;
+23 -4
View File
@@ -16,6 +16,8 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events) : TopLevelImpl(events), WindowB
_extendClientHints = AvnDefaultChrome;
_fullScreenActive = false;
_canResize = true;
_canMinimize = true;
_canMaximize = true;
_decorations = SystemDecorationsFull;
_transitioningWindowState = false;
_inSetWindowState = false;
@@ -191,7 +193,8 @@ bool WindowImpl::IsZoomed() {
void WindowImpl::DoZoom() {
if (_decorations == SystemDecorationsNone ||
_decorations == SystemDecorationsBorderOnly ||
_canResize == false) {
_canResize == false ||
_canMaximize == false) {
[Window setFrame:[Window screen].visibleFrame display:true];
} else {
[Window performZoom:Window];
@@ -208,6 +211,22 @@ HRESULT WindowImpl::SetCanResize(bool value) {
}
}
HRESULT WindowImpl::SetCanMinimize(bool value) {
START_COM_ARP_CALL;
_canMinimize = value;
UpdateAppearance();
return S_OK;
}
HRESULT WindowImpl::SetCanMaximize(bool value) {
START_COM_ARP_CALL;
_canMaximize = value;
UpdateAppearance();
return S_OK;
}
HRESULT WindowImpl::SetDecorations(SystemDecorations value) {
START_COM_CALL;
@@ -583,7 +602,7 @@ NSWindowStyleMask WindowImpl::CalculateStyleMask() {
break;
}
if (!IsOwned()) {
if (_canMinimize && !IsOwned()) {
s |= NSWindowStyleMaskMiniaturizable;
}
@@ -611,9 +630,9 @@ void WindowImpl::UpdateAppearance() {
[closeButton setHidden:!hasTrafficLights];
[closeButton setEnabled:_isEnabled];
[miniaturizeButton setHidden:!hasTrafficLights];
[miniaturizeButton setEnabled:_isEnabled];
[miniaturizeButton setEnabled:_isEnabled && _canMinimize];
[zoomButton setHidden:!hasTrafficLights];
[zoomButton setEnabled:CanZoom()];
[zoomButton setEnabled:CanZoom() || (([Window styleMask] & NSWindowStyleMaskFullScreen) != 0 && _isEnabled)];
}
extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events)
+3
View File
@@ -10,6 +10,9 @@
ExtendClientAreaToDecorationsHint="{Binding ExtendClientAreaEnabled}"
ExtendClientAreaChromeHints="{Binding ChromeHints}"
ExtendClientAreaTitleBarHeightHint="{Binding TitleBarHeight}"
CanResize="{Binding CanResize}"
CanMinimize="{Binding CanMinimize}"
CanMaximize="{Binding CanMaximize}"
x:Name="MainWindow"
Background="Transparent"
x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}"
@@ -8,18 +8,55 @@
x:DataType="viewModels:MainWindowViewModel"
x:CompileBindings="True">
<StackPanel>
<StackPanel Spacing="10" Margin="25" IsEnabled="{OnFormFactor false, Desktop=true}">
<TextBlock Classes="h2" Text="Desktop properties" Margin="4" />
<CheckBox Content="Extend Client Area to Decorations" IsChecked="{Binding ExtendClientAreaEnabled}" />
<CheckBox Content="Title Bar" IsChecked="{Binding SystemTitleBarEnabled}" />
<CheckBox Content="Prefer System Chrome" IsChecked="{Binding PreferSystemChromeEnabled}" />
<Slider Minimum="-1" Maximum="200" Value="{Binding TitleBarHeight}" />
<StackPanel
Spacing="10"
Margin="25"
IsEnabled="{OnFormFactor false, Desktop=true}">
<TextBlock Classes="h2"
Text="Desktop properties"
Margin="4" />
<CheckBox Content="Extend Client Area to Decorations"
IsChecked="{Binding ExtendClientAreaEnabled}" />
<DockPanel IsEnabled="{Binding ExtendClientAreaEnabled}">
<CheckBox Content="Title Bar"
IsChecked="{Binding SystemTitleBarEnabled}"
DockPanel.Dock="Left" />
<Slider Minimum="-1"
Maximum="200"
Value="{Binding TitleBarHeight}"
IsEnabled="{Binding SystemTitleBarEnabled}"
Margin="8,-10" />
</DockPanel>
<CheckBox Content="Prefer System Chrome"
IsChecked="{Binding PreferSystemChromeEnabled}"
IsEnabled="{Binding ExtendClientAreaEnabled}" />
<CheckBox Content="Can Resize"
IsChecked="{Binding CanResize}" />
<CheckBox Content="Can Minimize"
IsChecked="{Binding CanMinimize}" />
<CheckBox Content="Can Maximize"
IsChecked="{Binding CanMaximize}"
IsEnabled="{Binding CanResize}" />
</StackPanel>
<StackPanel Spacing="10" Margin="25" IsEnabled="{OnFormFactor false, Mobile=true}">
<TextBlock Classes="h2" Text="Mobile properties" Margin="4" />
<CheckBox Content="Is System Bar Visible" IsChecked="{Binding IsSystemBarVisible}" />
<CheckBox Content="Display Edge To Edge" IsChecked="{Binding DisplayEdgeToEdge}" />
<TextBlock Text="{Binding SafeAreaPadding, StringFormat='Safe Area Padding: {0}'}" />
</StackPanel>
</StackPanel>
</UserControl>
@@ -1,6 +1,5 @@
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Notifications;
using Avalonia.Dialogs;
using Avalonia.Platform;
using System;
@@ -22,6 +21,9 @@ namespace ControlCatalog.ViewModels
private bool _isSystemBarVisible;
private bool _displayEdgeToEdge;
private Thickness _safeAreaPadding;
private bool _canResize;
private bool _canMinimize;
private bool _canMaximize;
public MainWindowViewModel()
{
@@ -49,7 +51,7 @@ namespace ControlCatalog.ViewModels
WindowState.FullScreen,
};
this.PropertyChanged += (s, e) =>
PropertyChanged += (s, e) =>
{
if (e.PropertyName is nameof(SystemTitleBarEnabled) or nameof(PreferSystemChromeEnabled))
{
@@ -67,70 +69,104 @@ namespace ControlCatalog.ViewModels
}
};
SystemTitleBarEnabled = true;
SystemTitleBarEnabled = true;
TitleBarHeight = -1;
CanResize = true;
CanMinimize = true;
CanMaximize = true;
}
public ExtendClientAreaChromeHints ChromeHints
{
get { return _chromeHints; }
set { this.RaiseAndSetIfChanged(ref _chromeHints, value); }
set { RaiseAndSetIfChanged(ref _chromeHints, value); }
}
public bool ExtendClientAreaEnabled
{
get { return _extendClientAreaEnabled; }
set { this.RaiseAndSetIfChanged(ref _extendClientAreaEnabled, value); }
set
{
if (RaiseAndSetIfChanged(ref _extendClientAreaEnabled, value) && !value)
{
SystemTitleBarEnabled = true;
}
}
}
public bool SystemTitleBarEnabled
{
get { return _systemTitleBarEnabled; }
set { this.RaiseAndSetIfChanged(ref _systemTitleBarEnabled, value); }
set
{
if (RaiseAndSetIfChanged(ref _systemTitleBarEnabled, value) && !value)
{
TitleBarHeight = -1;
}
}
}
public bool PreferSystemChromeEnabled
{
get { return _preferSystemChromeEnabled; }
set { this.RaiseAndSetIfChanged(ref _preferSystemChromeEnabled, value); }
set { RaiseAndSetIfChanged(ref _preferSystemChromeEnabled, value); }
}
public double TitleBarHeight
{
get { return _titleBarHeight; }
set { this.RaiseAndSetIfChanged(ref _titleBarHeight, value); }
set { RaiseAndSetIfChanged(ref _titleBarHeight, value); }
}
public WindowState WindowState
{
get { return _windowState; }
set { this.RaiseAndSetIfChanged(ref _windowState, value); }
set { RaiseAndSetIfChanged(ref _windowState, value); }
}
public WindowState[] WindowStates
{
get { return _windowStates; }
set { this.RaiseAndSetIfChanged(ref _windowStates, value); }
set { RaiseAndSetIfChanged(ref _windowStates, value); }
}
public bool IsSystemBarVisible
{
get { return _isSystemBarVisible; }
set { this.RaiseAndSetIfChanged(ref _isSystemBarVisible, value); }
set { RaiseAndSetIfChanged(ref _isSystemBarVisible, value); }
}
public bool DisplayEdgeToEdge
{
get { return _displayEdgeToEdge; }
set { this.RaiseAndSetIfChanged(ref _displayEdgeToEdge, value); }
set { RaiseAndSetIfChanged(ref _displayEdgeToEdge, value); }
}
public Thickness SafeAreaPadding
{
get { return _safeAreaPadding; }
set { this.RaiseAndSetIfChanged(ref _safeAreaPadding, value); }
set { RaiseAndSetIfChanged(ref _safeAreaPadding, value); }
}
public bool CanResize
{
get { return _canResize; }
set { RaiseAndSetIfChanged(ref _canResize, value); }
}
public bool CanMinimize
{
get { return _canMinimize; }
set { RaiseAndSetIfChanged(ref _canMinimize, value); }
}
public bool CanMaximize
{
get { return _canMaximize; }
set { RaiseAndSetIfChanged(ref _canMaximize, value); }
}
public MiniCommand AboutCommand { get; }
public MiniCommand ExitCommand { get; }
@@ -144,7 +180,7 @@ namespace ControlCatalog.ViewModels
public DateTime? ValidatedDateExample
{
get => _validatedDateExample;
set => this.RaiseAndSetIfChanged(ref _validatedDateExample, value);
set => RaiseAndSetIfChanged(ref _validatedDateExample, value);
}
}
}
@@ -30,14 +30,16 @@
</ComboBox>
<CheckBox Name="ShowWindowExtendClientAreaToDecorationsHint">ExtendClientAreaToDecorationsHint</CheckBox>
<CheckBox Name="ShowWindowCanResize" IsChecked="True">Can Resize</CheckBox>
<CheckBox Name="ShowWindowCanMinimize" IsChecked="True">Can Minimize</CheckBox>
<CheckBox Name="ShowWindowCanMaximize" IsChecked="True">Can Maximize</CheckBox>
</StackPanel>
<StackPanel Grid.Column="2">
<Button Name="ShowWindow" Click="ShowWindow_Click">Show Window</Button>
<Button Name="SendToBack" Click="SendToBack_Click">Send to Back</Button>
<Button Name="EnterFullscreen" Click="EnterFullscreen_Click">Enter Fullscreen</Button>
<Button Name="ExitFullscreen" Click="ExitFullscreen_Click">Exit Fullscreen</Button>
<Button Name="RestoreAll" Click="RestoreAll_Click">Restore All</Button>
<Button Name="ShowTopmostWindow" Click="ShowTopmostWindow_Click">Show Topmost Window</Button>
</StackPanel>
<StackPanel Grid.Column="2">
<Button Name="ShowTransparentWindow" Click="ShowTransparentWindow_Click">Transparent Window</Button>
<Button Name="ShowTransparentPopup" Click="ShowTransparentPopup_Click">Transparent Popup</Button>
</StackPanel>
@@ -23,10 +23,13 @@ public partial class WindowPage : UserControl
private void ShowWindow_Click(object? sender, RoutedEventArgs e)
{
var size = !string.IsNullOrWhiteSpace(ShowWindowSize.Text) ? Size.Parse(ShowWindowSize.Text) : (Size?)null;
var canResize = ShowWindowCanResize.IsChecked ?? false;
var window = new ShowWindowTest
{
WindowStartupLocation = (WindowStartupLocation)ShowWindowLocation.SelectedIndex,
CanResize = ShowWindowCanResize.IsChecked ?? false,
CanResize = canResize,
CanMinimize = ShowWindowCanMinimize.IsChecked ?? false,
CanMaximize = canResize && (ShowWindowCanMaximize.IsChecked ?? false)
};
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
+50 -5
View File
@@ -21,6 +21,8 @@ namespace Avalonia.Controls.Chrome
internal const string PART_FullScreenButton = "PART_FullScreenButton";
private Button? _restoreButton;
private Button? _minimizeButton;
private Button? _fullScreenButton;
private IDisposable? _disposables;
/// <summary>
@@ -36,11 +38,16 @@ namespace Avalonia.Controls.Chrome
_disposables = new CompositeDisposable
{
HostWindow.GetObservable(Window.CanResizeProperty)
.Subscribe(x =>
HostWindow.GetObservable(Window.CanMaximizeProperty)
.Subscribe(_ =>
{
if (_restoreButton is not null)
_restoreButton.IsEnabled = x;
UpdateRestoreButtonState();
UpdateFullScreenButtonState();
}),
HostWindow.GetObservable(Window.CanMinimizeProperty)
.Subscribe(_ =>
{
UpdateMinimizeButtonState();
}),
HostWindow.GetObservable(Window.WindowStateProperty)
.Subscribe(x =>
@@ -49,6 +56,9 @@ namespace Avalonia.Controls.Chrome
PseudoClasses.Set(":normal", x == WindowState.Normal);
PseudoClasses.Set(":maximized", x == WindowState.Maximized);
PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
UpdateRestoreButtonState();
UpdateMinimizeButtonState();
UpdateFullScreenButtonState();
}),
};
}
@@ -116,8 +126,8 @@ namespace Avalonia.Controls.Chrome
OnRestore();
args.Handled = true;
};
restoreButton.IsEnabled = HostWindow?.CanResize ?? true;
_restoreButton = restoreButton;
UpdateRestoreButtonState();
}
if (e.NameScope.Find<Button>(PART_MinimizeButton) is { } minimizeButton)
@@ -127,6 +137,8 @@ namespace Avalonia.Controls.Chrome
OnMinimize();
args.Handled = true;
};
_minimizeButton = minimizeButton;
UpdateMinimizeButtonState();
}
if (e.NameScope.Find<Button>(PART_FullScreenButton) is { } fullScreenButton)
@@ -136,7 +148,40 @@ namespace Avalonia.Controls.Chrome
OnToggleFullScreen();
args.Handled = true;
};
_fullScreenButton = fullScreenButton;
UpdateFullScreenButtonState();
}
}
private void UpdateRestoreButtonState()
{
if (_restoreButton is null)
return;
_restoreButton.IsEnabled = HostWindow?.WindowState switch
{
WindowState.Maximized or WindowState.FullScreen => HostWindow.CanResize,
WindowState.Normal => HostWindow.CanMaximize,
_ => true
};
}
private void UpdateMinimizeButtonState()
{
if (_minimizeButton is null)
return;
_minimizeButton.IsEnabled = HostWindow?.CanMinimize ?? true;
}
private void UpdateFullScreenButtonState()
{
if (_fullScreenButton is null)
return;
_fullScreenButton.IsEnabled = HostWindow?.WindowState == WindowState.FullScreen ?
HostWindow.CanResize :
HostWindow?.CanMaximize ?? true;
}
}
}
+10 -1
View File
@@ -64,6 +64,16 @@ namespace Avalonia.Platform
/// </summary>
void CanResize(bool value);
/// <summary>
/// Enables or disables minimizing the window.
/// </summary>
void SetCanMinimize(bool value);
/// <summary>
/// Enables or disables maximizing the window.
/// </summary>
void SetCanMaximize(bool value);
/// <summary>
/// Gets or sets a method called before the underlying implementation is destroyed.
/// Return true to prevent the underlying implementation from closing.
@@ -124,7 +134,6 @@ namespace Avalonia.Platform
/// <summary>
/// Minimum width of the window.
/// </summary>
///
void SetMinMaxSize(Size minSize, Size maxSize);
/// <summary>
+52 -1
View File
@@ -181,9 +181,24 @@ namespace Avalonia.Controls
public static readonly StyledProperty<WindowStartupLocation> WindowStartupLocationProperty =
AvaloniaProperty.Register<Window, WindowStartupLocation>(nameof(WindowStartupLocation));
/// <summary>
/// Defines the <see cref="CanResize"/> property.
/// </summary>
public static readonly StyledProperty<bool> CanResizeProperty =
AvaloniaProperty.Register<Window, bool>(nameof(CanResize), true);
/// <summary>
/// Defines the <see cref="CanMinimize"/> property.
/// </summary>
public static readonly StyledProperty<bool> CanMinimizeProperty =
AvaloniaProperty.Register<Window, bool>(nameof(CanMinimize), true);
/// <summary>
/// Defines the <see cref="CanMaximize"/> property.
/// </summary>
public static readonly StyledProperty<bool> CanMaximizeProperty =
AvaloniaProperty.Register<Window, bool>(nameof(CanMaximize), true, coerce: CoerceCanMaximize);
/// <summary>
/// Routed event that can be used for global tracking of window destruction
/// </summary>
@@ -236,6 +251,8 @@ namespace Avalonia.Controls
CreatePlatformImplBinding(TitleProperty, title => PlatformImpl!.SetTitle(title));
CreatePlatformImplBinding(IconProperty, icon => PlatformImpl!.SetIcon((icon ?? s_defaultIcon.Value)?.PlatformImpl));
CreatePlatformImplBinding(CanResizeProperty, canResize => PlatformImpl!.CanResize(canResize));
CreatePlatformImplBinding(CanMinimizeProperty, canMinimize => PlatformImpl!.SetCanMinimize(canMinimize));
CreatePlatformImplBinding(CanMaximizeProperty, canMaximize => PlatformImpl!.SetCanMaximize(canMaximize));
CreatePlatformImplBinding(ShowInTaskbarProperty, show => PlatformImpl!.ShowTaskbarIcon(show));
CreatePlatformImplBinding(WindowStateProperty, state => PlatformImpl!.WindowState = state);
@@ -407,6 +424,32 @@ namespace Avalonia.Controls
set => SetValue(CanResizeProperty, value);
}
/// <summary>
/// Enables or disables minimizing the window.
/// </summary>
/// <remarks>
/// This property might be ignored by some window managers on Linux.
/// </remarks>
public bool CanMinimize
{
get => GetValue(CanMinimizeProperty);
set => SetValue(CanMinimizeProperty, value);
}
/// <summary>
/// Enables or disables maximizing the window.
/// </summary>
/// <remarks>
/// <para>When <see cref="CanResize"/> is false, this property is always false.</para>
/// <para>On macOS, setting this property to false also disables the full screen mode.</para>
/// <para>This property might be ignored by some window managers on Linux.</para>
/// </remarks>
public bool CanMaximize
{
get => GetValue(CanMaximizeProperty);
set => SetValue(CanMaximizeProperty, value);
}
/// <summary>
/// Gets or sets the icon of the window.
/// </summary>
@@ -1184,7 +1227,7 @@ namespace Avalonia.Controls
PlatformImpl?.SetSystemDecorations(typedNewValue);
}
if (change.Property == OwnerProperty)
else if (change.Property == OwnerProperty)
{
var oldParent = change.OldValue as Window;
var newParent = change.NewValue as Window;
@@ -1197,6 +1240,11 @@ namespace Avalonia.Controls
impl.SetParent(_showingAsDialog ? newParent?.PlatformImpl! : (newParent?.PlatformImpl ?? null));
}
}
else if (change.Property == CanResizeProperty)
{
CoerceValue(CanMaximizeProperty);
}
}
protected override AutomationPeer OnCreateAutomationPeer()
@@ -1217,5 +1265,8 @@ namespace Avalonia.Controls
}
return null;
}
private static bool CoerceCanMaximize(AvaloniaObject target, bool value)
=> value && target is not Window { CanResize: false };
}
}
@@ -133,6 +133,14 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public void SetCanMinimize(bool value)
{
}
public void SetCanMaximize(bool value)
{
}
public void SetTopmost(bool value)
{
}
@@ -152,6 +152,14 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public void SetCanMinimize(bool value)
{
}
public void SetCanMaximize(bool value)
{
}
public void SetTopmost(bool value)
{
}
+22 -3
View File
@@ -18,6 +18,7 @@ namespace Avalonia.Native
private DoubleClickHelper _doubleClickHelper;
private readonly ITopLevelNativeMenuExporter _nativeMenuExporter;
private bool _canResize = true;
private bool _canMaximize = true;
internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(factory)
{
@@ -77,6 +78,17 @@ namespace Avalonia.Native
_native.SetCanResize(value.AsComBool());
}
public void SetCanMinimize(bool value)
{
_native.SetCanMinimize(value.AsComBool());
}
public void SetCanMaximize(bool value)
{
_canMaximize = value;
_native.SetCanMaximize(value.AsComBool());
}
public void SetSystemDecorations(Controls.SystemDecorations enabled)
{
_native.SetDecorations((Interop.SystemDecorations)enabled);
@@ -138,10 +150,17 @@ namespace Avalonia.Native
{
if (_doubleClickHelper.IsDoubleClick(e.Timestamp, e.Position))
{
if (_canResize)
switch (WindowState)
{
WindowState = WindowState is WindowState.Maximized or WindowState.FullScreen ?
WindowState.Normal : WindowState.Maximized;
case WindowState.Maximized or WindowState.FullScreen
when _canResize:
WindowState = WindowState.Normal;
break;
case WindowState.Normal
when _canMaximize:
WindowState = WindowState.Maximized;
break;
}
}
else
+2
View File
@@ -772,6 +772,8 @@ interface IAvnWindow : IAvnWindowBase
{
HRESULT SetEnabled(bool enable);
HRESULT SetCanResize(bool value);
HRESULT SetCanMinimize(bool value);
HRESULT SetCanMaximize(bool value);
HRESULT SetDecorations(SystemDecorations value);
HRESULT SetTitle(char* utf8Title);
HRESULT SetTitleBarColor(AvnColor color);
@@ -112,7 +112,7 @@
<Style Selector="^:fullscreen /template/ Button#PART_MinimizeButton">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^ /template/ Button#PART_RestoreButton:disabled">
<Style Selector="^ /template/ Button:disabled">
<Setter Property="Opacity" Value="0.2"/>
</Style>
</ControlTheme>
+32 -12
View File
@@ -316,22 +316,28 @@ namespace Avalonia.X11
|| _systemDecorations == SystemDecorations.None)
decorations = 0;
if (!_canResize || !IsEnabled)
{
functions &= ~(MotifFunctions.Resize | MotifFunctions.Maximize);
decorations &= ~(MotifDecorations.Maximize | MotifDecorations.ResizeH);
}
if (!IsEnabled)
{
functions &= ~(MotifFunctions.Resize | MotifFunctions.Minimize);
var isDisabled = !IsEnabled;
UpdateSizeHints(null, true);
}
else
if (!_canResize || isDisabled)
{
UpdateSizeHints(null);
functions &= ~MotifFunctions.Resize;
decorations &= ~MotifDecorations.ResizeH;
}
if (!_canMinimize || isDisabled)
{
functions &= ~MotifFunctions.Minimize;
decorations &= ~MotifDecorations.Minimize;
}
if (!_canMaximize || isDisabled)
{
functions &= ~MotifFunctions.Maximize;
decorations &= ~MotifDecorations.Maximize;
}
UpdateSizeHints(null, isDisabled);
var hints = new MotifWmHints
{
flags = new IntPtr((int)(MotifFlags.Decorations | MotifFlags.Functions)),
@@ -857,6 +863,8 @@ namespace Avalonia.X11
private SystemDecorations _systemDecorations = SystemDecorations.Full;
private bool _canResize = true;
private bool _canMinimize = true;
private bool _canMaximize = true;
private const int MaxWindowDimension = 100000;
private (Size minSize, Size maxSize) _scaledMinMaxSize =
@@ -1162,6 +1170,18 @@ namespace Avalonia.X11
UpdateSizeHints(null);
}
public void SetCanMinimize(bool value)
{
_canMinimize = value;
UpdateMotifHints();
}
public void SetCanMaximize(bool value)
{
_canMaximize = value;
UpdateMotifHints();
}
public void SetCursor(ICursorImpl? cursor)
{
if (cursor == null)
@@ -165,6 +165,14 @@ namespace Avalonia.Headless
}
public void SetCanMinimize(bool value)
{
}
public void SetCanMaximize(bool value)
{
}
public Func<WindowCloseReason, bool>? Closing { get; set; }
private class FramebufferProxy : ILockedFramebuffer
@@ -12,6 +12,8 @@ namespace Avalonia.Win32
{
ShowInTaskbar = false,
IsResizable = false,
IsMinimizable = false,
IsMaximizable = false,
Decorations = SystemDecorations.None
};
}
+2
View File
@@ -111,6 +111,8 @@ namespace Avalonia.Win32
{
ShowInTaskbar = false,
IsResizable = false,
IsMinimizable = false,
IsMaximizable = false,
Decorations = SystemDecorations.None,
};
+31 -5
View File
@@ -136,6 +136,8 @@ namespace Avalonia.Win32
{
ShowInTaskbar = false,
IsResizable = true,
IsMinimizable = true,
IsMaximizable = true,
Decorations = SystemDecorations.Full
};
@@ -858,6 +860,24 @@ namespace Avalonia.Win32
UpdateWindowProperties(newWindowProperties);
}
public void SetCanMinimize(bool value)
{
var newWindowProperties = _windowProperties;
newWindowProperties.IsMinimizable = value;
UpdateWindowProperties(newWindowProperties);
}
public void SetCanMaximize(bool value)
{
var newWindowProperties = _windowProperties;
newWindowProperties.IsMaximizable = value;
UpdateWindowProperties(newWindowProperties);
}
public void SetSystemDecorations(SystemDecorations value)
{
var newWindowProperties = _windowProperties;
@@ -1425,15 +1445,19 @@ namespace Avalonia.Win32
style |= WindowStyles.WS_VISIBLE;
if (newProperties.IsResizable || newProperties.WindowState == WindowState.Maximized)
{
style |= WindowStyles.WS_THICKFRAME;
style |= WindowStyles.WS_MAXIMIZEBOX;
}
else
{
style &= ~WindowStyles.WS_THICKFRAME;
if (newProperties.IsMinimizable)
style |= WindowStyles.WS_MINIMIZEBOX;
else
style &= ~WindowStyles.WS_MINIMIZEBOX;
if (newProperties.IsMaximizable || (newProperties.WindowState == WindowState.Maximized && newProperties.IsResizable))
style |= WindowStyles.WS_MAXIMIZEBOX;
else
style &= ~WindowStyles.WS_MAXIMIZEBOX;
}
const WindowStyles fullDecorationFlags = WindowStyles.WS_CAPTION | WindowStyles.WS_SYSMENU | WindowStyles.WS_BORDER;
@@ -1656,6 +1680,8 @@ namespace Avalonia.Win32
{
public bool ShowInTaskbar;
public bool IsResizable;
public bool IsMinimizable;
public bool IsMaximizable;
public SystemDecorations Decorations;
public bool IsFullScreen;
public WindowState WindowState;
@@ -672,6 +672,23 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void CanMaximize_Should_Be_False_If_CanResize_Is_False()
{
var windowImpl = MockWindowingPlatform.CreateWindowMock();
using var app = UnitTestApplication.Start(TestServices.StyledWindow.With(
windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object)));
var window = new Window();
Assert.True(window.CanMaximize);
window.CanResize = false;
Assert.False(window.CanMaximize);
}
public class SizingTests : ScopedTestBase
{
[Fact]
@@ -311,6 +311,37 @@ namespace Avalonia.IntegrationTests.Appium
}
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void Window_Minimize_Button_Enabled_Matches_CanMinimize(bool canMinimize)
{
using (OpenWindow(canMinimize: canMinimize))
{
var secondaryWindow = Session.GetWindowById("SecondaryWindow");
var windowChrome = secondaryWindow.GetSystemChromeButtons();
Assert.NotNull(windowChrome.Minimize);
Assert.Equal(canMinimize, windowChrome.Minimize!.Enabled);
}
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void Window_Maximize_Button_Enabled_Matches_CanMaximize(bool canMaximize)
{
using (OpenWindow(canMaximize: canMaximize))
{
var secondaryWindow = Session.GetWindowById("SecondaryWindow");
var windowChrome = secondaryWindow.GetSystemChromeButtons();
var maximizeButton = windowChrome.FullScreen ?? windowChrome.Maximize;
Assert.NotNull(maximizeButton);
Assert.Equal(canMaximize, maximizeButton.Enabled);
}
}
[Fact]
public void Changing_SystemDecorations_Should_Not_Change_Frame_Size_And_Position()
{
@@ -452,13 +483,17 @@ namespace Avalonia.IntegrationTests.Appium
WindowStartupLocation location = WindowStartupLocation.Manual,
WindowState state = Controls.WindowState.Normal,
bool canResize = true,
bool extendClientArea = false)
bool extendClientArea = false,
bool canMinimize = true,
bool canMaximize = true)
{
var sizeTextBox = Session.FindElementByAccessibilityId("ShowWindowSize");
var modeComboBox = Session.FindElementByAccessibilityId("ShowWindowMode");
var locationComboBox = Session.FindElementByAccessibilityId("ShowWindowLocation");
var stateComboBox = Session.FindElementByAccessibilityId("ShowWindowState");
var canResizeCheckBox = Session.FindElementByAccessibilityId("ShowWindowCanResize");
var canMinimizeCheckBox = Session.FindElementByAccessibilityId("ShowWindowCanMinimize");
var canMaximizeCheckBox = Session.FindElementByAccessibilityId("ShowWindowCanMaximize");
var showButton = Session.FindElementByAccessibilityId("ShowWindow");
var extendClientAreaCheckBox = Session.FindElementByAccessibilityId("ShowWindowExtendClientAreaToDecorationsHint");
@@ -486,6 +521,12 @@ namespace Avalonia.IntegrationTests.Appium
if (canResizeCheckBox.GetIsChecked() != canResize)
canResizeCheckBox.Click();
if (canMinimizeCheckBox.GetIsChecked() != canMinimize)
canMinimizeCheckBox.Click();
if (canMaximizeCheckBox.GetIsChecked() != canMaximize)
canMaximizeCheckBox.Click();
if (extendClientAreaCheckBox.GetIsChecked() != extendClientArea)
extendClientAreaCheckBox.Click();