diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index 1f84574318..3f2c977718 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -45,6 +45,11 @@ namespace Avalonia.Platform /// void ShowTaskbarIcon(bool value); + /// + /// Enables or disables resizing of the window + /// + void CanResize(bool value); + /// /// Gets or sets a method called before the underlying implementation is destroyed. /// Return true to prevent the underlying implementation from closing. diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index c66209d3c6..16ee3a46b3 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -95,6 +95,9 @@ namespace Avalonia.Controls o => o.WindowStartupLocation, (o, v) => o.WindowStartupLocation = v); + public static readonly StyledProperty CanResizeProperty = + AvaloniaProperty.Register(nameof(CanResize), true); + private readonly NameScope _nameScope = new NameScope(); private object _dialogResult; private readonly Size _maxPlatformClientSize; @@ -113,6 +116,8 @@ namespace Avalonia.Controls ShowInTaskbarProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue)); IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); + + CanResizeProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.CanResize((bool)e.NewValue)); } /// @@ -208,6 +213,15 @@ namespace Avalonia.Controls } } + /// + /// Enables or disables resizing of the window + /// + public bool CanResize + { + get { return GetValue(CanResizeProperty); } + set { SetValue(CanResizeProperty, value); } + } + /// /// Gets or sets the icon of the window. /// diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index fc9541abb7..f949d05171 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -93,5 +93,9 @@ namespace Avalonia.DesignerSupport.Remote public void ShowTaskbarIcon(bool value) { } + + public void CanResize(bool value) + { + } } } \ No newline at end of file diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index 560425286e..01c3065486 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -95,6 +95,10 @@ namespace Avalonia.DesignerSupport.Remote public void ShowTaskbarIcon(bool value) { } + + public void CanResize(bool value) + { + } } class ClipboardStub : IClipboard diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index d4618e7bc1..f33623dd91 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -115,6 +115,8 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_set_title(GtkWindow gtkWindow, Utf8Buffer title); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_set_resizable(GtkWindow gtkWindow, bool resizable); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_set_decorated(GtkWindow gtkWindow, bool decorated); @@ -395,6 +397,7 @@ namespace Avalonia.Gtk3.Interop public static D.gdk_screen_get_monitor_geometry GdkScreenGetMonitorGeometry; public static D.gdk_screen_get_monitor_workarea GdkScreenGetMonitorWorkarea; public static D.gtk_window_set_decorated GtkWindowSetDecorated; + public static D.gtk_window_set_resizable GtkWindowSetResizable; public static D.gtk_window_set_skip_taskbar_hint GtkWindowSetSkipTaskbarHint; public static D.gtk_window_get_skip_taskbar_hint GtkWindowGetSkipTaskbarHint; public static D.gtk_window_set_skip_pager_hint GtkWindowSetSkipPagerHint; diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index c586661a7a..2d309e19d4 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -61,6 +61,8 @@ namespace Avalonia.Gtk3 } public void ShowTaskbarIcon(bool value) => Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value); + + public void CanResize(bool value) => Native.GtkWindowSetResizable(GtkWidget, value); class EmptyDisposable : IDisposable diff --git a/src/OSX/Avalonia.MonoMac/WindowImpl.cs b/src/OSX/Avalonia.MonoMac/WindowImpl.cs index 6825fce82e..d01cbd6ae3 100644 --- a/src/OSX/Avalonia.MonoMac/WindowImpl.cs +++ b/src/OSX/Avalonia.MonoMac/WindowImpl.cs @@ -9,6 +9,7 @@ namespace Avalonia.MonoMac class WindowImpl : WindowBaseImpl, IWindowImpl { public bool IsDecorated = true; + public bool IsResizable = true; public CGRect? UndecoratedLastUnmaximizedFrame; public WindowImpl() @@ -76,10 +77,15 @@ namespace Avalonia.MonoMac protected override NSWindowStyle GetStyle() { + var windowStyle = NSWindowStyle.Borderless; + if (IsDecorated) - return NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Miniaturizable | - NSWindowStyle.Titled; - return NSWindowStyle.Borderless; + windowStyle |= NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Titled; + + if (IsResizable) + windowStyle |= NSWindowStyle.Resizable; + + return windowStyle; } public void SetSystemDecorations(bool enabled) @@ -88,6 +94,12 @@ namespace Avalonia.MonoMac UpdateStyle(); } + public void CanResize(bool value) + { + IsResizable = value; + UpdateStyle(); + } + public void SetTitle(string title) => Window.Title = title; class ModalDisposable : IDisposable diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index c5ef006b84..3639cfab55 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -558,7 +558,18 @@ namespace Avalonia.Win32.Interop { DIB_RGB_COLORS = 0, /* color table in RGBs */ DIB_PAL_COLORS /* color table in palette indices */ - }; + } + + public enum WindowLongParam + { + GWL_WNDPROC = -4, + GWL_HINSTANCE = -6, + GWL_HWNDPARENT = -8, + GWL_ID = -12, + GWL_STYLE = -16, + GWL_EXSTYLE = -20, + GWL_USERDATA = -21 + } [StructLayout(LayoutKind.Sequential)] public struct RGBQUAD diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index bb3c4cf6e6..ce1eaed13c 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -31,6 +31,7 @@ namespace Avalonia.Win32 private IInputRoot _owner; private bool _trackingMouse; private bool _decorated = true; + private bool _resizable = true; private double _scaling = 1; private WindowState _showWindowState; private FramebufferManager _framebuffer; @@ -77,8 +78,8 @@ namespace Avalonia.Win32 { get { - var style = UnmanagedMethods.GetWindowLong(_hwnd, -16); - var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, -20); + var style = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE); + var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE); var padding = new UnmanagedMethods.RECT(); if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle)) @@ -235,13 +236,19 @@ namespace Avalonia.Win32 return; } - var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, -16); + var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE); - style |= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW; + var systemDecorationStyles = UnmanagedMethods.WindowStyles.WS_OVERLAPPED + | UnmanagedMethods.WindowStyles.WS_CAPTION + | UnmanagedMethods.WindowStyles.WS_SYSMENU + | UnmanagedMethods.WindowStyles.WS_MINIMIZEBOX + | UnmanagedMethods.WindowStyles.WS_MAXIMIZEBOX; + + style |= systemDecorationStyles; if (!value) { - style ^= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW; + style ^= systemDecorationStyles; } UnmanagedMethods.RECT windowRect; @@ -251,7 +258,7 @@ namespace Avalonia.Win32 Rect newRect; var oldThickness = BorderThickness; - UnmanagedMethods.SetWindowLong(_hwnd, -16, (uint)style); + UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE, (uint)style); if (value) { @@ -798,11 +805,11 @@ namespace Avalonia.Win32 public void ShowTaskbarIcon(bool value) { - var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, -20); - - style &= ~(UnmanagedMethods.WindowStyles.WS_VISIBLE); + var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE); - style |= UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW; + style &= ~(UnmanagedMethods.WindowStyles.WS_VISIBLE); + + style |= UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW; if (value) style |= UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW; else @@ -813,9 +820,27 @@ namespace Avalonia.Win32 { //Toggle to make the styles stick UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide); - UnmanagedMethods.SetWindowLong(_hwnd, -20, (uint)style); + UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE, (uint)style); UnmanagedMethods.ShowWindow(_hwnd, windowPlacement.ShowCmd); } } + + public void CanResize(bool value) + { + if (value == _resizable) + { + return; + } + + var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE); + + if (value) + style |= UnmanagedMethods.WindowStyles.WS_SIZEFRAME; + else + style &= ~(UnmanagedMethods.WindowStyles.WS_SIZEFRAME); + + UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE, (uint)style); + _resizable = value; + } } }