Skip to content

Commit

Permalink
Fix ProportionalStackPanel layout update when IsEmpty of its children…
Browse files Browse the repository at this point in the history
… changes
  • Loading branch information
BAndysc committed Mar 8, 2024
1 parent 9cded63 commit 2ad2e87
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 226 deletions.
6 changes: 3 additions & 3 deletions samples/DockMvvmSample/Views/ProportionalStackPanelView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
<TabControl>
<TabItem Header="Default">
<ProportionalStackPanel Orientation="Horizontal">
<ProportionalStackPanel ProportionalStackPanelSplitter.Proportion="0.5">
<Rectangle Fill="Red" ProportionalStackPanelSplitter.Proportion="0.5" />
<ProportionalStackPanel ProportionalStackPanel.Proportion="0.5">
<Rectangle Fill="Red" ProportionalStackPanel.Proportion="0.5" />
<ProportionalStackPanelSplitter />
<Rectangle Fill="Green" />
<ProportionalStackPanelSplitter />
Expand All @@ -35,7 +35,7 @@
<ProportionalStackPanelSplitter />
<Rectangle Fill="Blue" />
<ProportionalStackPanelSplitter />
<Rectangle Fill="Red" ProportionalStackPanelSplitter.Proportion="0.5" />
<Rectangle Fill="Red" ProportionalStackPanel.Proportion="0.5" />
</ProportionalStackPanel>
</ProportionalStackPanel>
</TabItem>
Expand Down
3 changes: 0 additions & 3 deletions src/Dock.Avalonia/Controls/DockDockControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@

<ControlTheme x:Key="{x:Type DockDockControl}" TargetType="DockDockControl">

<Setter Property="(ProportionalStackPanelSplitter.Proportion)" Value="{Binding Proportion}" x:DataType="core:IDock" />
<Setter Property="(ProportionalStackPanelSplitter.IsEmpty)" Value="{Binding IsEmpty}" x:DataType="core:IDock" />

<Setter Property="Template">
<ControlTemplate>
<DockableControl TrackingMode="Visible">
Expand Down
6 changes: 1 addition & 5 deletions src/Dock.Avalonia/Controls/DocumentDockControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@

<ControlTheme x:Key="{x:Type DocumentDockControl}" TargetType="DocumentDockControl">

<Setter Property="(ProportionalStackPanelSplitter.Proportion)" Value="{Binding Proportion}" x:DataType="core:IDock" />
<Setter Property="(ProportionalStackPanelSplitter.IsEmpty)" Value="{Binding IsEmpty}" x:DataType="core:IDock" />

<Setter Property="Template">
<ControlTemplate>
<DockableControl TrackingMode="Visible"
ProportionalStackPanelSplitter.Proportion="{Binding Proportion}">
<DockableControl TrackingMode="Visible">
<DocumentControl IsActive="{Binding IsActive}" />
</DockableControl>
</ControlTemplate>
Expand Down
11 changes: 4 additions & 7 deletions src/Dock.Avalonia/Controls/ProportionalDockControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@

<ControlTheme x:Key="{x:Type ProportionalDockControl}" TargetType="ProportionalDockControl">

<Setter Property="(ProportionalStackPanelSplitter.Proportion)" Value="{Binding Proportion}" x:DataType="core:IDock" />
<Setter Property="(ProportionalStackPanelSplitter.IsEmpty)" Value="{Binding IsEmpty}" x:DataType="core:IDock" />

<Setter Property="Template">
<ControlTemplate>
<DockableControl TrackingMode="Visible"
ProportionalStackPanelSplitter.Proportion="{Binding Proportion}">
<DockableControl TrackingMode="Visible">
<ItemsControl ItemsSource="{Binding VisibleDockables}">
<ItemsControl.Styles>
<Style Selector="ItemsControl > ContentPresenter > :is(core|IDock)" x:DataType="core:IDock">
<Setter Property="(ProportionalStackPanelSplitter.Proportion)" Value="{Binding Proportion}" />
<Style Selector="ItemsControl > ContentPresenter">
<Setter x:DataType="core:IDock" Property="(ProportionalStackPanel.Proportion)" Value="{Binding Proportion}" />
<Setter x:DataType="core:IDock" Property="(ProportionalStackPanel.IsEmpty)" Value="{Binding IsEmpty}" />
</Style>
</ItemsControl.Styles>
<ItemsControl.ItemsPanel>
Expand Down
103 changes: 82 additions & 21 deletions src/Dock.Avalonia/Controls/ProportionalStackPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Data;
using Avalonia.Layout;

