Browse Source

Merge branch 'master' into feature/textbox-selectedtext

Steven Kirk 5 years ago
parent
commit
2bd9372170

+ 7 - 1
native/Avalonia.Native/inc/avalonia-native.h

@@ -25,6 +25,12 @@ struct IAvnGlSurfaceRenderingSession;
 struct IAvnAppMenu;
 struct IAvnAppMenuItem;
 
+enum SystemDecorations {
+    SystemDecorationsNone = 0,
+    SystemDecorationsBorderOnly = 1,
+    SystemDecorationsFull = 2,
+};
+
 struct AvnSize
 {
     double Width, Height;
@@ -236,7 +242,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
 {
     virtual HRESULT ShowDialog (IAvnWindow* parent) = 0;
     virtual HRESULT SetCanResize(bool value) = 0;
-    virtual HRESULT SetHasDecorations(bool value) = 0;
+    virtual HRESULT SetHasDecorations(SystemDecorations value) = 0;
     virtual HRESULT SetTitle (void* utf8Title) = 0;
     virtual HRESULT SetTitleBarColor (AvnColor color) = 0;
     virtual HRESULT SetWindowState(AvnWindowState state) = 0;

+ 60 - 21
native/Avalonia.Native/src/OSX/window.mm

@@ -115,7 +115,6 @@ public:
             [NSApp activateIgnoringOtherApps:YES];
             
             [Window setTitle:_lastTitle];
-            [Window setTitleVisibility:NSWindowTitleVisible];
         
             return S_OK;
         }
@@ -411,7 +410,7 @@ class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, pub
 {
 private:
     bool _canResize = true;
-    bool _hasDecorations = true;
+    SystemDecorations _hasDecorations = SystemDecorationsFull;
     CGRect _lastUndecoratedFrame;
     AvnWindowState _lastWindowState;
     
@@ -476,23 +475,26 @@ private:
     
     bool IsZoomed ()
     {
-        return _hasDecorations ? [Window isZoomed] : UndecoratedIsMaximized();
+        return _hasDecorations != SystemDecorationsNone ? [Window isZoomed] : UndecoratedIsMaximized();
     }
     
     void DoZoom()
     {
-        if (_hasDecorations)
+        switch (_hasDecorations)
         {
-            [Window performZoom:Window];
-        }
-        else
-        {
-            if (!UndecoratedIsMaximized())
-            {
-                _lastUndecoratedFrame = [Window frame];
-            }
-            
-            [Window zoom:Window];
+            case SystemDecorationsNone:
+                if (!UndecoratedIsMaximized())
+                {
+                    _lastUndecoratedFrame = [Window frame];
+                }
+                
+                [Window zoom:Window];
+                break;
+
+            case SystemDecorationsBorderOnly:
+            case SystemDecorationsFull:
+                [Window performZoom:Window];
+                break;
         }
     }
     
@@ -506,13 +508,35 @@ private:
         }
     }
     
-    virtual HRESULT SetHasDecorations(bool value) override
+    virtual HRESULT SetHasDecorations(SystemDecorations value) override
     {
         @autoreleasepool
         {
             _hasDecorations = value;
             UpdateStyle();
-            
+
+            switch (_hasDecorations)
+            {
+                case SystemDecorationsNone:
+                    [Window setHasShadow:NO];
+                    [Window setTitleVisibility:NSWindowTitleHidden];
+                    [Window setTitlebarAppearsTransparent:YES];
+                    break;
+
+                case SystemDecorationsBorderOnly:
+                    [Window setHasShadow:YES];
+                    [Window setTitleVisibility:NSWindowTitleHidden];
+                    [Window setTitlebarAppearsTransparent:YES];
+                    break;
+
+                case SystemDecorationsFull:
+                    [Window setHasShadow:YES];
+                    [Window setTitleVisibility:NSWindowTitleVisible];
+                    [Window setTitlebarAppearsTransparent:NO];
+                    [Window setTitle:_lastTitle];
+                    break;
+            }
+
             return S_OK;
         }
     }
@@ -523,7 +547,6 @@ private:
         {
             _lastTitle = [NSString stringWithUTF8String:(const char*)utf8title];
             [Window setTitle:_lastTitle];
-            [Window setTitleVisibility:NSWindowTitleVisible];
             
             return S_OK;
         }
