Browse Source

Merge pull request #5950 from MarchingCube/devtools-layout-alignment

Allow for easily changing layout alignment
Dan Walmsley 4 years ago
parent
commit
21b4423534

+ 87 - 0
src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.axaml

@@ -0,0 +1,87 @@
+<Styles xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:controls="clr-namespace:Avalonia.Diagnostics.Controls">
+
+    <Styles.Resources>
+        <SolidColorBrush x:Key="HighlightBorderBrush" Color="CornflowerBlue" />
+        <SolidColorBrush x:Key="ThicknessBorderBrush" Color="#666666" />
+    </Styles.Resources>
+
+    <Style Selector="controls|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>
+                <Panel>
+                    <Rectangle x:Name="PART_Background"
+                               Classes.no-content-pointerover="{Binding !#PART_ContentPresenter.IsPointerOver}" />
+                    <Border
+                        x:Name="PART_Border"
+                        Classes.no-content-pointerover="{Binding !#PART_ContentPresenter.IsPointerOver}"
+                        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[controls: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>
+                </Panel>
+
+            </ControlTemplate>
+        </Setter>
+    </Style>
+
+    <Style Selector="controls|ThicknessEditor[IsPresent=False]">
+        <Setter Property="BorderThickness" Value="0" />
+    </Style>
+
+    <Style Selector="controls|ThicknessEditor /template/ Rectangle#PART_Background">
+        <Setter Property="Fill" Value="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}" />
+    </Style>
+
+    <Style Selector="controls|ThicknessEditor:pointerover /template/ Rectangle#PART_Background.no-content-pointerover">
+        <Setter Property="Fill" Value="{Binding Highlight, RelativeSource={RelativeSource TemplatedParent}}" />
+    </Style>
+
+    <Style Selector="controls|ThicknessEditor:pointerover /template/ Border#PART_Border.no-content-pointerover">
+        <Setter Property="BorderBrush" Value="{StaticResource HighlightBorderBrush}" />
+    </Style>
+</Styles>

+ 7 - 7
src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs → src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.cs

@@ -2,7 +2,7 @@ using Avalonia.Controls;
 using Avalonia.Data;
 using Avalonia.Media;
 
-namespace Avalonia.Diagnostics.Views
+namespace Avalonia.Diagnostics.Controls
 {
     internal class ThicknessEditor : ContentControl
     {
@@ -35,12 +35,6 @@ namespace Avalonia.Diagnostics.Views
         public static readonly StyledProperty<IBrush> HighlightProperty =
             AvaloniaProperty.Register<ThicknessEditor, IBrush>(nameof(Highlight));
 
-        public IBrush Highlight
-        {
-            get => GetValue(HighlightProperty);
-            set => SetValue(HighlightProperty, value);
-        }
-        
         private Thickness _thickness;
         private string _header;
         private bool _isPresent = true;
@@ -92,6 +86,12 @@ namespace Avalonia.Diagnostics.Views
             set => SetAndRaise(BottomProperty, ref _bottom, value);
         }
 
+        public IBrush Highlight
+        {
+            get => GetValue(HighlightProperty);
+            set => SetValue(HighlightProperty, value);
+        }
+
         protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
         {
             base.OnPropertyChanged(change);

+ 25 - 0
src/Avalonia.Diagnostics/Diagnostics/Converters/EnumToCheckedConverter.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Globalization;
+using Avalonia.Data;
+using Avalonia.Data.Converters;
+
+namespace Avalonia.Diagnostics.Converters
+{
+    internal class EnumToCheckedConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return Equals(value, parameter);
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value is bool isChecked && isChecked)
+            {
+                return parameter;
+            }
+
+            return BindingOperations.DoNothing;
+        }
+    }
+}

+ 2 - 2
src/Avalonia.Diagnostics/Diagnostics/DevToolsOptions.cs

@@ -19,8 +19,8 @@ namespace Avalonia.Diagnostics
         public bool ShowAsChildWindow { get; set; } = true;
 
         /// <summary>
-        /// Gets or sets the initial size of the DevTools window. The default value is 1024x512.
+        /// Gets or sets the initial size of the DevTools window. The default value is 1280x720.
         /// </summary>
-        public Size Size { get; set; } = new Size(1024, 512);
+        public Size Size { get; set; } = new Size(1280, 720);
     }
 }

+ 67 - 34
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs

@@ -10,27 +10,58 @@ 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 HorizontalAlignment _horizontalAlignment;
+        private Thickness _marginThickness;
+        private Thickness _paddingThickness;
         private bool _updatingFromControl;
+        private VerticalAlignment _verticalAlignment;
+        private double _width;
+        private string _widthConstraint;
+
+        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);
+                }
+
+                HorizontalAlignment = ao.GetValue(Layoutable.HorizontalAlignmentProperty);
+                VerticalAlignment = ao.GetValue(Layoutable.VerticalAlignmentProperty);
+            }
+
+            UpdateSize();
+            UpdateSizeConstraints();
+        }
 
         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;
