Browse Source

Display control size accurately and add info about sizing constraints.

pull/5015/head
Dariusz Komosinski 6 years ago
parent
commit
77ecdc0013
  1. 75
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs
  2. 47
      src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
  3. 109
      src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml.cs
  4. 13
      src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs

75
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs

@ -11,7 +11,10 @@ namespace Avalonia.Diagnostics.ViewModels
private Thickness _marginThickness;
private Thickness _borderThickness;
private Thickness _paddingThickness;
private string _sizeText;
private double _width;
private double _height;
private string _widthConstraint;
private string _heightConstraint;
private bool _updatingFromControl;
public Thickness MarginThickness
@ -32,10 +35,28 @@ namespace Avalonia.Diagnostics.ViewModels
set => RaiseAndSetIfChanged(ref _paddingThickness, value);
}
public string SizeText
public double Width
{
get => _sizeText;
private set => RaiseAndSetIfChanged(ref _sizeText, value);
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; }
@ -65,6 +86,29 @@ namespace Avalonia.Diagnostics.ViewModels
}
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)
@ -99,11 +143,9 @@ namespace Avalonia.Diagnostics.ViewModels
{
_updatingFromControl = true;
var updateSize = false;
if (e.Property == Visual.BoundsProperty)
{
updateSize = true;
UpdateSize();
}
else
{
@ -120,16 +162,16 @@ namespace Avalonia.Diagnostics.ViewModels
else if (e.Property == Border.BorderThicknessProperty)
{
BorderThickness = ao.GetValue(Border.BorderThicknessProperty);
updateSize = true;
}
else if (e.Property == Layoutable.MinWidthProperty ||
e.Property == Layoutable.MaxWidthProperty ||
e.Property == Layoutable.MinHeightProperty ||
e.Property == Layoutable.MaxHeightProperty)
{
UpdateSizeConstraints();
}
}
}
if (updateSize)
{
UpdateSize();
}
}
finally
{
@ -141,9 +183,8 @@ namespace Avalonia.Diagnostics.ViewModels
{
var size = _control.Bounds;
size.Deflate(BorderThickness);
SizeText = $"{size.Width} × {size.Height}";
Width = size.Width;
Height = size.Height;
}
}
}

47
src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml

@ -106,15 +106,48 @@
<Grid Grid.Column="2" RowDefinitions="Auto,*" >
<TextBlock Grid.Row="0" Text="Layout Visualizer" Margin="4" />
<local:ThicknessEditor Grid.Row="1" Margin="8,0,8,8" Header="margin" VerticalAlignment="Top" HorizontalAlignment="Center" Background="{StaticResource MarginBackgroundBrush}" Thickness="{Binding Layout.MarginThickness}">
<local:ThicknessEditor Header="border" Background="{StaticResource BorderBackgroundBrush}" Thickness="{Binding Layout.BorderThickness}" IsPresent="{Binding Layout.HasBorder}">
<local:ThicknessEditor Header="padding" Background="{StaticResource PaddingBackgroundBrush}" Thickness="{Binding Layout.PaddingThickness}" IsPresent="{Binding Layout.HasPadding}">
<Border BorderThickness="1" BorderBrush="{StaticResource ThicknessBorderBrush}" MinWidth="100" MinHeight="16" Background="{StaticResource SizeBackgroundBrush}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<TextBlock Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Layout.SizeText}" />
</Border>
<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>
</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="#333333" />
<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>

109
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);
}
}
}
}

13
src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs

@ -1,8 +1,21 @@
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 =

Loading…
Cancel
Save