@@ -645,10 +668,26 @@ protected:
     virtual NSWindowStyleMask GetStyle() override
     {
         unsigned long s = NSWindowStyleMaskBorderless;
-        if(_hasDecorations)
-            s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
-        if(_canResize)
-            s = s | NSWindowStyleMaskResizable;
+
+        switch (_hasDecorations)
+        {
+            case SystemDecorationsNone:
+                break;
+
+            case SystemDecorationsBorderOnly:
+                s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView;
+                break;
+
+            case SystemDecorationsFull:
+                s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskBorderless;
+                if(_canResize)
+                {
+                    s = s | NSWindowStyleMaskResizable;
+                }
+
+                break;
+        }
+
         return s;
     }
 };

+ 9 - 2
samples/ControlCatalog/MainView.xaml

@@ -59,10 +59,17 @@
       <TabItem Header="TreeView"><pages:TreeViewPage/></TabItem>
       <TabItem Header="Viewbox"><pages:ViewboxPage/></TabItem>
       <TabControl.Tag>
-        <ComboBox x:Name="Themes" SelectedIndex="0" Width="100" Margin="8" HorizontalAlignment="Right" VerticalAlignment="Bottom">
+        <StackPanel Width="115" Margin="8" HorizontalAlignment="Right" VerticalAlignment="Bottom">
+          <ComboBox x:Name="Decorations" SelectedIndex="0" Margin="0,0,0,8">
+            <ComboBoxItem>No Decorations</ComboBoxItem>
+            <ComboBoxItem>Border Only</ComboBoxItem>
+            <ComboBoxItem>Full Decorations</ComboBoxItem>
+          </ComboBox>
+          <ComboBox x:Name="Themes" SelectedIndex="0">
             <ComboBoxItem>Light</ComboBoxItem>
             <ComboBoxItem>Dark</ComboBoxItem>
-        </ComboBox>
+          </ComboBox>
+        </StackPanel>
       </TabControl.Tag>
     </TabControl>
   </Grid>

+ 14 - 0
samples/ControlCatalog/MainView.xaml.cs

@@ -56,6 +56,20 @@ namespace ControlCatalog
                 }
             };
             Styles.Add(light);
+
+            var decorations = this.Find<ComboBox>("Decorations");
+            decorations.SelectionChanged += (sender, e) =>
+            {
+                Window window = (Window)VisualRoot;
+                window.SystemDecorations = (SystemDecorations)decorations.SelectedIndex;
+            };
+        }
+
+        protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+        {
+            base.OnAttachedToVisualTree(e);
+            var decorations = this.Find<ComboBox>("Decorations");
+            decorations.SelectedIndex = (int)((Window)VisualRoot).SystemDecorations;
         }
     }
 }

+ 5 - 1
src/Avalonia.Base/AvaloniaObject.cs

@@ -80,8 +80,12 @@ namespace Avalonia
                     _inheritanceParent?.RemoveInheritanceChild(this);
                     _inheritanceParent = value;
 