@@ -61,35 +92,21 @@ namespace Avalonia.Diagnostics.ViewModels
             private set => RaiseAndSetIfChanged(ref _heightConstraint, value);
         }
 
-        public bool HasPadding { get; }
-        
-        public bool HasBorder { get; }
-        
-        public ControlLayoutViewModel(IVisual control)
+        public HorizontalAlignment HorizontalAlignment
         {
-            _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);
+            get => _horizontalAlignment;
+            private set => RaiseAndSetIfChanged(ref _horizontalAlignment, value);
+        }
 
-                if (HasPadding)
-                {
-                    PaddingThickness = ao.GetValue(Decorator.PaddingProperty);
-                }
+        public VerticalAlignment VerticalAlignment
+        {
+            get => _verticalAlignment;
+            private set => RaiseAndSetIfChanged(ref _verticalAlignment, value);
+        }
 
-                if (HasBorder)
-                {
-                    BorderThickness = ao.GetValue(Border.BorderThicknessProperty);
-                }
-            }
+        public bool HasPadding { get; }
 
-            UpdateSize();
-            UpdateSizeConstraints();
-        }
+        public bool HasBorder { get; }
 
         private void UpdateSizeConstraints()
         {
@@ -151,6 +168,14 @@ namespace Avalonia.Diagnostics.ViewModels
                 {
                     ao.SetValue(Border.BorderThicknessProperty, BorderThickness);
                 }
+                else if (e.PropertyName == nameof(HorizontalAlignment))
+                {
+                    ao.SetValue(Layoutable.HorizontalAlignmentProperty, HorizontalAlignment);
+                }
+                else if (e.PropertyName == nameof(VerticalAlignment))
+                {
+                    ao.SetValue(Layoutable.VerticalAlignmentProperty, VerticalAlignment);
+                }
             }
         }
 
@@ -171,15 +196,15 @@ namespace Avalonia.Diagnostics.ViewModels
                         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 ||
@@ -187,6 +212,14 @@ namespace Avalonia.Diagnostics.ViewModels
                         {
                             UpdateSizeConstraints();
                         }
+                        else if (e.Property == Layoutable.HorizontalAlignmentProperty)
+                        {
+                            HorizontalAlignment = ao.GetValue(Layoutable.HorizontalAlignmentProperty);
+                        }
+                        else if (e.Property == Layoutable.VerticalAlignmentProperty)
+                        {
+                            VerticalAlignment = ao.GetValue(Layoutable.VerticalAlignmentProperty);
+                        }
                     }
                 }
             }

+ 112 - 241
src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml

@@ -7,88 +7,10 @@
              x:Name="Main">
 
   <UserControl.Resources>
-    <SolidColorBrush x:Key="ThicknessBorderBrush" Color="#666666" />
-    <SolidColorBrush x:Key="HighlightBorderBrush" Color="CornflowerBlue" />
-    <SolidColorBrush x:Key="SizeGuidelineBrush" Color="#333333" />
-    <SolidColorBrush x:Key="MarginBackgroundBrush" Color="#D78965" />
-    <SolidColorBrush x:Key="MarginHighlightBrush" Color="#EA966F" />
-    <SolidColorBrush x:Key="BorderBackgroundBrush" Color="#E3C381" />
-    <SolidColorBrush x:Key="BorderHighlightBrush" Color="#EFCD88" />
-    <SolidColorBrush x:Key="PaddingBackgroundBrush" Color="#B8C47F" />
-    <SolidColorBrush x:Key="PaddingHighlightBrush" Color="#CEDA8E" />
-    <SolidColorBrush x:Key="SizeBackgroundBrush" Color="#88B2BD" />
-    <SolidColorBrush x:Key="SizeHighlightBrush" Color="#9ED0DC" />
     <conv:BoolToOpacityConverter x:Key="BoolToOpacity" Opacity="0.6"/>
   </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>
-          <Panel>
-            <Rectangle x:Name="PART_Background" Classes.no-content-pointerover="{Binding !#PART_ContentPresenter.IsPointerOver}" />
-            <Border
-              x:Name="PART_Border"
-              Classes.no-content-pointerover="{Binding !#PART_ContentPresenter.IsPointerOver}"
-              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>
-          </Panel>
-
-        </ControlTemplate>
-      </Setter>
-    </Style>
-
-    <Style Selector="local|ThicknessEditor /template/ Rectangle#PART_Background">
-      <Setter Property="Fill" Value="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}" />
-    </Style>
-
-    <Style Selector="local|ThicknessEditor:pointerover /template/ Rectangle#PART_Background.no-content-pointerover">
-      <Setter Property="Fill" Value="{Binding Highlight, RelativeSource={RelativeSource TemplatedParent}}" />
-    </Style>
-
-    <Style Selector="local|ThicknessEditor:pointerover /template/ Border#PART_Border.no-content-pointerover">
-      <Setter Property="BorderBrush" Value="{StaticResource HighlightBorderBrush}" />
-    </Style>
-
-    <Style Selector="local|ThicknessEditor[IsPresent=False]">
-      <Setter Property="BorderThickness" Value="0" />
-    </Style>
-  </UserControl.Styles>
-
-  <Grid ColumnDefinitions="*,Auto,280">
+  <Grid ColumnDefinitions="*,Auto,320">
 
     <Grid Grid.Column="0" ColumnDefinitions="*,Auto,Auto,Auto" RowDefinitions="Auto,*">
 