namespace Dock.Avalonia.Controls;
Expand All @@ -28,6 +29,58 @@ public Orientation Orientation
set => SetValue(OrientationProperty, value);
}

/// <summary>
/// Defines the Proportion attached property.
/// </summary>
public static readonly AttachedProperty<double> ProportionProperty =
AvaloniaProperty.RegisterAttached<ProportionalStackPanel, Control, double>("Proportion", double.NaN, false, BindingMode.TwoWay);

/// <summary>
/// Gets the value of the Proportion attached property on the specified control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The Proportion attached property.</returns>
public static double GetProportion(AvaloniaObject control)
{
return control.GetValue(ProportionProperty);
}

/// <summary>
/// Sets the value of the Proportion attached property on the specified control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The value of the Proportion property.</param>
public static void SetProportion(AvaloniaObject control, double value)
{
control.SetValue(ProportionProperty, value);
}

/// <summary>
/// Defines the IsEmpty attached property.
/// </summary>
public static readonly AttachedProperty<bool> IsEmptyProperty =
AvaloniaProperty.RegisterAttached<ProportionalStackPanel, Control, bool>("IsEmpty", false, false, BindingMode.TwoWay);

/// <summary>
/// Gets the value of the IsEmpty attached property on the specified control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The IsEmpty attached property.</returns>
public static bool GetIsEmpty(AvaloniaObject control)
{
return control.GetValue(IsEmptyProperty);
}

/// <summary>
/// Sets the value of the IsEmpty attached property on the specified control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The value of the IsEmpty property.</param>
public static void SetIsEmpty(AvaloniaObject control, bool value)
{
control.SetValue(IsEmptyProperty, value);
}