-                    foreach (var property in AvaloniaPropertyRegistry.Instance.GetRegisteredInherited(GetType()))
+                    var properties = AvaloniaPropertyRegistry.Instance.GetRegisteredInherited(GetType());
+                    var propertiesCount = properties.Count;
+
+                    for (var i = 0; i < propertiesCount; i++)
                     {
+                        var property = properties[i];
                         if (valuestore?.IsSet(property) == true)
                         {
                             // If local value set there can be no change.

+ 47 - 41
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@@ -3,9 +3,7 @@
 
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using System.Runtime.CompilerServices;
-using Avalonia.Data;
 
 namespace Avalonia
 {
@@ -28,8 +26,6 @@ namespace Avalonia
             new Dictionary<Type, List<AvaloniaProperty>>();
         private readonly Dictionary<Type, List<AvaloniaProperty>> _directCache =
             new Dictionary<Type, List<AvaloniaProperty>>();
-        private readonly Dictionary<Type, List<PropertyInitializationData>> _initializedCache =
-            new Dictionary<Type, List<PropertyInitializationData>>();
         private readonly Dictionary<Type, List<AvaloniaProperty>> _inheritedCache =
             new Dictionary<Type, List<AvaloniaProperty>>();
 
@@ -49,7 +45,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="type">The type.</param>
         /// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
-        public IEnumerable<AvaloniaProperty> GetRegistered(Type type)
+        public IReadOnlyList<AvaloniaProperty> GetRegistered(Type type)
         {
             Contract.Requires<ArgumentNullException>(type != null);
 
@@ -83,7 +79,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="type">The type.</param>
         /// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
-        public IEnumerable<AvaloniaProperty> GetRegisteredAttached(Type type)
+        public IReadOnlyList<AvaloniaProperty> GetRegisteredAttached(Type type)
         {
             Contract.Requires<ArgumentNullException>(type != null);
 
@@ -114,7 +110,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="type">The type.</param>
         /// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
-        public IEnumerable<AvaloniaProperty> GetRegisteredDirect(Type type)
+        public IReadOnlyList<AvaloniaProperty> GetRegisteredDirect(Type type)
         {
             Contract.Requires<ArgumentNullException>(type != null);
 
@@ -145,7 +141,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="type">The type.</param>
         /// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
-        public IEnumerable<AvaloniaProperty> GetRegisteredInherited(Type type)
+        public IReadOnlyList<AvaloniaProperty> GetRegisteredInherited(Type type)
         {
             Contract.Requires<ArgumentNullException>(type != null);
 
@@ -157,16 +153,27 @@ namespace Avalonia
             result = new List<AvaloniaProperty>();
             var visited = new HashSet<AvaloniaProperty>();
 
-            foreach (var property in GetRegistered(type))
+            var registered = GetRegistered(type);
+            var registeredCount = registered.Count;
+
+            for (var i = 0; i < registeredCount; i++)
             {
+                var property = registered[i];
+
                 if (property.Inherits)
                 {
                     result.Add(property);
                     visited.Add(property);
                 }
             }
-            foreach (var property in GetRegisteredAttached(type))
+
+            var registeredAttached = GetRegisteredAttached(type);
+            var registeredAttachedCount = registeredAttached.Count;
+
+            for (var i = 0; i < registeredAttachedCount; i++)
             {
+                var property = registeredAttached[i];
+
                 if (property.Inherits)
                 {
                     if (!visited.Contains(property))
@@ -185,7 +192,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="o">The object.</param>
         /// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
-        public IEnumerable<AvaloniaProperty> GetRegistered(IAvaloniaObject o)
+        public IReadOnlyList<AvaloniaProperty> GetRegistered(IAvaloniaObject o)
         {
             Contract.Requires<ArgumentNullException>(o != null);
 
@@ -229,8 +236,13 @@ namespace Avalonia
                 throw new InvalidOperationException("Attached properties not supported.");
             }
 
-            foreach (AvaloniaProperty x in GetRegistered(type))
+            var registered = GetRegistered(type);
+            var registeredCount = registered.Count;
+
+            for (var i = 0; i < registeredCount; i++)
             {
+                AvaloniaProperty x = registered[i];
+
                 if (x.Name == name)
                 {
                     return x;
@@ -276,8 +288,13 @@ namespace Avalonia
                 return property;
             }
 
-            foreach (var p in GetRegisteredDirect(o.GetType()))
+            var registeredDirect = GetRegisteredDirect(o.GetType());
+            var registeredDirectCount = registeredDirect.Count;
+
+            for (var i = 0; i < registeredDirectCount; i++)
             {
+                var p = registeredDirect[i];
+
                 if (p == property)
                 {
                     return (DirectPropertyBase<T>)p;
@@ -308,8 +325,23 @@ namespace Avalonia
             Contract.Requires<ArgumentNullException>(type != null);
             Contract.Requires<ArgumentNullException>(property != null);
 
-            return Instance.GetRegistered(type).Any(x => x == property) ||
-                Instance.GetRegisteredAttached(type).Any(x => x == property);
+            static bool ContainsProperty(IReadOnlyList<AvaloniaProperty> properties, AvaloniaProperty property)
+            {
+                var propertiesCount = properties.Count;
+
+                for (var i = 0; i < propertiesCount; i++)
+                {
+                    if (properties[i] == property)
+                    {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            return ContainsProperty(Instance.GetRegistered(type), property) ||
+                   ContainsProperty(Instance.GetRegisteredAttached(type), property);
         }
 
         /// <summary>
@@ -374,7 +406,6 @@ namespace Avalonia
             }
             
             _registeredCache.Clear();
-            _initializedCache.Clear();
             _inheritedCache.Clear();
         }
 
@@ -411,32 +442,7 @@ namespace Avalonia
             }
             
             _attachedCache.Clear();
-            _initializedCache.Clear();
             _inheritedCache.Clear();
         }
-
-        private readonly struct PropertyInitializationData
-        {
-            public AvaloniaProperty Property { get; }
-            public object Value { get; }
-            public bool IsDirect { get; }
-            public IDirectPropertyAccessor DirectAccessor { get; }
-
-            public PropertyInitializationData(AvaloniaProperty property, IDirectPropertyAccessor directAccessor)
-            {
-                Property = property;
-                Value = null;
-                IsDirect = true;
-                DirectAccessor = directAccessor;
-            }
-
-            public PropertyInitializationData(AvaloniaProperty property, IStyledPropertyAccessor styledAccessor, Type type)
-            {
-                Property = property;
-                Value = styledAccessor.GetDefaultValue(type);
-                IsDirect = false;
-                DirectAccessor = null;
-            }
-        }
     }
 }

+ 1 - 1
src/Avalonia.Controls/Platform/IWindowImpl.cs

@@ -36,7 +36,7 @@ namespace Avalonia.Platform
         /// <summary>
         /// Enables or disables system window decorations (title bar, buttons, etc)
         /// </summary>
-        void SetSystemDecorations(bool enabled);
+        void SetSystemDecorations(SystemDecorations enabled);
 
         /// <summary>
         /// Sets the icon of this window.

+ 21 - 4
src/Avalonia.Controls/TextBox.cs

@@ -699,12 +699,12 @@ namespace Avalonia.Controls
 
         protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
-            var point = e.GetPosition(_presenter);
-            var index = CaretIndex = _presenter.GetCaretIndex(point);
             var text = Text;
 
-            if (text != null && e.MouseButton == MouseButton.Left)
+            if (text != null && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
             {
+                var point = e.GetPosition(_presenter);
+                var index = CaretIndex = _presenter.GetCaretIndex(point);
                 switch (e.ClickCount)
                 {
                     case 1:
@@ -730,7 +730,8 @@ namespace Avalonia.Controls
 
         protected override void OnPointerMoved(PointerEventArgs e)
         {
-            if (_presenter != null && e.Pointer.Captured == _presenter)
+            // selection should not change during pointer move if the user right clicks
+            if (_presenter != null && e.Pointer.Captured == _presenter && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
             {
                 var point = e.GetPosition(_presenter);
 
@@ -743,6 +744,22 @@ namespace Avalonia.Controls
         {
             if (_presenter != null && e.Pointer.Captured == _presenter)
             {
+                if (e.InitialPressMouseButton == MouseButton.Right)
+                {
+                    var point = e.GetPosition(_presenter);
+                    var caretIndex = _presenter.GetCaretIndex(point);
+
+                    // see if mouse clicked inside current selection
+                    // if it did not, we change the selection to where the user clicked
+                    var firstSelection = Math.Min(SelectionStart, SelectionEnd);
+                    var lastSelection = Math.Max(SelectionStart, SelectionEnd);
+                    var didClickInSelection = SelectionStart != SelectionEnd && 
+                        caretIndex >= firstSelection && caretIndex <= lastSelection;
+                    if (!didClickInSelection)
+                    {
+                        CaretIndex = SelectionEnd = SelectionStart = caretIndex;
+                    }
+                }
                 e.Pointer.Capture(null);
             }
         }

+ 82 - 14
src/Avalonia.Controls/Window.cs

@@ -2,19 +2,19 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.ComponentModel;
+using System.Linq;
 using System.Reactive.Linq;
 using System.Threading.Tasks;
 using Avalonia.Controls.Platform;
+using Avalonia.Data;
 using Avalonia.Input;
+using Avalonia.Interactivity;
 using Avalonia.Layout;
 using Avalonia.Media;
 using Avalonia.Platform;
 using Avalonia.Styling;
-using System.Collections.Generic;
-using System.Linq;
 using JetBrains.Annotations;
-using System.ComponentModel;
-using Avalonia.Interactivity;
 
 namespace Avalonia.Controls
 {
@@ -45,6 +45,27 @@ namespace Avalonia.Controls
         WidthAndHeight = 3,
     }
 
+    /// <summary>
+    /// Determines system decorations (title bar, border, etc) for a <see cref="Window"/>
+    /// </summary>
+    public enum SystemDecorations
+    {
+        /// <summary>
+        /// No decorations
+        /// </summary>
+        None = 0,
+
+        /// <summary>
+        /// Window border without titlebar
+        /// </summary>
+        BorderOnly = 1,
+
+        /// <summary>
+        /// Fully decorated (default)
+        /// </summary>
+        Full = 2
+    }
+
     /// <summary>
     /// A top-level window.
     /// </summary>
@@ -59,8 +80,18 @@ namespace Avalonia.Controls
         /// <summary>
         /// Enables or disables system window decorations (title bar, buttons, etc)
         /// </summary>
-        public static readonly StyledProperty<bool> HasSystemDecorationsProperty =
-            AvaloniaProperty.Register<Window, bool>(nameof(HasSystemDecorations), true);
+        [Obsolete("Use SystemDecorationsProperty instead")]
+        public static readonly DirectProperty<Window, bool> HasSystemDecorationsProperty =
+            AvaloniaProperty.RegisterDirect<Window, bool>(
+                nameof(HasSystemDecorations),
+                o => o.HasSystemDecorations,
+                (o, v) => o.HasSystemDecorations = v);
+
+        /// <summary>
+        /// Defines the <see cref="SystemDecorations"/> property.
+        /// </summary>
+        public static readonly StyledProperty<SystemDecorations> SystemDecorationsProperty =
+            AvaloniaProperty.Register<Window, SystemDecorations>(nameof(SystemDecorations), SystemDecorations.Full);
 
         /// <summary>
         /// Enables or disables the taskbar icon
@@ -124,9 +155,6 @@ namespace Avalonia.Controls
         {
             BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
             TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue));
-            HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
-                (s, e) => s.PlatformImpl?.SetSystemDecorations((bool)e.NewValue));
-
             ShowInTaskbarProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue));
 
             IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue)?.PlatformImpl));
@@ -135,12 +163,11 @@ namespace Avalonia.Controls
 
             WindowStateProperty.Changed.AddClassHandler<Window>(
                 (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.WindowState = (WindowState)e.NewValue; });
-            
+
             MinWidthProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size((double)e.NewValue, w.MinHeight), new Size(w.MaxWidth, w.MaxHeight)));
             MinHeightProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue), new Size(w.MaxWidth, w.MaxHeight)));
             MaxWidthProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size((double)e.NewValue, w.MaxHeight)));
             MaxHeightProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size(w.MaxWidth, (double)e.NewValue)));