@@ -130,184 +52,133 @@
 
     <GridSplitter Grid.Column="1" />
 
-    <Grid Grid.Column="2" RowDefinitions="Auto,*, Auto,*,Auto" >
-      <TextBlock Grid.Row="0" Text="Layout Visualizer" Margin="4" />
+    <Grid Grid.Column="2" RowDefinitions="*,Auto,*" >
 
-      <Viewbox Grid.Row="1" StretchDirection="DownOnly" >
-        <Grid Grid.Row="1" x:Name="LayoutRoot" Margin="8,0,8,8" RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,Auto">
-
-          <Grid.Styles>
-            <Style Selector="TextBlock.with-constraint">
-              <Setter Property="TextDecorations" Value="Underline" />
-            </Style>
+      <Grid RowDefinitions="Auto,*" Grid.Row="0">
+        <TextBlock FontWeight="Bold" Grid.Row="0" Text="Layout Visualizer" Margin="4" />
+        <local:LayoutExplorerView Grid.Row="1" DataContext="{Binding Layout}" />
+      </Grid>
 
-          </Grid.Styles>
+      <GridSplitter Grid.Row="1" />
 
-          <Border x:Name="VerticalSize" Grid.Row="0" Grid.Column="1" >
-            <TextBlock VerticalAlignment="Center" FontWeight="Bold"
-                       Classes.with-constraint="{Binding Layout.HeightConstraint, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
-                       Text="{Binding Layout.Height}"
-                       ToolTip.Tip="{Binding Layout.HeightConstraint}" />
-          </Border>
+      <Grid RowDefinitions="Auto,*,Auto" Grid.Row="2">
 
-          <Border x:Name="HorizontalSize" Grid.Row="1" Grid.Column="0" >
-            <TextBlock HorizontalAlignment="Center" FontWeight="Bold"
-                       Classes.with-constraint="{Binding Layout.WidthConstraint, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
-                       Text="{Binding Layout.Width}"
-                       ToolTip.Tip="{Binding Layout.WidthConstraint}" />
-          </Border>
+        <Grid Grid.Row="0" Margin="4" RowDefinitions="Auto,Auto">
 
-          <local:ThicknessEditor Grid.Row="0" Grid.Column="0" Header="margin" VerticalAlignment="Top" HorizontalAlignment="Center" Background="{StaticResource MarginBackgroundBrush}" Highlight="{StaticResource MarginHighlightBrush}" Thickness="{Binding Layout.MarginThickness}">
-            <local:ThicknessEditor x:Name="BorderArea" Header="border" Background="{StaticResource BorderBackgroundBrush}" Highlight="{StaticResource BorderHighlightBrush}" Thickness="{Binding Layout.BorderThickness}" IsPresent="{Binding Layout.HasBorder}">
-              <local:ThicknessEditor x:Name="PaddingArea" Header="padding" Background="{StaticResource PaddingBackgroundBrush}" Highlight="{StaticResource PaddingHighlightBrush}" Thickness="{Binding Layout.PaddingThickness}" IsPresent="{Binding Layout.HasPadding}">
-                <Border x:Name="ContentArea" BorderThickness="1" MinWidth="100" MinHeight="16" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
-                  <Border.Styles>
-                    <Style Selector="Border">
-                      <Setter Property="Background" Value="{StaticResource SizeBackgroundBrush}" />
-                      <Setter Property="BorderBrush" Value="{StaticResource ThicknessBorderBrush}" />
-                    </Style>
-                    <Style Selector="Border:pointerover">
-                      <Setter Property="Background" Value="{StaticResource SizeHighlightBrush}" />
-                      <Setter Property="BorderBrush" Value="{StaticResource HighlightBorderBrush}" />
-                    </Style>
-                  </Border.Styles>
-                  <TextBlock Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" Text="content" />
-                </Border>
-              </local:ThicknessEditor>
-            </local:ThicknessEditor>
-          </local:ThicknessEditor>
+          <Grid Grid.Row="0" Margin="2" ColumnDefinitions="Auto,*,Auto,Auto">
+            <TextBlock FontWeight="Bold" Grid.Column="0" Text="{Binding StyleStatus}" VerticalAlignment="Center" />
+            <CheckBox Margin="2,0,0,0" Grid.Column="2" Content="Show inactive" IsChecked="{Binding ShowInactiveStyles}" ToolTip.Tip="Show styles that are currently inactive" />
+            <ToggleButton Margin="2,0,0,0" Grid.Column="3" ToolTip.Tip="Snapshot current styles (Alt+S/Alt+D to enable/disable within debugged window)" Content="Snapshot" IsChecked="{Binding SnapshotStyles}" />
+          </Grid>
 
