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:
committed by
GitHub
parent
df578394f0
commit
1f8701a2fc
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -111,6 +111,8 @@ namespace Avalonia.Win32
|
||||
{
|
||||
ShowInTaskbar = false,
|
||||
IsResizable = false,
|
||||
IsMinimizable = false,
|
||||
IsMaximizable = false,
|
||||
Decorations = SystemDecorations.None,
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user