-
         }
 
         /// <summary>
@@ -191,11 +218,30 @@ namespace Avalonia.Controls
         /// <summary>
         /// Enables or disables system window decorations (title bar, buttons, etc)
         /// </summary>
-        /// 
+        [Obsolete("Use SystemDecorations instead")]
         public bool HasSystemDecorations
         {
-            get { return GetValue(HasSystemDecorationsProperty); }
-            set { SetValue(HasSystemDecorationsProperty, value); }
+            get => SystemDecorations == SystemDecorations.Full;
+            set
+            {
+                var oldValue = HasSystemDecorations;
+
+                if (oldValue != value)
+                {
+                    SystemDecorations = value ? SystemDecorations.Full : SystemDecorations.None;
+                    RaisePropertyChanged(HasSystemDecorationsProperty, oldValue, value);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Sets the system decorations (title bar, border, etc)
+        /// </summary>
+        /// 
+        public SystemDecorations SystemDecorations
+        {
+            get { return GetValue(SystemDecorationsProperty); }
+            set { SetValue(SystemDecorationsProperty, value); }
         }
 
         /// <summary>
@@ -584,5 +630,27 @@ namespace Avalonia.Controls
         /// <see cref="Closing"/> event needs to be raised.
         /// </remarks>
         protected virtual void OnClosing(CancelEventArgs e) => Closing?.Invoke(this, e);
+
+        protected override void OnPropertyChanged<T>(
+            AvaloniaProperty<T> property,
+            Optional<T> oldValue,
+            BindingValue<T> newValue,
+            BindingPriority priority)
+        {
+            if (property == SystemDecorationsProperty)
+            {
+                var typedNewValue = newValue.GetValueOrDefault<SystemDecorations>();
+
+                PlatformImpl?.SetSystemDecorations(typedNewValue);
+
+                var o = oldValue.GetValueOrDefault<SystemDecorations>() == SystemDecorations.Full;
+                var n = typedNewValue == SystemDecorations.Full;
+
+                if (o != n)
+                {
+                    RaisePropertyChanged(HasSystemDecorationsProperty, o, n);
+                }
+            }
+        }
     }
 }

+ 1 - 1
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@@ -96,7 +96,7 @@ namespace Avalonia.DesignerSupport.Remote
         {
         }
 
-        public void SetSystemDecorations(bool enabled)
+        public void SetSystemDecorations(SystemDecorations enabled)
         {
         }
 

+ 1 - 1
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@@ -110,7 +110,7 @@ namespace Avalonia.DesignerSupport.Remote
         {
         }
 
-        public void SetSystemDecorations(bool enabled)
+        public void SetSystemDecorations(SystemDecorations enabled)
         {
         }
 

+ 2 - 2
src/Avalonia.Native/WindowImpl.cs

@@ -68,9 +68,9 @@ namespace Avalonia.Native
             _native.CanResize = value;
         }
 