-          <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" />
-                <Setter Property="IsHitTestVisible" Value="False" />
-              </Style>
-            </Canvas.Styles>
-            <Rectangle x:Name="HorizontalSizeBegin" />
-            <Rectangle x:Name="HorizontalSizeEnd" />
-            <Rectangle x:Name="VerticalSizeBegin" />
-            <Rectangle x:Name="VerticalSizeEnd" />
-          </Canvas>
+          <TextBox Grid.Row="1" Margin="2" Grid.Column="0" Watermark="Filter" Text="{Binding StyleFilter}" />
         </Grid>
-      </Viewbox>
 
-      <Grid Grid.Row="2" Margin="4" RowDefinitions="Auto,Auto">
-
-        <Grid Grid.Row="0" Margin="2" ColumnDefinitions="Auto,*,Auto,Auto">
-          <TextBlock FontWeight="Bold" Grid.Column="0" Text="{Binding StyleStatus}" VerticalAlignment="Center" />
-          <CheckBox Margin="2,0,0,0" Grid.Column="2" Content="Show inactive" IsChecked="{Binding ShowInactiveStyles}" ToolTip.Tip="Show styles that are currently inactive" />
-          <ToggleButton Margin="2,0,0,0" Grid.Column="3" ToolTip.Tip="Snapshot current styles (Alt+S/Alt+D to enable/disable within debugged window)" Content="Snapshot" IsChecked="{Binding SnapshotStyles}" />
-        </Grid>
-
-        <TextBox Grid.Row="1" Margin="2" Grid.Column="0" Watermark="Filter" Text="{Binding StyleFilter}" />
-      </Grid>
-
-        <ScrollViewer Grid.Row="3" HorizontalScrollBarVisibility="Disabled">
-        <ItemsControl Items="{Binding AppliedStyles}" >
-          <ItemsControl.ItemTemplate>
-            <DataTemplate>
-              <Border BorderThickness="0,0,0,1" BorderBrush="#6C6C6C" Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}">
-                <Border.IsVisible>
-                  <MultiBinding Converter="{x:Static BoolConverters.And}">
-                    <MultiBinding Converter="{x:Static BoolConverters.Or}" >
-                      <Binding Path="IsActive" />
-                      <Binding Path="#Main.DataContext.ShowInactiveStyles" />
+        <ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled">
+          <ItemsControl Items="{Binding AppliedStyles}" >
+            <ItemsControl.ItemTemplate>
+              <DataTemplate>
+                <Border BorderThickness="0,0,0,1" BorderBrush="#6C6C6C" Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}">
+                  <Border.IsVisible>
+                    <MultiBinding Converter="{x:Static BoolConverters.And}">
+                      <MultiBinding Converter="{x:Static BoolConverters.Or}" >
+                        <Binding Path="IsActive" />
+                        <Binding Path="#Main.DataContext.ShowInactiveStyles" />
+                      </MultiBinding>
+                      <Binding Path="IsVisible" />
                     </MultiBinding>
-                    <Binding Path="IsVisible" />
-                  </MultiBinding>
-                </Border.IsVisible>
-                <Expander IsExpanded="True" Margin="0" Padding="8,0" ContentTransition="{x:Null}" >
-                  <Expander.Header>
-                    <TextBlock Grid.Row="0" Text="{Binding Name}" />
-                  </Expander.Header>
-
-                  <ItemsControl Margin="20,0,0,0" Grid.Row="1" Items="{Binding Setters}">
-                    <ItemsControl.DataTemplates>
-
-                      <DataTemplate DataType="IBrush">
-                        <StackPanel Orientation="Horizontal" Spacing="2">
-                          <Border BorderThickness="1" BorderBrush="Black" Background="{Binding}" Width="8" Height="8"/>
-                          <TextBlock Text="{Binding}" />
-                        </StackPanel>
-                      </DataTemplate>
+                  </Border.IsVisible>
+                  <Expander IsExpanded="True" Margin="0" Padding="8,0" ContentTransition="{x:Null}" >
+                    <Expander.Header>
+                      <TextBlock Grid.Row="0" Text="{Binding Name}" />
+                    </Expander.Header>
 
-                      <DataTemplate DataType="Color">
-                        <StackPanel Orientation="Horizontal" Spacing="2">
-                          <Border BorderThickness="1" BorderBrush="Black" Width="8" Height="8">
-                            <Border.Background>
-                              <SolidColorBrush Color="{Binding}" />
-                            </Border.Background>
-                          </Border>
-                          <TextBlock Text="{Binding}" />
-                        </StackPanel>
-                      </DataTemplate>
+                    <ItemsControl Margin="20,0,0,0" Grid.Row="1" Items="{Binding Setters}">
+                      <ItemsControl.DataTemplates>
 
