Browse Source

Merge branch 'master' into master

Dariusz Komosiński 5 years ago
parent
commit
d23f9d77e8

+ 6 - 0
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs

@@ -32,6 +32,8 @@ namespace Avalonia.Diagnostics.ViewModels
             view.Filter = FilterProperty;
             PropertiesView = view;
 
+            Layout = new ControlLayoutViewModel(control);
+
             if (control is INotifyPropertyChanged inpc)
             {
                 inpc.PropertyChanged += ControlPropertyChanged;
@@ -52,6 +54,8 @@ namespace Avalonia.Diagnostics.ViewModels
             get => _selectedProperty;
             set => RaiseAndSetIfChanged(ref _selectedProperty, value);
         }
+        
+        public ControlLayoutViewModel Layout { get; }
 
         public void Dispose()
         {
@@ -112,6 +116,8 @@ namespace Avalonia.Diagnostics.ViewModels
                     property.Update();
                 }
             }
+
+            Layout.ControlPropertyChanged(sender, e);
         }
 
         private void ControlPropertyChanged(object sender, PropertyChangedEventArgs e)

+ 190 - 0
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs

@@ -0,0 +1,190 @@
+using System.ComponentModel;
+using Avalonia.Controls;
+using Avalonia.Layout;
+using Avalonia.VisualTree;
+
+namespace Avalonia.Diagnostics.ViewModels
+{
+    internal class ControlLayoutViewModel : ViewModelBase
+    {
+        private readonly IVisual _control;
+        private Thickness _marginThickness;
+        private Thickness _borderThickness;
+        private Thickness _paddingThickness;
+        private double _width;
+        private double _height;
+        private string _widthConstraint;
+        private string _heightConstraint;
+        private bool _updatingFromControl;
+
+        public Thickness MarginThickness
+        {
+            get => _marginThickness;
+            set => RaiseAndSetIfChanged(ref _marginThickness, value);
+        }
+        
+        public Thickness BorderThickness
+        {
+            get => _borderThickness;
+            set => RaiseAndSetIfChanged(ref _borderThickness, value);
+        }
+        
+        public Thickness PaddingThickness
+        {
+            get => _paddingThickness;
+            set => RaiseAndSetIfChanged(ref _paddingThickness, value);
+        }
+
+        public double Width
+        {
+            get => _width;
+            private set => RaiseAndSetIfChanged(ref _width, value);
+        }
+
+        public double Height
+        {
+            get => _height;
+            private set => RaiseAndSetIfChanged(ref _height, value);
+        }
+
+        public string WidthConstraint
+        {
+            get => _widthConstraint;
+            private set => RaiseAndSetIfChanged(ref _widthConstraint, value);
+        }
+
+        public string HeightConstraint
+        {
+            get => _heightConstraint;
+            private set => RaiseAndSetIfChanged(ref _heightConstraint, value);
+        }
+
+        public bool HasPadding { get; }
+        
+        public bool HasBorder { get; }
+        
+        public ControlLayoutViewModel(IVisual control)
+        {
+            _control = control;
+
+            HasPadding = AvaloniaPropertyRegistry.Instance.IsRegistered(control, Decorator.PaddingProperty);
+            HasBorder = AvaloniaPropertyRegistry.Instance.IsRegistered(control, Border.BorderThicknessProperty);
+
+            if (control is AvaloniaObject ao)
+            {
+                MarginThickness = ao.GetValue(Layoutable.MarginProperty);
+
+                if (HasPadding)
+                {
+                    PaddingThickness = ao.GetValue(Decorator.PaddingProperty);
+                }
+
+                if (HasBorder)
+                {
+                    BorderThickness = ao.GetValue(Border.BorderThicknessProperty);
+                }
+            }
+
+            UpdateSize();
+            UpdateSizeConstraints();
+        }
+
+        private void UpdateSizeConstraints()
+        {
+            if (_control is IAvaloniaObject ao)
+            {
+                string CreateConstraintInfo(StyledProperty<double> minProperty, StyledProperty<double> maxProperty)
+                {
+                    if (ao.IsSet(minProperty) || ao.IsSet(maxProperty))
+                    {
+                        var minValue = ao.GetValue(minProperty);
+                        var maxValue = ao.GetValue(maxProperty);
+
+                        return $"{minValue} < size < {maxValue}";
+                    }
+
+                    return null;
+                }
+
+                WidthConstraint = CreateConstraintInfo(Layoutable.MinWidthProperty, Layoutable.MaxWidthProperty);
+                HeightConstraint = CreateConstraintInfo(Layoutable.MinHeightProperty, Layoutable.MaxHeightProperty);
+            }
+        }
+
+        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
+        {
+            base.OnPropertyChanged(e);
+
+            if (_updatingFromControl)
+            {
+                return;
+            }
+
+            if (_control is AvaloniaObject ao)
+            {
+                if (e.PropertyName == nameof(MarginThickness))
+                {
+                    ao.SetValue(Layoutable.MarginProperty, MarginThickness);
+                }
+                else if (HasPadding && e.PropertyName == nameof(PaddingThickness))
+                {
+                    ao.SetValue(Decorator.PaddingProperty, PaddingThickness);
+                }
+                else if (HasBorder && e.PropertyName == nameof(BorderThickness))
+                {
+                    ao.SetValue(Border.BorderThicknessProperty, BorderThickness);
+                }
+            }
+        }
+
+        public void ControlPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
+        {
+            try
+            {
+                _updatingFromControl = true;
+
+                if (e.Property == Visual.BoundsProperty)
+                {
+                    UpdateSize();
+                }
+                else
+                {
+                    if (_control is IAvaloniaObject ao)
+                    {
+                        if (e.Property == Layoutable.MarginProperty)
+                        {
+                            MarginThickness = ao.GetValue(Layoutable.MarginProperty);
+                        } 
+                        else if (e.Property == Decorator.PaddingProperty)
+                        {
+                            PaddingThickness = ao.GetValue(Decorator.PaddingProperty);
+                        } 
+                        else if (e.Property == Border.BorderThicknessProperty)
+                        {
+                            BorderThickness = ao.GetValue(Border.BorderThicknessProperty);
+                        } 
+                        else if (e.Property == Layoutable.MinWidthProperty ||
+                                 e.Property == Layoutable.MaxWidthProperty ||
+                                 e.Property == Layoutable.MinHeightProperty ||
+                                 e.Property == Layoutable.MaxHeightProperty)
+                        {
+                            UpdateSizeConstraints();
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                _updatingFromControl = false;
+            }
+        }
+
+        private void UpdateSize()
+        {
+            var size = _control.Bounds;
+
+            Width = size.Width;
+            Height = size.Height;
+        }
+    }
+}

+ 148 - 31
src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml

@@ -1,38 +1,155 @@
 <UserControl xmlns="https://github.com/avaloniaui"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:conv="clr-namespace:Avalonia.Diagnostics.Converters"
+             xmlns:local="clr-namespace:Avalonia.Diagnostics.Views"
              x:Class="Avalonia.Diagnostics.Views.ControlDetailsView">
-  <Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,*">
-
-    <TextBox Grid.Row="0" Grid.Column="0"
-             BorderThickness="0"
-             Text="{Binding TreePage.PropertyFilter}"
-             Watermark="Filter properties" />
-
-    <CheckBox Grid.Row="0" 
-              Grid.Column="1" 
-              Content="Regex" 
-              IsChecked="{Binding TreePage.UseRegexFilter}"/>
-
-    <DataGrid Items="{Binding PropertiesView}"
-              Grid.Row="1" Grid.ColumnSpan="2"
-              BorderThickness="0"
-              RowBackground="Transparent"
-              SelectedItem="{Binding SelectedProperty, Mode=TwoWay}"
-              CanUserResizeColumns="true">
-      <DataGrid.Columns>
-        <DataGridTextColumn Header="Property" Binding="{Binding Name}" IsReadOnly="True" />
-        <DataGridTextColumn Header="Value" Binding="{Binding Value}" />
-        <DataGridTextColumn Header="Type" Binding="{Binding Type}" />
-        <DataGridTextColumn Header="Priority" Binding="{Binding Priority}" IsReadOnly="True" />
-      </DataGrid.Columns>
-
-      <DataGrid.Styles>
-        <Style Selector="DataGridRow TextBox">
-          <Setter Property="SelectionBrush" Value="LightBlue"/>
-        </Style>
-      </DataGrid.Styles>
-    </DataGrid>
 
+  <UserControl.Resources>
+    <SolidColorBrush x:Key="ThicknessBorderBrush" Color="#666666" />
+    <SolidColorBrush x:Key="SizeGuidelineBrush" Color="#333333" />
+    <SolidColorBrush x:Key="MarginBackgroundBrush" Color="#D78965" />
+    <SolidColorBrush x:Key="BorderBackgroundBrush" Color="#E3C381" />
+    <SolidColorBrush x:Key="PaddingBackgroundBrush" Color="#B8C47F" />
+    <SolidColorBrush x:Key="SizeBackgroundBrush" Color="#88B2BD" />
+  </UserControl.Resources>
+
+  <UserControl.Styles>
+    <Style Selector="local|ThicknessEditor">
+      <Setter Property="HorizontalContentAlignment" Value="Center" />
+      <Setter Property="VerticalContentAlignment" Value="Center" />
+      <Setter Property="BorderThickness" Value="1" />
+      <Setter Property="BorderBrush" Value="{StaticResource ThicknessBorderBrush}" />
+      <Setter Property="Template">
+        <ControlTemplate>
+          <Border
+            Background="{TemplateBinding Background}"
+            BorderBrush="{TemplateBinding BorderBrush}"
+            BorderThickness="{TemplateBinding BorderThickness}">
+            <Grid RowDefinitions="Auto,*,Auto" ColumnDefinitions="Auto,*,Auto">
+              <Grid.Styles>
+                <Style Selector="TextBox.thickness-edit">
+                  <Setter Property="Background" Value="Transparent" />
+                  <Setter Property="BorderThickness" Value="0" />
+                  <Setter Property="Margin" Value="2" />
+                  <Setter Property="HorizontalAlignment" Value="Stretch" />
+                  <Setter Property="VerticalAlignment" Value="Stretch" />
+                  <Setter Property="HorizontalContentAlignment" Value="Center" />
+                  <Setter Property="VerticalContentAlignment" Value="Center" />
+                  <Setter Property="(ScrollViewer.HorizontalScrollBarVisibility)" Value="Disabled" />
+                  <Setter Property="(ScrollViewer.VerticalScrollBarVisibility)" Value="Disabled" />
+                  <Setter Property="IsVisible" Value="{Binding $parent[local:ThicknessEditor].IsPresent}" />
+                </Style>
+              </Grid.Styles>
+              <TextBlock IsVisible="{TemplateBinding IsPresent}" Margin="4,0,0,0" Text="{TemplateBinding Header}" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" />
+              <TextBox Grid.Row="1" Grid.Column="0" Text="{Binding Left, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
+              <TextBox x:Name="Right"  Grid.Row="0" Grid.Column="1" Text="{Binding Top, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
+              <TextBox Grid.Row="1" Grid.Column="2" Text="{Binding Right, RelativeSource={RelativeSource TemplatedParent}}"  Classes="thickness-edit" />
+              <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Bottom, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
+              <ContentPresenter Grid.Row="1" Grid.Column="1"
+                Name="PART_ContentPresenter"
+                                ContentTemplate="{TemplateBinding ContentTemplate}"
+                                Content="{TemplateBinding Content}"
+                                Padding="{TemplateBinding Padding}"
+                                VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
+                                HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
+            </Grid>
+          </Border>
+
+        </ControlTemplate>
+      </Setter>
+    </Style>
+    <Style Selector="local|ThicknessEditor[IsPresent=False]">
+      <Setter Property="BorderThickness" Value="0" />
+    </Style>
+  </UserControl.Styles>
+
+  <Grid ColumnDefinitions="*,Auto,280">
+
+    <Grid Grid.Column="0" ColumnDefinitions="*,Auto,Auto,Auto" RowDefinitions="Auto,*">
+
+      <TextBox Grid.Row="0" 
+               Grid.Column="0"
+               BorderThickness="0"
+               Text="{Binding TreePage.PropertyFilter}"
+               Watermark="Filter properties" />
+
+      <CheckBox Grid.Row="0"
+                Grid.Column="1"
+                Margin="0,0,4,0"
+                Content="Regex"
+                IsChecked="{Binding TreePage.UseRegexFilter}" />
+
+      <DataGrid Items="{Binding PropertiesView}"
+                Grid.Row="1"
+                Grid.Column="0"
+                Grid.ColumnSpan="2"
+                BorderThickness="0"
+                RowBackground="Transparent"
+                SelectedItem="{Binding SelectedProperty, Mode=TwoWay}"
+                CanUserResizeColumns="true">
+        <DataGrid.Columns>
+          <DataGridTextColumn Header="Property" Binding="{Binding Name}" IsReadOnly="True" />
+          <DataGridTextColumn Header="Value" Binding="{Binding Value}" />
+          <DataGridTextColumn Header="Type" Binding="{Binding Type}" />
+          <DataGridTextColumn Header="Priority" Binding="{Binding Priority}" IsReadOnly="True" />
+        </DataGrid.Columns>
+
+        <DataGrid.Styles>
+          <Style Selector="DataGridRow TextBox">
+            <Setter Property="SelectionBrush" Value="LightBlue" />
+          </Style>
+        </DataGrid.Styles>
+      </DataGrid>
+
+    </Grid>
+
+    <GridSplitter Grid.Column="1" />
+
+    <Grid Grid.Column="2" RowDefinitions="Auto,*" >
+      <TextBlock Grid.Row="0" Text="Layout Visualizer" Margin="4" />
+      
+      <Grid Grid.Row="1" x:Name="LayoutRoot" Margin="8,0,8,8" RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,Auto">
+
+        <Border x:Name="VerticalSize" Grid.Row="0" Grid.Column="1" >
+          <TextBlock VerticalAlignment="Center" FontWeight="Bold"
+                     TextDecorations="{Binding Layout.HeightConstraint, Converter={x:Static local:Converters.HasConstraintConverter}}"
+                     Text="{Binding Layout.Height}"
+                     ToolTip.Tip="{Binding Layout.HeightConstraint}" />
+        </Border>
+        
+        <Border x:Name="HorizontalSize" Grid.Row="1" Grid.Column="0" >
+          <TextBlock HorizontalAlignment="Center" FontWeight="Bold" 
+                     TextDecorations="{Binding Layout.WidthConstraint, Converter={x:Static local:Converters.HasConstraintConverter}}"
+                     Text="{Binding Layout.Width}" 
+                     ToolTip.Tip="{Binding Layout.WidthConstraint}" />
+        </Border>
+        
+        <local:ThicknessEditor Grid.Row="0" Grid.Column="0" Header="margin" VerticalAlignment="Top" HorizontalAlignment="Center" Background="{StaticResource MarginBackgroundBrush}" Thickness="{Binding Layout.MarginThickness}">
+          <local:ThicknessEditor x:Name="BorderArea" Header="border" Background="{StaticResource BorderBackgroundBrush}" Thickness="{Binding Layout.BorderThickness}" IsPresent="{Binding Layout.HasBorder}">
+            <local:ThicknessEditor x:Name="PaddingArea" Header="padding" Background="{StaticResource PaddingBackgroundBrush}" Thickness="{Binding Layout.PaddingThickness}" IsPresent="{Binding Layout.HasPadding}">
+              <Border x:Name="ContentArea" BorderThickness="1" BorderBrush="{StaticResource ThicknessBorderBrush}" MinWidth="100" MinHeight="16" Background="{StaticResource SizeBackgroundBrush}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
+                <TextBlock Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" Text="content" />
+              </Border>
+            </local:ThicknessEditor>
+          </local:ThicknessEditor>
+        </local:ThicknessEditor>
+
+        <Canvas Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2">
+          <Canvas.Styles>
+            <Style Selector="Rectangle">
+              <Setter Property="StrokeDashArray" Value="1,3" />
+              <Setter Property="Stroke" Value="{StaticResource SizeGuidelineBrush}" />
+              <Setter Property="StrokeThickness" Value="1" />
+            </Style>
+          </Canvas.Styles>
+          <Rectangle x:Name="HorizontalSizeBegin" />
+          <Rectangle x:Name="HorizontalSizeEnd" />
+          <Rectangle x:Name="VerticalSizeBegin" />
+          <Rectangle x:Name="VerticalSizeEnd" />
+        </Canvas>
+      </Grid>
+      
+    </Grid>
   </Grid>
+
 </UserControl>

+ 109 - 0
src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml.cs

@@ -1,10 +1,24 @@
+using System;
 using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
 using Avalonia.Markup.Xaml;
+using Avalonia.VisualTree;
 
 namespace Avalonia.Diagnostics.Views
 {
     internal class ControlDetailsView : UserControl
     {
+        private ThicknessEditor _borderArea;
+        private ThicknessEditor _paddingArea;
+        private Rectangle _horizontalSizeBegin;
+        private Rectangle _horizontalSizeEnd;
+        private Rectangle _verticalSizeBegin;
+        private Rectangle _verticalSizeEnd;
+        private Grid _layoutRoot;
+        private Border _horizontalSize;
+        private Border _verticalSize;
+        private Border _contentArea;
+
         public ControlDetailsView()
         {
             InitializeComponent();
@@ -13,6 +27,101 @@ namespace Avalonia.Diagnostics.Views
         private void InitializeComponent()
         {
             AvaloniaXamlLoader.Load(this);
+
+            _borderArea = this.FindControl<ThicknessEditor>("BorderArea");
+            _paddingArea = this.FindControl<ThicknessEditor>("PaddingArea");
+
+            _horizontalSizeBegin = this.FindControl<Rectangle>("HorizontalSizeBegin");
+            _horizontalSizeEnd = this.FindControl<Rectangle>("HorizontalSizeEnd");
+            _verticalSizeBegin = this.FindControl<Rectangle>("VerticalSizeBegin");
+            _verticalSizeEnd = this.FindControl<Rectangle>("VerticalSizeEnd");
+
+            _horizontalSize = this.FindControl<Border>("HorizontalSize");
+            _verticalSize = this.FindControl<Border>("VerticalSize");
+
+            _contentArea = this.FindControl<Border>("ContentArea");
+
+            _layoutRoot = this.FindControl<Grid>("LayoutRoot");
+
+            void SubscribeToBounds(Visual visual)
+            {
+                visual.GetPropertyChangedObservable(TransformedBoundsProperty)
+                    .Subscribe(UpdateSizeGuidelines);
+            }
+
+            SubscribeToBounds(_borderArea);
+            SubscribeToBounds(_paddingArea);
+            SubscribeToBounds(_contentArea);
+        }
+
+        private void UpdateSizeGuidelines(AvaloniaPropertyChangedEventArgs e)
+        {
+            void UpdateGuidelines(Visual area)
+            {
+                if (area.TransformedBounds is TransformedBounds bounds)
+                {
+                    // Horizontal guideline
+                    {
+                        var sizeArea = TranslateToRoot((_horizontalSize.TransformedBounds ?? default).Bounds.BottomLeft,
+                            _horizontalSize);
+
+                        var start = TranslateToRoot(bounds.Bounds.BottomLeft, area);
+
+                        SetPosition(_horizontalSizeBegin, start);
+
+                        var end = TranslateToRoot(bounds.Bounds.BottomRight, area);
+
+                        SetPosition(_horizontalSizeEnd, end.WithX(end.X - 1));
+
+                        var height = sizeArea.Y - start.Y + 2;
+
+                        _horizontalSizeBegin.Height = height;
+                        _horizontalSizeEnd.Height = height;
+                    }
+
+                    // Vertical guideline
+                    {
+                        var sizeArea = TranslateToRoot((_verticalSize.TransformedBounds ?? default).Bounds.TopRight, _verticalSize);
+
+                        var start = TranslateToRoot(bounds.Bounds.TopRight, area);
+
+                        SetPosition(_verticalSizeBegin, start);
+
+                        var end = TranslateToRoot(bounds.Bounds.BottomRight, area);
+
+                        SetPosition(_verticalSizeEnd, end.WithY(end.Y - 1));
+
+                        var width = sizeArea.X - start.X + 2;
+
+                        _verticalSizeBegin.Width = width;
+                        _verticalSizeEnd.Width = width;
+                    }
+                }
+            }
+
+            Point TranslateToRoot(Point point, IVisual from)
+            {
+                return from.TranslatePoint(point, _layoutRoot) ?? default;
+            }
+
+            static void SetPosition(Rectangle rect, Point start)
+            {
+                Canvas.SetLeft(rect, start.X);
+                Canvas.SetTop(rect, start.Y);
+            }
+
+            if (_borderArea.IsPresent)
+            {
+                UpdateGuidelines(_borderArea);
+            } 
+            else if (_paddingArea.IsPresent)
+            {
+                UpdateGuidelines(_paddingArea);
+            }
+            else
+            {
+                UpdateGuidelines(_contentArea);
+            }
         }
     }
 }

+ 130 - 0
src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs

@@ -0,0 +1,130 @@
+using Avalonia.Controls;
+using Avalonia.Data;
+using Avalonia.Data.Converters;
+using Avalonia.Media;
+
+namespace Avalonia.Diagnostics.Views
+{
+    internal static class Converters
+    {
+        public static IValueConverter HasConstraintConverter =
+            new FuncValueConverter<object, TextDecorationCollection>(ConvertToDecoration);
+
+        private static TextDecorationCollection ConvertToDecoration(object arg)
+        {
+            return arg != null ? TextDecorations.Underline : null;
+        }
+    }
+
+    internal class ThicknessEditor : ContentControl
+    {
+        public static readonly DirectProperty<ThicknessEditor, Thickness> ThicknessProperty =
+            AvaloniaProperty.RegisterDirect<ThicknessEditor, Thickness>(nameof(Thickness), o => o.Thickness,
+                (o, v) => o.Thickness = v, defaultBindingMode: BindingMode.TwoWay);
+
+        public static readonly DirectProperty<ThicknessEditor, string> HeaderProperty =
+            AvaloniaProperty.RegisterDirect<ThicknessEditor, string>(nameof(Header), o => o.Header,
+                (o, v) => o.Header = v);
+
+        public static readonly DirectProperty<ThicknessEditor, bool> IsPresentProperty =
+            AvaloniaProperty.RegisterDirect<ThicknessEditor, bool>(nameof(Header), o => o.IsPresent,
+                (o, v) => o.IsPresent = v);
+
+        public static readonly DirectProperty<ThicknessEditor, double> LeftProperty =
+            AvaloniaProperty.RegisterDirect<ThicknessEditor, double>(nameof(Left), o => o.Left, (o, v) => o.Left = v);
+
+        public static readonly DirectProperty<ThicknessEditor, double> TopProperty =
+            AvaloniaProperty.RegisterDirect<ThicknessEditor, double>(nameof(Top), o => o.Top, (o, v) => o.Top = v);
+
+        public static readonly DirectProperty<ThicknessEditor, double> RightProperty =
+            AvaloniaProperty.RegisterDirect<ThicknessEditor, double>(nameof(Right), o => o.Right,
+                (o, v) => o.Right = v);
+
+        public static readonly DirectProperty<ThicknessEditor, double> BottomProperty =
+            AvaloniaProperty.RegisterDirect<ThicknessEditor, double>(nameof(Bottom), o => o.Bottom,
+                (o, v) => o.Bottom = v);
+
+        
+        private Thickness _thickness;
+        private string _header;
+        private bool _isPresent = true;
+        private double _left;
+        private double _top;
+        private double _right;
+        private double _bottom;
+
+        private bool _isUpdatingThickness;
+
+        public Thickness Thickness
+        {
+            get => _thickness;
+            set => SetAndRaise(ThicknessProperty, ref _thickness, value);
+        }
+
+        public string Header
+        {
+            get => _header;
+            set => SetAndRaise(HeaderProperty, ref _header, value);
+        }
+
+        public bool IsPresent
+        {
+            get => _isPresent;
+            set => SetAndRaise(IsPresentProperty, ref _isPresent, value);
+        }
+
+        public double Left
+        {
+            get => _left;
+            set => SetAndRaise(LeftProperty, ref _left, value);
+        }
+
+        public double Top
+        {
+            get => _top;
+            set => SetAndRaise(TopProperty, ref _top, value);
+        }
+
+        public double Right
+        {
+            get => _right;
+            set => SetAndRaise(RightProperty, ref _right, value);
+        }
+
+        public double Bottom
+        {
+            get => _bottom;
+            set => SetAndRaise(BottomProperty, ref _bottom, value);
+        }
+
+        protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
+        {
+            base.OnPropertyChanged(change);
+
+            if (change.Property == ThicknessProperty)
+            {
+                try
+                {
+                    _isUpdatingThickness = true;
+
+                    var value = change.NewValue.GetValueOrDefault<Thickness>();
+
+                    Left = value.Left;
+                    Top = value.Top;
+                    Right = value.Right;
+                    Bottom = value.Bottom;
+                }
+                finally
+                {
+                    _isUpdatingThickness = false;
+                }
+            }
+            else if (!_isUpdatingThickness &&
+                     (change.Property == LeftProperty || change.Property == TopProperty ||
+                      change.Property == RightProperty || change.Property == BottomProperty))
+            {
+                Thickness = new Thickness(Left, Top, Right, Bottom);
+            }
+        }
+    }
+}

+ 1 - 1
src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml

@@ -2,7 +2,7 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:vm="clr-namespace:Avalonia.Diagnostics.ViewModels"
              x:Class="Avalonia.Diagnostics.Views.TreePageView">
-  <Grid ColumnDefinitions="0.8*,4,*">    
+  <Grid ColumnDefinitions="0.4*,4,0.6*">    
     <TreeView Name="tree"
               BorderThickness="0"
               Items="{Binding Nodes}"

+ 3 - 2
src/Avalonia.Native/WindowImplBase.cs

@@ -155,10 +155,10 @@ namespace Avalonia.Native
                 }
                 finally
                 {
+                    
+                    _parent?.Dispose();
                     n?.Dispose();
                 }
-                
-                _parent._mouse.Dispose();
             }
 
             void IAvnWindowBaseEvents.Activated() => _parent.Activated?.Invoke();
@@ -336,6 +336,7 @@ namespace Avalonia.Native
             _nativeControlHost = null;
             
             (Screen as ScreenImpl)?.Dispose();
+            _mouse.Dispose();
         }
 
 

+ 11 - 1
src/Avalonia.Visuals/Media/FontFamily.cs

@@ -133,6 +133,16 @@ namespace Avalonia.Media
             }
         }
 
+        /// <summary>
+        /// Parses a <see cref="T:Avalonia.Media.FontFamily"/> string.
+        /// </summary>
+        /// <param name="s">The <see cref="T:Avalonia.Media.FontFamily"/> string.</param>
+        /// <returns></returns>
+        /// <exception cref="ArgumentException">
+        /// Specified family is not supported.
+        /// </exception>
+        public static FontFamily Parse(string s) => Parse(s, null);
+
         /// <summary>
         /// Parses a <see cref="T:Avalonia.Media.FontFamily"/> string.
         /// </summary>
@@ -142,7 +152,7 @@ namespace Avalonia.Media
         /// <exception cref="ArgumentException">
         /// Specified family is not supported.
         /// </exception>
-        public static FontFamily Parse(string s, Uri baseUri = null)
+        public static FontFamily Parse(string s, Uri baseUri)
         {
             if (string.IsNullOrEmpty(s))
             {