-        public void SetSystemDecorations(bool enabled)
+        public void SetSystemDecorations(Controls.SystemDecorations enabled)
         {
-            _native.HasDecorations = enabled;
+            _native.HasDecorations = (Interop.SystemDecorations)enabled;
         }
 
         public void SetTitleBarColor (Avalonia.Media.Color color)

+ 11 - 5
src/Avalonia.X11/X11Window.cs

@@ -173,6 +173,7 @@ namespace Avalonia.X11
             
             Surfaces = surfaces.ToArray();
             UpdateMotifHints();
+            UpdateSizeHints(null);
             _xic = XCreateIC(_x11.Xim, XNames.XNInputStyle, XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing,
                 XNames.XNClientWindow, _handle, IntPtr.Zero);
             XFlush(_x11.Display);
@@ -219,12 +220,16 @@ namespace Avalonia.X11
             var decorations = MotifDecorations.Menu | MotifDecorations.Title | MotifDecorations.Border |
                               MotifDecorations.Maximize | MotifDecorations.Minimize | MotifDecorations.ResizeH;
 
-            if (_popup || !_systemDecorations)
+            if (_popup || _systemDecorations == SystemDecorations.None)
             {
                 decorations = 0;
             }
+            else if (_systemDecorations == SystemDecorations.BorderOnly)
+            {
+                decorations = MotifDecorations.Border;
+            }
 