-                      <DataTemplate DataType="vm:ResourceSetterViewModel">
-                        <Panel Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}" IsVisible="{Binding IsVisible}" HorizontalAlignment="Left">
-                          <Panel.ContextMenu>
-                            <ContextMenu>
-                              <MenuItem Header="Copy property name" Command="{Binding CopyPropertyName} "/>
-                              <MenuItem Header="Copy value" Command="{Binding CopyValue} "/>
-                              <MenuItem Header="Copy resource key" Command="{Binding CopyResourceKey}" />
-                            </ContextMenu>
-                          </Panel.ContextMenu>
-                          <StackPanel Orientation="Horizontal" Spacing="2" HorizontalAlignment="Left">
-                            <TextBlock Text="{Binding Name}" FontWeight="SemiBold" />
-                            <TextBlock Text=":" />
-                            <ContentControl Content="{Binding Value}"/>
-                            <TextBlock>(</TextBlock>
-                            <Ellipse Height="8" Width="8" VerticalAlignment="Center" Fill="{Binding Tint}"/>
-                            <TextBlock FontStyle="Italic" Text="{Binding Key}" />
-                            <TextBlock>)</TextBlock>
+                        <DataTemplate DataType="IBrush">
+                          <StackPanel Orientation="Horizontal" Spacing="2">
+                            <Border BorderThickness="1" BorderBrush="Black" Background="{Binding}" Width="8" Height="8"/>
+                            <TextBlock Text="{Binding}" />
                           </StackPanel>
-                          <Rectangle Height="1" Fill="#6C6C6C" IsVisible="{Binding !IsActive}" />
-                        </Panel>
-                      </DataTemplate>
+                        </DataTemplate>
 
-                      <DataTemplate DataType="vm:SetterViewModel">
-                        <Panel Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}" IsVisible="{Binding IsVisible}" HorizontalAlignment="Left">
-                          <Panel.ContextMenu>
-                            <ContextMenu>
-                              <MenuItem Header="Copy property name" Command="{Binding CopyPropertyName} "/>
-                              <MenuItem Header="Copy value" Command="{Binding CopyValue} "/>
-                            </ContextMenu>
-                          </Panel.ContextMenu>
+                        <DataTemplate DataType="Color">
                           <StackPanel Orientation="Horizontal" Spacing="2">
-                            <TextBlock Text="{Binding Name}" FontWeight="SemiBold" />
-                            <TextBlock Text=":" />
-                            <ContentControl Content="{Binding Value}"/>
+                            <Border BorderThickness="1" BorderBrush="Black" Width="8" Height="8">
+                              <Border.Background>
+                                <SolidColorBrush Color="{Binding}" />
+                              </Border.Background>
+                            </Border>
+                            <TextBlock Text="{Binding}" />
                           </StackPanel>
-                          <Rectangle Height="1" Fill="#6C6C6C" VerticalAlignment="Center" IsVisible="{Binding !IsActive}" />
-                        </Panel>
-                      </DataTemplate>
-
-                    </ItemsControl.DataTemplates>
-                  </ItemsControl>
-
-                </Expander>
-              </Border>
-            </DataTemplate>
-          </ItemsControl.ItemTemplate>
-        </ItemsControl>
-      </ScrollViewer>
+                        </DataTemplate>
+
+                        <DataTemplate DataType="vm:ResourceSetterViewModel">
+                          <Panel Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}" IsVisible="{Binding IsVisible}" HorizontalAlignment="Left">
+                            <Panel.ContextMenu>
+                              <ContextMenu>
+                                <MenuItem Header="Copy property name" Command="{Binding CopyPropertyName} "/>
+                                <MenuItem Header="Copy value" Command="{Binding CopyValue} "/>
+                                <MenuItem Header="Copy resource key" Command="{Binding CopyResourceKey}" />
+                              </ContextMenu>
+                            </Panel.ContextMenu>
+                            <StackPanel Orientation="Horizontal" Spacing="2" HorizontalAlignment="Left">
+                              <TextBlock Text="{Binding Name}" FontWeight="SemiBold" />
+                              <TextBlock Text=":" />
+                              <ContentControl Content="{Binding Value}"/>
+                              <TextBlock>(</TextBlock>
+                              <Ellipse Height="8" Width="8" VerticalAlignment="Center" Fill="{Binding Tint}"/>
+                              <TextBlock FontStyle="Italic" Text="{Binding Key}" />
+                              <TextBlock>)</TextBlock>
+                            </StackPanel>
+                            <Rectangle Height="1" Fill="#6C6C6C" IsVisible="{Binding !IsActive}" />
+                          </Panel>
+                        </DataTemplate>
+
+                        <DataTemplate DataType="vm:SetterViewModel">
+                          <Panel Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}" IsVisible="{Binding IsVisible}" HorizontalAlignment="Left">
+                            <Panel.ContextMenu>
+                              <ContextMenu>
+                                <MenuItem Header="Copy property name" Command="{Binding CopyPropertyName} "/>
+                                <MenuItem Header="Copy value" Command="{Binding CopyValue} "/>
+                              </ContextMenu>
+                            </Panel.ContextMenu>
+                            <StackPanel Orientation="Horizontal" Spacing="2">
+                              <TextBlock Text="{Binding Name}" FontWeight="SemiBold" />
+                              <TextBlock Text=":" />
+                              <ContentControl Content="{Binding Value}"/>
+                            </StackPanel>
+                            <Rectangle Height="1" Fill="#6C6C6C" VerticalAlignment="Center" IsVisible="{Binding !IsActive}" />
+                          </Panel>
+                        </DataTemplate>
+
+                      </ItemsControl.DataTemplates>
+                    </ItemsControl>
+
+                  </Expander>
+                </Border>
+              </DataTemplate>
+            </ItemsControl.ItemTemplate>
+          </ItemsControl>
+        </ScrollViewer>
+
+        <Expander Header="Pseudo Classes" Grid.Row="2">
+          <ItemsControl Items="{Binding PseudoClasses}">
+            <ItemsControl.ItemsPanel>
+              <ItemsPanelTemplate>
+                <WrapPanel />
+              </ItemsPanelTemplate>
+            </ItemsControl.ItemsPanel>
+            <ItemsControl.ItemTemplate>
+              <DataTemplate>
+                <CheckBox Margin="2" Content="{Binding Name}" IsChecked="{Binding IsActive, Mode=TwoWay}" />
+              </DataTemplate>
+            </ItemsControl.ItemTemplate>
+          </ItemsControl>
+        </Expander>
 
