diff --git a/samples/DockMvvmSample/Views/ProportionalStackPanelView.axaml b/samples/DockMvvmSample/Views/ProportionalStackPanelView.axaml index 9e07a74fb..f1b182926 100644 --- a/samples/DockMvvmSample/Views/ProportionalStackPanelView.axaml +++ b/samples/DockMvvmSample/Views/ProportionalStackPanelView.axaml @@ -14,8 +14,8 @@ - - + + @@ -35,7 +35,7 @@ - + diff --git a/src/Dock.Avalonia/Controls/DockDockControl.axaml b/src/Dock.Avalonia/Controls/DockDockControl.axaml index d647abd88..30acd1bec 100644 --- a/src/Dock.Avalonia/Controls/DockDockControl.axaml +++ b/src/Dock.Avalonia/Controls/DockDockControl.axaml @@ -8,9 +8,6 @@ - - - diff --git a/src/Dock.Avalonia/Controls/DocumentDockControl.axaml b/src/Dock.Avalonia/Controls/DocumentDockControl.axaml index ac76ef257..505791a5e 100644 --- a/src/Dock.Avalonia/Controls/DocumentDockControl.axaml +++ b/src/Dock.Avalonia/Controls/DocumentDockControl.axaml @@ -7,13 +7,9 @@ - - - - + diff --git a/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml b/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml index 20f3e25a4..6859ab333 100644 --- a/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml +++ b/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml @@ -8,17 +8,14 @@ - - - - + - diff --git a/src/Dock.Avalonia/Controls/ProportionalStackPanel.cs b/src/Dock.Avalonia/Controls/ProportionalStackPanel.cs index 85a1bc139..6a7b8e6a3 100644 --- a/src/Dock.Avalonia/Controls/ProportionalStackPanel.cs +++ b/src/Dock.Avalonia/Controls/ProportionalStackPanel.cs @@ -4,6 +4,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Presenters; +using Avalonia.Data; using Avalonia.Layout; namespace Dock.Avalonia.Controls; @@ -28,6 +29,58 @@ public Orientation Orientation set => SetValue(OrientationProperty, value); } + /// + /// Defines the Proportion attached property. + /// + public static readonly AttachedProperty ProportionProperty = + AvaloniaProperty.RegisterAttached("Proportion", double.NaN, false, BindingMode.TwoWay); + + /// + /// Gets the value of the Proportion attached property on the specified control. + /// + /// The control. + /// The Proportion attached property. + public static double GetProportion(AvaloniaObject control) + { + return control.GetValue(ProportionProperty); + } + + /// + /// Sets the value of the Proportion attached property on the specified control. + /// + /// The control. + /// The value of the Proportion property. + public static void SetProportion(AvaloniaObject control, double value) + { + control.SetValue(ProportionProperty, value); + } + + /// + /// Defines the IsEmpty attached property. + /// + public static readonly AttachedProperty IsEmptyProperty = + AvaloniaProperty.RegisterAttached("IsEmpty", false, false, BindingMode.TwoWay); + + /// + /// Gets the value of the IsEmpty attached property on the specified control. + /// + /// The control. + /// The IsEmpty attached property. + public static bool GetIsEmpty(AvaloniaObject control) + { + return control.GetValue(IsEmptyProperty); + } + + /// + /// Sets the value of the IsEmpty attached property on the specified control. + /// + /// The control. + /// The value of the IsEmpty property. + public static void SetIsEmpty(AvaloniaObject control, bool value) + { + control.SetValue(IsEmptyProperty, value); + } + private void AssignProportions(global::Avalonia.Controls.Controls children) { var assignedProportion = 0.0; @@ -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) { @@ -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; } } @@ -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) @@ -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); } } } @@ -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; @@ -144,7 +197,7 @@ private double GetTotalSplitterThickness(global::Avalonia.Controls.Controls chil } else { - previousIsEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(c); + previousIsEmpty = GetIsEmpty(c); } } @@ -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. @@ -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) @@ -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. @@ -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)) @@ -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) { @@ -405,4 +458,12 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang InvalidateMeasure(); } } + + static ProportionalStackPanel() + { + AffectsParentMeasure(IsEmptyProperty); + AffectsParentArrange(IsEmptyProperty); + AffectsParentMeasure(ProportionProperty); + AffectsParentArrange(ProportionProperty); + } } diff --git a/src/Dock.Avalonia/Controls/ProportionalStackPanelSplitter.axaml.cs b/src/Dock.Avalonia/Controls/ProportionalStackPanelSplitter.axaml.cs index a318b46a0..19f4e4a69 100644 --- a/src/Dock.Avalonia/Controls/ProportionalStackPanelSplitter.axaml.cs +++ b/src/Dock.Avalonia/Controls/ProportionalStackPanelSplitter.axaml.cs @@ -1,4 +1,5 @@ -using Avalonia; +using System; +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Presenters; @@ -16,58 +17,6 @@ namespace Dock.Avalonia.Controls; [PseudoClasses(":horizontal", ":vertical")] public class ProportionalStackPanelSplitter : Thumb { - /// - /// Defines the Proportion attached property. - /// - public static readonly AttachedProperty ProportionProperty = - AvaloniaProperty.RegisterAttached("Proportion", double.NaN, false, BindingMode.TwoWay); - - /// - /// Gets the value of the Proportion attached property on the specified control. - /// - /// The control. - /// The Proportion attached property. - public static double GetProportion(AvaloniaObject control) - { - return control.GetValue(ProportionProperty); - } - - /// - /// Sets the value of the Proportion attached property on the specified control. - /// - /// The control. - /// The value of the Proportion property. - public static void SetProportion(AvaloniaObject control, double value) - { - control.SetValue(ProportionProperty, value); - } - - /// - /// Defines the IsEmpty attached property. - /// - public static readonly AttachedProperty IsEmptyProperty = - AvaloniaProperty.RegisterAttached("IsEmpty", false, false, BindingMode.TwoWay); - - /// - /// Gets the value of the IsEmpty attached property on the specified control. - /// - /// The control. - /// The IsEmpty attached property. - public static bool GetIsEmpty(AvaloniaObject control) - { - return control.GetValue(IsEmptyProperty); - } - - /// - /// Sets the value of the IsEmpty attached property on the specified control. - /// - /// The control. - /// The value of the IsEmpty property. - public static void SetIsEmpty(AvaloniaObject control, bool value) - { - control.SetValue(IsEmptyProperty, value); - } - /// /// Defines the property. /// @@ -139,79 +88,6 @@ internal static bool IsSplitter(Control? control, out ProportionalStackPanelSpli return false; } - internal static void SetControlProportion(Control? control, double proportion) - { - if (control is ContentPresenter contentPresenter) - { - if (contentPresenter.Child is null) - { - contentPresenter.UpdateChild(); - } - - if (contentPresenter.Child is not null) - { - SetProportion(contentPresenter.Child, proportion); - } - } - else - { - if (control is not null) - { - SetProportion(control, proportion); - } - } - } - - internal static double GetControlProportion(Control? control) - { - if (control is ContentPresenter contentPresenter) - { - if (contentPresenter.Child is null) - { - contentPresenter.UpdateChild(); - } - - if (contentPresenter.Child is not null) - { - return GetProportion(contentPresenter.Child); - } - - return double.NaN; - } - - if (control is not null) - { - return GetProportion(control); - } - - return double.NaN; - } - - internal static bool GetControlIsEmpty(Control? control) - { - if (control is ContentPresenter contentPresenter) - { - if (contentPresenter.Child is null) - { - contentPresenter.UpdateChild(); - } - - if (contentPresenter.Child is not null) - { - return GetIsEmpty(contentPresenter.Child); - } - - return false; - } - - if (control is not null) - { - return GetIsEmpty(control); - } - - return false; - } - /// protected override void OnPointerPressed(PointerPressedEventArgs e) { @@ -304,18 +180,7 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) nextIndex = children.IndexOf(this) + 1; } - var child = children[nextIndex]; - if (child is ContentPresenter contentPresenter) - { - if (contentPresenter.Child is null) - { - contentPresenter.UpdateChild(); - } - - return contentPresenter.Child; - } - - return child; + return children[nextIndex]; } private void SetTargetProportion(double dragDelta) @@ -329,8 +194,8 @@ private void SetTargetProportion(double dragDelta) var child = FindNextChild(panel); - var targetElementProportion = GetControlProportion(target); - var neighbourProportion = child is not null ? GetControlProportion(child) : double.NaN; + var targetElementProportion = ProportionalStackPanel.GetProportion(target); + var neighbourProportion = child is not null ? ProportionalStackPanel.GetProportion(child) : double.NaN; var dProportion = dragDelta / (panel.Orientation == Orientation.Vertical ? panel.Bounds.Height : panel.Bounds.Width); @@ -363,15 +228,12 @@ private void SetTargetProportion(double dragDelta) targetElementProportion += dProportion; } - SetProportion(target, targetElementProportion); + ProportionalStackPanel.SetProportion(target, targetElementProportion); if (child is not null) { - SetProportion(child, neighbourProportion); + ProportionalStackPanel.SetProportion(child, neighbourProportion); } - - panel.InvalidateMeasure(); - panel.InvalidateArrange(); } private void UpdateHeightOrWidth() @@ -422,17 +284,10 @@ private void UpdateHeightOrWidth() } var parent = Parent as Control; - var index = parent is null ? -1 :panel.Children.IndexOf(parent); + var index = parent is null ? -1 : panel.Children.IndexOf(parent); if (index > 0 && panel.Children.Count > 0) { - if (panel.Children[index - 1] is ContentPresenter contentPresenter) - { - return contentPresenter.Child; - } - else - { - return null; - } + return panel.Children[index - 1]; } } else diff --git a/src/Dock.Avalonia/Controls/ToolDockControl.axaml b/src/Dock.Avalonia/Controls/ToolDockControl.axaml index 4ad5ee920..0c401fb7d 100644 --- a/src/Dock.Avalonia/Controls/ToolDockControl.axaml +++ b/src/Dock.Avalonia/Controls/ToolDockControl.axaml @@ -7,14 +7,10 @@ - - - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property == ProportionalStackPanelSplitter.IsEmptyProperty) - { - // TODO: Handle invalidation better. - if (GetPanel() is { } panel) - { - panel.InvalidateMeasure(); - panel.InvalidateArrange(); - panel.UpdateLayout(); - - panel.InvalidateMeasure(); - panel.InvalidateArrange(); - panel.UpdateLayout(); - - panel.InvalidateMeasure(); - panel.InvalidateArrange(); - panel.UpdateLayout(); - } - } - } } diff --git a/src/Dock.Model.Avalonia/Controls/ProportionalDockSplitter.cs b/src/Dock.Model.Avalonia/Controls/ProportionalDockSplitter.cs index 5182849b9..84259b640 100644 --- a/src/Dock.Model.Avalonia/Controls/ProportionalDockSplitter.cs +++ b/src/Dock.Model.Avalonia/Controls/ProportionalDockSplitter.cs @@ -1,4 +1,6 @@ -using System.Runtime.Serialization; +using System; +using System.Runtime.Serialization; +using Avalonia; using Dock.Model.Avalonia.Core; using Dock.Model.Controls; diff --git a/tests/Dock.Avalonia.UnitTests/Controls/ProportionalStackPanelTests.cs b/tests/Dock.Avalonia.UnitTests/Controls/ProportionalStackPanelTests.cs index 807c08ae9..263f5f880 100644 --- a/tests/Dock.Avalonia.UnitTests/Controls/ProportionalStackPanelTests.cs +++ b/tests/Dock.Avalonia.UnitTests/Controls/ProportionalStackPanelTests.cs @@ -85,7 +85,7 @@ public void Lays_Out_Children_Default() new Border() { Background = Brushes.Red, - [ProportionalStackPanelSplitter.ProportionProperty] = 0.5 + [ProportionalStackPanel.ProportionProperty] = 0.5 }, new ProportionalStackPanelSplitter(), new Border() @@ -138,7 +138,7 @@ public void Lays_Out_Children_Default() new Border() { Background=Brushes.Red, - [ProportionalStackPanelSplitter.ProportionProperty] = 0.5 + [ProportionalStackPanel.ProportionProperty] = 0.5 } } },