-            if (!_canResize)
+            if (!_canResize || _systemDecorations == SystemDecorations.BorderOnly)
             {
                 functions &= ~(MotifFunctions.Resize | MotifFunctions.Maximize);
                 decorations &= ~(MotifDecorations.Maximize | MotifDecorations.ResizeH);
@@ -247,7 +252,7 @@ namespace Avalonia.X11
             var min = _minMaxSize.minSize;
             var max = _minMaxSize.maxSize;
 
-            if (!_canResize)
+            if (!_canResize || _systemDecorations == SystemDecorations.BorderOnly)
                 max = min = _realSize;
             
             if (preResize.HasValue)
@@ -621,7 +626,7 @@ namespace Avalonia.X11
             return rv;
         }
         
-        private bool _systemDecorations = true;
+        private SystemDecorations _systemDecorations = SystemDecorations.Full;
         private bool _canResize = true;
         private const int MaxWindowDimension = 100000;
 
@@ -777,10 +782,11 @@ namespace Avalonia.X11
             (int)(point.X * Scaling + Position.X),
             (int)(point.Y * Scaling + Position.Y));
         
-        public void SetSystemDecorations(bool enabled)
+        public void SetSystemDecorations(SystemDecorations enabled)
         {
             _systemDecorations = enabled;
             UpdateMotifHints();
+            UpdateSizeHints(null);
         }
 
 