-      <Expander Header="Pseudo Classes" Grid.Row="4">
-        <ItemsControl Items="{Binding PseudoClasses}">
-          <ItemsControl.ItemsPanel>
-            <ItemsPanelTemplate>
-              <WrapPanel />
-            </ItemsPanelTemplate>
-          </ItemsControl.ItemsPanel>
-          <ItemsControl.ItemTemplate>
-            <DataTemplate>
-              <CheckBox Margin="2" Content="{Binding Name}" IsChecked="{Binding IsActive, Mode=TwoWay}" />
-            </DataTemplate>
-          </ItemsControl.ItemTemplate>
-        </ItemsControl>
-      </Expander>
+      </Grid>
 
     </Grid>
   </Grid>

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

@@ -1,24 +1,10 @@
-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();
@@ -27,101 +13,6 @@ 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);
-            }
         }
     }
 }

+ 210 - 0
src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml

@@ -0,0 +1,210 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="Avalonia.Diagnostics.Views.LayoutExplorerView"
+             xmlns:local="clr-namespace:Avalonia.Diagnostics.Views"
+             xmlns:controls="clr-namespace:Avalonia.Diagnostics.Controls"
+             xmlns:converters="clr-namespace:Avalonia.Diagnostics.Converters">
+
+    <UserControl.Resources>
+        <SolidColorBrush x:Key="SizeGuidelineBrush" Color="#333333" />
+        <SolidColorBrush x:Key="MarginBackgroundBrush" Color="#D78965" />
+        <SolidColorBrush x:Key="MarginHighlightBrush" Color="#EA966F" />
+        <SolidColorBrush x:Key="BorderBackgroundBrush" Color="#E3C381" />
+        <SolidColorBrush x:Key="BorderHighlightBrush" Color="#EFCD88" />
+        <SolidColorBrush x:Key="PaddingBackgroundBrush" Color="#B8C47F" />
+        <SolidColorBrush x:Key="PaddingHighlightBrush" Color="#CEDA8E" />
+        <SolidColorBrush x:Key="SizeBackgroundBrush" Color="#88B2BD" />
+        <SolidColorBrush x:Key="SizeHighlightBrush" Color="#9ED0DC" />
+    </UserControl.Resources>
+
+    <Grid RowDefinitions="*,Auto">
+
+        <Viewbox Grid.Row="0" StretchDirection="DownOnly">
+            <Grid x:Name="LayoutRoot" Margin="8,0,8,8" RowDefinitions="Auto,Auto"
+                  ColumnDefinitions="Auto,Auto">
+
+                <Grid.Styles>
+                    <Style Selector="TextBlock.with-constraint">
+                        <Setter Property="TextDecorations" Value="Underline" />
+                    </Style>
+
+                </Grid.Styles>
+
+                <Border x:Name="VerticalSize" Grid.Row="0" Grid.Column="1">
+                    <TextBlock VerticalAlignment="Center" FontWeight="Bold"
+                               Classes.with-constraint="{Binding HeightConstraint, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
+                               Text="{Binding Height}"
+                               ToolTip.Tip="{Binding HeightConstraint}" />
+                </Border>
+
+                <Border x:Name="HorizontalSize" Grid.Row="1" Grid.Column="0">
+                    <TextBlock HorizontalAlignment="Center" FontWeight="Bold"
+                               Classes.with-constraint="{Binding WidthConstraint, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
+                               Text="{Binding Width}"
+                               ToolTip.Tip="{Binding WidthConstraint}" />
+                </Border>
+
+                <controls:ThicknessEditor Grid.Row="0" Grid.Column="0" Header="margin" VerticalAlignment="Top"
+                                          HorizontalAlignment="Center"
+                                          Background="{StaticResource MarginBackgroundBrush}"
+                                          Highlight="{StaticResource MarginHighlightBrush}"
+                                          Thickness="{Binding MarginThickness}">
+                    <controls:ThicknessEditor x:Name="BorderArea" Header="border"
+                                              Background="{StaticResource BorderBackgroundBrush}"
+                                              Highlight="{StaticResource BorderHighlightBrush}"
+                                              Thickness="{Binding BorderThickness}"
+                                              IsPresent="{Binding HasBorder}">
+                        <controls:ThicknessEditor x:Name="PaddingArea" Header="padding"
+                                                  Background="{StaticResource PaddingBackgroundBrush}"
+                                                  Highlight="{StaticResource PaddingHighlightBrush}"
+                                                  Thickness="{Binding PaddingThickness}"
+                                                  IsPresent="{Binding HasPadding}">
+                            <Border x:Name="ContentArea" BorderThickness="1" MinWidth="100" MinHeight="16"
+                                    VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
+                                <Border.Styles>
+                                    <Style Selector="Border">
+                                        <Setter Property="Background" Value="{StaticResource SizeBackgroundBrush}" />
+                                        <Setter Property="BorderBrush" Value="{DynamicResource ThicknessBorderBrush}" />
+                                    </Style>
+                                    <Style Selector="Border:pointerover">
+                                        <Setter Property="Background" Value="{StaticResource SizeHighlightBrush}" />
+                                        <Setter Property="BorderBrush" Value="{DynamicResource HighlightBorderBrush}" />
+                                    </Style>
+                                </Border.Styles>
+                                <TextBlock Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"
+                                           Text="content" />
+                            </Border>
+                        </controls:ThicknessEditor>
+                    </controls:ThicknessEditor>
+                </controls: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" />
+                            <Setter Property="IsHitTestVisible" Value="False" />
+                        </Style>
+                    </Canvas.Styles>
+                    <Rectangle x:Name="HorizontalSizeBegin" />
+                    <Rectangle x:Name="HorizontalSizeEnd" />
+                    <Rectangle x:Name="VerticalSizeBegin" />
+                    <Rectangle x:Name="VerticalSizeEnd" />
+                </Canvas>
+            </Grid>
+        </Viewbox>
+
+        <StackPanel Orientation="Vertical" Grid.Column="0" Grid.Row="1" Spacing="4"
+                    Margin="4" HorizontalAlignment="Stretch">
+
+            <StackPanel.Resources>
+                <converters:EnumToCheckedConverter x:Key="AlignmentConverter" />
+            </StackPanel.Resources>
+
+            <StackPanel.Styles>
+                <Style Selector="RadioButton">
+                    <Setter Property="Template">
+                        <ControlTemplate>
+                            <Border
+                                Background="{TemplateBinding Background}"
+                                BorderThickness="{TemplateBinding BorderThickness}"
+                                BorderBrush="{TemplateBinding BorderBrush}"
+                                Padding="{TemplateBinding Padding}">
+                                <Path x:Name="PART_Path" Fill="Black" Width="16" Height="16" Stretch="Uniform" />
+                            </Border>
+                        </ControlTemplate>
+                    </Setter>
+                </Style>
+
+                <Style Selector="RadioButton">
+                    <Setter Property="Background" Value="WhiteSmoke" />
+                    <Setter Property="Padding" Value="2" />
+                </Style>
+
+                <Style Selector="RadioButton:pointerover">
+                    <Setter Property="Background" Value="{DynamicResource ThemeControlHighlightMidBrush}" />
+                </Style>
+
+                <Style Selector="RadioButton:checked">
+                    <Setter Property="Background" Value="{DynamicResource ThemeAccentBrush4}" />
+                </Style>
+
+            </StackPanel.Styles>
+
+            <DockPanel HorizontalAlignment="Stretch" LastChildFill="True">
+
+                <DockPanel.Styles>
+                    <Style Selector="RadioButton.h-left /template/ Path#PART_Path">
+                        <Setter Property="Data" Value="M22 13V19H6V13H22M6 5V11H16V5H6M2 2V22H4V2H2" />
+                    </Style>
+                    <Style Selector="RadioButton.h-center /template/ Path#PART_Path">
+                        <Setter Property="Data"
+                                Value="M20 19H13V22H11V19H4V13H11V11H7V5H11V2H13V5H17V11H13V13H20V19Z" />
+                    </Style>
+                    <Style Selector="RadioButton.h-right /template/ Path#PART_Path">
+                        <Setter Property="Data" Value="M18 13V19H2V13H18M8 5V11H18V5H8M20 2V22H22V2H20Z" />
+                    </Style>
+                    <Style Selector="RadioButton.h-stretch /template/ Path#PART_Path">
+                        <Setter Property="Data" Value="M4,22H2V2h2V22z M22,2h-2v20h2V2z M13.5,7h-3v10h3V7z" />
+                    </Style>
+                </DockPanel.Styles>
+
+                <Label DockPanel.Dock="Left" Content="Horizontal Alignment" />
+
+                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
+                    <RadioButton GroupName="HAlignment" Classes="h-left"
+                                 ToolTip.Tip="Left"
+                                 IsChecked="{Binding HorizontalAlignment, Converter={StaticResource AlignmentConverter}, ConverterParameter={x:Static HorizontalAlignment.Left}}" />
+                    <RadioButton GroupName="HAlignment" Classes="h-center" BorderThickness="0,1,1,1"
+                                 ToolTip.Tip="Center"
+                                 IsChecked="{Binding HorizontalAlignment, Converter={StaticResource AlignmentConverter}, ConverterParameter={x:Static HorizontalAlignment.Center}}" />
+                    <RadioButton GroupName="HAlignment" Classes="h-right" BorderThickness="0,1,1,1"
+                                 ToolTip.Tip="Right"
+                                 IsChecked="{Binding HorizontalAlignment, Converter={StaticResource AlignmentConverter}, ConverterParameter={x:Static HorizontalAlignment.Right}}" />
+                    <RadioButton GroupName="HAlignment" Classes="h-stretch"
+                                 ToolTip.Tip="Stretch"
+                                 IsChecked="{Binding HorizontalAlignment, Converter={StaticResource AlignmentConverter}, ConverterParameter={x:Static HorizontalAlignment.Stretch}}" />
+                </StackPanel>
+
+            </DockPanel>
+
+            <DockPanel HorizontalAlignment="Stretch" LastChildFill="True">
+                <DockPanel.Styles>
+                    <Style Selector="RadioButton.v-top /template/ Path#PART_Path">
+                        <Setter Property="Data" Value="M11 22H5V6H11V22M19 6H13V16H19V6M22 2H2V4H22V2Z" />
+                    </Style>
+                    <Style Selector="RadioButton.v-center /template/ Path#PART_Path">
+                        <Setter Property="Data"
+                                Value="M5 20V13H2V11H5V4H11V11H13V7H19V11H22V13H19V17H13V13H11V20H5Z" />
+                    </Style>
+                    <Style Selector="RadioButton.v-bottom /template/ Path#PART_Path">
+                        <Setter Property="Data" Value="M11 18H5V2H11V18M19 8H13V18H19V8M22 20H2V22H22V20Z" />
+                    </Style>
+                    <Style Selector="RadioButton.v-stretch /template/ Path#PART_Path">
+                        <Setter Property="Data" Value="M22,2v2H2V2H22z M7,10.5v3h10v-3H7z M2,20v2h20v-2H2z" />
+                    </Style>
+                </DockPanel.Styles>
+
+                <Label DockPanel.Dock="Left" Content="Vertical Alignment" />
+
+                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
+                    <RadioButton GroupName="VAlignment" Classes="v-top"
+                                 ToolTip.Tip="Top"
+                                 IsChecked="{Binding VerticalAlignment, Converter={StaticResource AlignmentConverter}, ConverterParameter={x:Static VerticalAlignment.Top}}" />
+                    <RadioButton GroupName="VAlignment" Classes="v-center" BorderThickness="0,1,1,1"
+                                 ToolTip.Tip="Center"
+                                 IsChecked="{Binding VerticalAlignment, Converter={StaticResource AlignmentConverter}, ConverterParameter={x:Static VerticalAlignment.Center}}" />
+                    <RadioButton GroupName="VAlignment" Classes="v-bottom" BorderThickness="0,1,1,1"
+                                 ToolTip.Tip="Bottom"
+                                 IsChecked="{Binding VerticalAlignment, Converter={StaticResource AlignmentConverter}, ConverterParameter={x:Static VerticalAlignment.Bottom}}" />
+                    <RadioButton GroupName="VAlignment" Classes="v-stretch"
+                                 ToolTip.Tip="Stretch"
+                                 IsChecked="{Binding VerticalAlignment, Converter={StaticResource AlignmentConverter}, ConverterParameter={x:Static VerticalAlignment.Stretch}}" />
+                </StackPanel>
+
+            </DockPanel>
+        </StackPanel>
+    </Grid>
+
+</UserControl>

