diff --git a/src/Avalonia.Controls/Platform/ExtendClientAreaChromeHints.cs b/src/Avalonia.Controls/Platform/ExtendClientAreaChromeHints.cs new file mode 100644 index 0000000000..af33e3093b --- /dev/null +++ b/src/Avalonia.Controls/Platform/ExtendClientAreaChromeHints.cs @@ -0,0 +1,17 @@ +using System; + +namespace Avalonia.Platform +{ + [Flags] + public enum ExtendClientAreaChromeHints + { + Default = SystemTitleBar | SystemChromeButtons, + NoChrome, + SystemTitleBar = 0x01, + SystemChromeButtons = 0x02, + ManagedChromeButtons = 0x04, + PreferSystemChromeButtons = 0x08, + AdaptiveChromeWithTitleBar = SystemTitleBar | PreferSystemChromeButtons, + AdaptiveChromeWithoutTitleBar = PreferSystemChromeButtons, + } +} diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index 9c4808a41c..d7bf93a603 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -95,7 +95,11 @@ namespace Avalonia.Platform /// void SetMinMaxSize(Size minSize, Size maxSize); - bool ExtendClientAreaToDecorationsHint { get; set; } + void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint); + + bool IsClientAreaExtendedToDecorations { get; } + + void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints); Action ExtendClientAreaToDecorationsChanged { get; set; } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 04b48d7f9f..85935d3d2a 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -95,6 +95,9 @@ namespace Avalonia.Controls public static readonly StyledProperty ExtendClientAreaToDecorationsHintProperty = AvaloniaProperty.Register(nameof(ExtendClientAreaToDecorationsHint), false); + public static readonly StyledProperty ExtendClientAreaChromeHintsProperty = + AvaloniaProperty.Register(nameof(ExtendClientAreaChromeHints), ExtendClientAreaChromeHints.Default); + /// /// Defines the property. @@ -190,7 +193,10 @@ namespace Avalonia.Controls (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.WindowState = (WindowState)e.NewValue; }); ExtendClientAreaToDecorationsHintProperty.Changed.AddClassHandler( - (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.ExtendClientAreaToDecorationsHint = (bool)e.NewValue; }); + (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.SetExtendClientAreaToDecorationsHint((bool)e.NewValue); }); + + ExtendClientAreaChromeHintsProperty.Changed.AddClassHandler( + (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.SetExtendClientAreaChromeHints((ExtendClientAreaChromeHints)e.NewValue); }); MinWidthProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size((double)e.NewValue, w.MinHeight), new Size(w.MaxWidth, w.MaxHeight))); MinHeightProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue), new Size(w.MaxWidth, w.MaxHeight))); @@ -275,6 +281,12 @@ namespace Avalonia.Controls set { SetValue(ExtendClientAreaToDecorationsHintProperty, value); } } + public ExtendClientAreaChromeHints ExtendClientAreaChromeHints + { + get => GetValue(ExtendClientAreaChromeHintsProperty); + set => SetValue(ExtendClientAreaChromeHintsProperty, value); + } + /// /// Gets if the ClientArea is Extended into the Window Decorations. /// diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index 441a79531a..06c7ac6411 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -83,14 +83,14 @@ namespace Avalonia.DesignerSupport.Remote } public IScreenImpl Screen { get; } = new ScreenStub(); - public Action GotInputWhenDisabled { get; set; } - - public bool ExtendClientAreaToDecorationsHint { get; set; } + public Action GotInputWhenDisabled { get; set; } public Action ExtendClientAreaToDecorationsChanged { get; set; } public Thickness ExtendedMargins { get; } = new Thickness(); + public bool IsClientAreaExtendedToDecorations { get; } + public void Activate() { } @@ -130,5 +130,13 @@ namespace Avalonia.DesignerSupport.Remote public void SetEnabled(bool enable) { } + + public void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint) + { + } + + public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) + { + } } } diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index f962382575..6f5a0e6051 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -37,9 +37,7 @@ namespace Avalonia.DesignerSupport.Remote public WindowState WindowState { get; set; } public Action WindowStateChanged { get; set; } - public Action TransparencyLevelChanged { get; set; } - - public bool ExtendClientAreaToDecorationsHint { get; set; } + public Action TransparencyLevelChanged { get; set; } public Action ExtendClientAreaToDecorationsChanged { get; set; } @@ -146,6 +144,14 @@ namespace Avalonia.DesignerSupport.Remote { } + public void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint) + { + } + + public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) + { + } + public IPopupPositioner PopupPositioner { get; } public Action GotInputWhenDisabled { get; set; } @@ -157,6 +163,8 @@ namespace Avalonia.DesignerSupport.Remote } public WindowTransparencyLevel TransparencyLevel { get; private set; } + + public bool IsClientAreaExtendedToDecorations { get; } } class ClipboardStub : IClipboard diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index 3495c4b392..47c8291803 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -96,14 +96,22 @@ namespace Avalonia.Native } } - public Action WindowStateChanged { get; set; } - - public bool ExtendClientAreaToDecorationsHint { get; set; } + public Action WindowStateChanged { get; set; } public Action ExtendClientAreaToDecorationsChanged { get; set; } public Thickness ExtendedMargins { get; } = new Thickness(); + public bool IsClientAreaExtendedToDecorations { get; } + + public void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint) + { + } + + public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) + { + } + public void ShowTaskbarIcon(bool value) { // NO OP On OSX diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index ac9048adad..d64f993299 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -309,14 +309,14 @@ namespace Avalonia.X11 { get => _transparencyHelper.TransparencyLevelChanged; set => _transparencyHelper.TransparencyLevelChanged = value; - } - - public bool ExtendClientAreaToDecorationsHint { get; set; } + } public Action ExtendClientAreaToDecorationsChanged { get; set; } public Thickness ExtendedMargins { get; } = new Thickness(); + public bool IsClientAreaExtendedToDecorations { get; } + public Action Closed { get; set; } public Action PositionChanged { get; set; } @@ -1041,6 +1041,14 @@ namespace Avalonia.X11 _disabled = !enable; } + public void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint) + { + } + + public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) + { + } + public Action GotInputWhenDisabled { get; set; } public void SetIcon(IWindowIconImpl icon) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index b66de8e0a8..6f843324f2 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -989,6 +989,12 @@ namespace Avalonia.Win32.Interop } } + [DllImport("user32.dll")] + public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); + + [DllImport("user32.dll")] + public static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable); + [DllImport("user32.dll", SetLastError = true)] public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 7474bdd10a..7c720b5612 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -41,6 +41,7 @@ namespace Avalonia.Win32 private SavedWindowInfo _savedWindowInfo; private bool _isFullScreenActive; private bool _isClientAreaExtended; + private Thickness _extendedMargins; #if USE_MANAGED_DRAG private readonly ManagedWindowResizeDragHelper _managedDrag; @@ -666,7 +667,7 @@ namespace Avalonia.Win32 TaskBarList.MarkFullscreen(_hwnd, fullscreen); } - private void ExtendClientArea () + private void ExtendClientArea (ExtendClientAreaChromeHints hints) { if (!_isClientAreaExtended) { @@ -683,6 +684,16 @@ namespace Avalonia.Win32 AdjustWindowRectEx(ref border_thickness, (uint)(GetStyle()), false, 0); border_thickness.left *= -1; border_thickness.top *= -1; + + border_thickness.left = 1; + border_thickness.bottom = 1; + border_thickness.right = 1; + + if (!hints.HasFlag(ExtendClientAreaChromeHints.SystemTitleBar)) + { + border_thickness.top = 1; + } + } else if (GetStyle().HasFlag(WindowStyles.WS_BORDER)) { @@ -697,6 +708,19 @@ namespace Avalonia.Win32 var hr = DwmExtendFrameIntoClientArea(_hwnd, ref margins); + if(!hints.HasFlag(ExtendClientAreaChromeHints.SystemChromeButtons) || + (hints.HasFlag(ExtendClientAreaChromeHints.PreferSystemChromeButtons) && + !hints.HasFlag(ExtendClientAreaChromeHints.SystemTitleBar))) + { + var style = GetStyle(); + + style &= ~(WindowStyles.WS_MINIMIZEBOX | WindowStyles.WS_MAXIMIZEBOX | WindowStyles.WS_SYSMENU); + + SetStyle(style); + + DisableCloseButton(_hwnd); + } + if (hr == 0) { _isClientAreaExtended = true; @@ -854,9 +878,10 @@ namespace Avalonia.Win32 // Otherwise it will still show in the taskbar. } + WindowStyles style; if ((oldProperties.IsResizable != newProperties.IsResizable) || forceChanges) { - var style = GetStyle(); + style = GetStyle(); if (newProperties.IsResizable) { @@ -877,7 +902,7 @@ namespace Avalonia.Win32 if ((oldProperties.Decorations != newProperties.Decorations) || forceChanges) { - var style = GetStyle(); + style = GetStyle(); const WindowStyles fullDecorationFlags = WindowStyles.WS_CAPTION | WindowStyles.WS_SYSMENU; @@ -922,7 +947,26 @@ namespace Avalonia.Win32 SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); } - } + } + } + + private const int MF_BYCOMMAND = 0x0; + private const int MF_BYPOSITION = 0x400; + private const int MF_REMOVE = 0x1000; + private const int MF_ENABLED = 0x0; + private const int MF_GRAYED = 0x1; + private const int MF_DISABLED = 0x2; + private const int SC_CLOSE = 0xF060; + + void DisableCloseButton(IntPtr hwnd) + { + EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE, + MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + } + void EnableCloseButton(IntPtr hwnd) + { + EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE, + MF_BYCOMMAND | MF_ENABLED); } #if USE_MANAGED_DRAG @@ -946,23 +990,23 @@ namespace Avalonia.Win32 } } - IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle; + IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle; - public bool ExtendClientAreaToDecorationsHint + public void SetExtendClientAreaToDecorationsHint(bool hint) { - get => _extendClientAreaToDecorationsHint; - set - { - _extendClientAreaToDecorationsHint = true; + ExtendClientArea(_extendChromeHints); + } - ExtendClientArea(); - // TODO Trigger transition. - } + private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; + + public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) + { + _extendChromeHints = hints; } - public Action ExtendClientAreaToDecorationsChanged { get; set; } + public bool IsClientAreaExtendedToDecorations => _isClientAreaExtended; - private Thickness _extendedMargins; + public Action ExtendClientAreaToDecorationsChanged { get; set; } public Thickness ExtendedMargins => _extendedMargins;