+ 10 - 0
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@@ -1298,7 +1298,17 @@ namespace Avalonia.Win32.Interop
         [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)]
         internal static extern void DoDragDrop(IOleDataObject dataObject, IDropSource dropSource, int allowedEffects, out int finalEffect);
 
+        [DllImport("dwmapi.dll")]
+        public static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);
 
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct MARGINS
+        {
+            public int cxLeftWidth;
+            public int cxRightWidth;
+            public int cyTopHeight;
+            public int cyBottomHeight;
+        }
 
         public enum MONITOR
         {

+ 12 - 8
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -34,7 +34,7 @@ namespace Avalonia.Win32
         private IInputRoot _owner;
         private ManagedDeferredRendererLock _rendererLock = new ManagedDeferredRendererLock();
         private bool _trackingMouse;
-        private bool _decorated = true;
+        private SystemDecorations _decorated = SystemDecorations.Full;
         private bool _resizable = true;
         private bool _topmost = false;
         private bool _taskbarIcon = true;
@@ -97,7 +97,7 @@ namespace Avalonia.Win32
         {
             get
             {
-                if (_decorated)
+                if (_decorated == SystemDecorations.Full)
                 {
                     var style = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE);
                     var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE);
@@ -281,7 +281,7 @@ namespace Avalonia.Win32
             UnmanagedMethods.ShowWindow(_hwnd, UnmanagedMethods.ShowWindowCommand.Hide);
         }
 
-        public void SetSystemDecorations(bool value)
+        public void SetSystemDecorations(SystemDecorations value)
         {
             if (value == _decorated)
             {
@@ -464,7 +464,7 @@ namespace Avalonia.Win32
                     return IntPtr.Zero;
 
                 case WindowsMessage.WM_NCCALCSIZE:
-                    if (ToInt32(wParam) == 1 && !_decorated)
+                    if (ToInt32(wParam) == 1 && _decorated != SystemDecorations.Full)
                     {
                         return IntPtr.Zero;
                     }
@@ -682,14 +682,14 @@ namespace Avalonia.Win32
                     
                     break;
                 case WindowsMessage.WM_NCPAINT:
-                    if (!_decorated)
+                    if (_decorated != SystemDecorations.Full)
                     {
                         return IntPtr.Zero;
                     }
                     break;
 
                 case WindowsMessage.WM_NCACTIVATE:
-                    if (!_decorated)
+                    if (_decorated != SystemDecorations.Full)
                     {
                         return new IntPtr(1);
                     }
@@ -1001,7 +1001,7 @@ namespace Avalonia.Win32
 
             style |= WindowStyles.WS_OVERLAPPEDWINDOW;
 
-            if (!_decorated)
+            if (_decorated != SystemDecorations.Full)
             {
                 style ^= (WindowStyles.WS_CAPTION | WindowStyles.WS_SYSMENU);
             }
@@ -1011,6 +1011,10 @@ namespace Avalonia.Win32
                 style ^= (WindowStyles.WS_SIZEFRAME);
             }
 
+            MARGINS margins = new MARGINS();
+            margins.cyBottomHeight = _decorated == SystemDecorations.BorderOnly ? 1 : 0;
+            UnmanagedMethods.DwmExtendFrameIntoClientArea(_hwnd, ref margins);
+
             GetClientRect(_hwnd, out var oldClientRect);
             var oldClientRectOrigin = new UnmanagedMethods.POINT();
             ClientToScreen(_hwnd, ref oldClientRectOrigin);
@@ -1024,7 +1028,7 @@ namespace Avalonia.Win32
             if (oldDecorated != _decorated)
             {
                 var newRect = oldClientRect;
-                if (_decorated)
+                if (_decorated == SystemDecorations.Full)
                     AdjustWindowRectEx(ref newRect, (uint)style, false,
                         GetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE));
                 SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height,

+ 1 - 1
src/iOS/Avalonia.iOS/EmbeddableImpl.cs

@@ -20,7 +20,7 @@ namespace Avalonia.iOS
             return Disposable.Empty;
         }
 
-        public void SetSystemDecorations(bool enabled)
+        public void SetSystemDecorations(SystemDecorations enabled)
         {
         }