+ 128 - 0
src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml.cs

@@ -0,0 +1,128 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Diagnostics.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.VisualTree;
+
+namespace Avalonia.Diagnostics.Views
+{
+    internal class LayoutExplorerView : UserControl
+    {
+        private readonly ThicknessEditor _borderArea;
+        private readonly ThicknessEditor _paddingArea;
+        private readonly Rectangle _horizontalSizeBegin;
+        private readonly Rectangle _horizontalSizeEnd;
+        private readonly Rectangle _verticalSizeBegin;
+        private readonly Rectangle _verticalSizeEnd;
+        private readonly Grid _layoutRoot;
+        private readonly Border _horizontalSize;
+        private readonly Border _verticalSize;
+        private readonly Border _contentArea;
+
+        public LayoutExplorerView()
+        {
+            InitializeComponent();
+
+            _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 InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+
+        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);
+            }
+        }
+    }
+}

+ 1 - 0
src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml

@@ -15,6 +15,7 @@
     <Style Selector="DataGrid ContextMenu">
       <Setter Property="Foreground" Value="Black"/>
     </Style>
+    <StyleInclude Source="avares://Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.axaml" />
   </Window.Styles>
   
   <views:MainView/>

+ 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.4*,4,0.6*">    
+  <Grid ColumnDefinitions="0.35*,4,0.65*">
     <TreeView Name="tree"
               BorderThickness="0"
               Items="{Binding Nodes}"