private void AssignProportions(global::Avalonia.Controls.Controls children)
{
var assignedProportion = 0.0;
Expand All @@ -36,12 +89,12 @@ private void AssignProportions(global::Avalonia.Controls.Controls children)
for (var i = 0; i < children.Count; i++)
{
var control = children[i];
var isEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(control);
var isEmpty = GetIsEmpty(control);
var isSplitter = ProportionalStackPanelSplitter.IsSplitter(control, out _);

if (!isSplitter)
{
var proportion = ProportionalStackPanelSplitter.GetControlProportion(control);
var proportion = GetProportion(control);

if (isEmpty)
{
Expand All @@ -64,14 +117,14 @@ private void AssignProportions(global::Avalonia.Controls.Controls children)
var toAssign = assignedProportion;
foreach (var control in children.Where(c =>
{
var isEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(c);
return !isEmpty && double.IsNaN(ProportionalStackPanelSplitter.GetControlProportion(c));
var isEmpty = GetIsEmpty(c);
return !isEmpty && double.IsNaN(GetProportion(c));
}))
{
if (!ProportionalStackPanelSplitter.IsSplitter(control, out _))
{
var proportion = (1.0 - toAssign) / unassignedProportions;
ProportionalStackPanelSplitter.SetControlProportion(control, proportion);
SetProportion(control, proportion);
assignedProportion += (1.0 - toAssign) / unassignedProportions;
}
}
Expand All @@ -85,12 +138,12 @@ private void AssignProportions(global::Avalonia.Controls.Controls children)

foreach (var child in children.Where(c =>
{
var isEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(c);
var isEmpty = GetIsEmpty(c);
return !isEmpty && !ProportionalStackPanelSplitter.IsSplitter(c, out _);
}))
{
var proportion = ProportionalStackPanelSplitter.GetControlProportion(child) + toAdd;
ProportionalStackPanelSplitter.SetControlProportion(child, proportion);
var proportion = GetProportion(child) + toAdd;
SetProportion(child, proportion);
}
}
else if (assignedProportion > 1)
Expand All @@ -101,12 +154,12 @@ private void AssignProportions(global::Avalonia.Controls.Controls children)

foreach (var child in children.Where(c =>
{
var isEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(c);
var isEmpty = GetIsEmpty(c);
return !isEmpty && !ProportionalStackPanelSplitter.IsSplitter(c, out _);
}))
{
var proportion = ProportionalStackPanelSplitter.GetControlProportion(child) - toRemove;
ProportionalStackPanelSplitter.SetControlProportion(child, proportion);
var proportion = GetProportion(child) - toRemove;
SetProportion(child, proportion);
}
}
}
Expand All @@ -132,7 +185,7 @@ private double GetTotalSplitterThickness(global::Avalonia.Controls.Controls chil
if (i + 1 < Children.Count)
{
var nextControl = Children[i + 1];
var nextIsEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(nextControl);
var nextIsEmpty = GetIsEmpty(nextControl);
if (nextIsEmpty)
{
continue;
Expand All @@ -144,7 +197,7 @@ private double GetTotalSplitterThickness(global::Avalonia.Controls.Controls chil
}
else
{
previousIsEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(c);
previousIsEmpty = GetIsEmpty(c);
}
}

Expand Down Expand Up @@ -184,9 +237,9 @@ protected override Size MeasureOverride(Size constraint)
Math.Max(0.0, constraint.Width - usedWidth - splitterThickness),
Math.Max(0.0, constraint.Height - usedHeight - splitterThickness));

var proportion = ProportionalStackPanelSplitter.GetControlProportion(control);
var proportion = GetProportion(control);

var isEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(control);
var isEmpty = !isSplitter && GetIsEmpty(control);
if (isEmpty)
{
// TODO: Also handle next is empty.
Expand Down Expand Up @@ -224,7 +277,7 @@ protected override Size MeasureOverride(Size constraint)
if (i + 1 < Children.Count)
{
var nextControl = Children[i + 1];
nextIsEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(nextControl);
nextIsEmpty = !ProportionalStackPanelSplitter.IsSplitter(nextControl, out _ ) && GetIsEmpty(nextControl);
}

if (previousIsEmpty || nextIsEmpty)
Expand Down Expand Up @@ -304,7 +357,9 @@ protected override Size ArrangeOverride(Size arrangeSize)
{
var control = Children[i];

var isEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(control);
var isSplitter = ProportionalStackPanelSplitter.IsSplitter(control, out _);

var isEmpty = !isSplitter && GetIsEmpty(control);
if (isEmpty)
{
// TODO: Also handle next is empty.
Expand All @@ -315,13 +370,11 @@ protected override Size ArrangeOverride(Size arrangeSize)
continue;
}

var isSplitter = ProportionalStackPanelSplitter.IsSplitter(control, out _);

var nextIsEmpty = false;
if (i + 1 < Children.Count)
{
var nextControl = Children[i + 1];
nextIsEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(nextControl);
nextIsEmpty = !ProportionalStackPanelSplitter.IsSplitter(nextControl, out _) && GetIsEmpty(nextControl);
}

if (isSplitter && (previousIsEmpty || nextIsEmpty))
Expand All @@ -347,7 +400,7 @@ protected override Size ArrangeOverride(Size arrangeSize)
if (index < Children.Count)
{
var desiredSize = control.DesiredSize;
var proportion = ProportionalStackPanelSplitter.GetControlProportion(control);
var proportion = GetProportion(control);

switch (Orientation)
{
Expand Down Expand Up @@ -405,4 +458,12 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
InvalidateMeasure();
}
}

static ProportionalStackPanel()
{
AffectsParentMeasure<ProportionalStackPanel>(IsEmptyProperty);
AffectsParentArrange<ProportionalStackPanel>(IsEmptyProperty);
AffectsParentMeasure<ProportionalStackPanel>(ProportionProperty);
AffectsParentArrange<ProportionalStackPanel>(ProportionProperty);
}
}
Loading

0 comments on commit 2ad2e87

Please sign in to comment.