diff --git a/samples/DockMvvmSample/Views/MainWindow.axaml b/samples/DockMvvmSample/Views/MainWindow.axaml
index be7c99312..b358b2328 100644
--- a/samples/DockMvvmSample/Views/MainWindow.axaml
+++ b/samples/DockMvvmSample/Views/MainWindow.axaml
@@ -20,22 +20,36 @@
ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaChromeHints="PreferSystemChrome"
ids:DockProperties.IsDragEnabled="True" ids:DockProperties.IsDropEnabled="True">
+
+
+
-
-
-
-
-
+
-
+
+
+
+
+
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/samples/DockXamlSample/MainWindow.axaml b/samples/DockXamlSample/MainWindow.axaml
index 43e33d2f4..6e1d1f45e 100644
--- a/samples/DockXamlSample/MainWindow.axaml
+++ b/samples/DockXamlSample/MainWindow.axaml
@@ -16,19 +16,33 @@
Title="Dock Avalonia Demo" Width="800" Height="600"
ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaChromeHints="PreferSystemChrome">
+
+
+
-
-
-
-
-
+
-
+
+
+
+
+
diff --git a/src/Dock.Avalonia/Controls/DockControl.axaml.cs b/src/Dock.Avalonia/Controls/DockControl.axaml.cs
index 07f6b8275..4d45cb25d 100644
--- a/src/Dock.Avalonia/Controls/DockControl.axaml.cs
+++ b/src/Dock.Avalonia/Controls/DockControl.axaml.cs
@@ -52,6 +52,12 @@ public class DockControl : TemplatedControl, IDockControl
public static readonly StyledProperty FactoryProperty =
AvaloniaProperty.Register(nameof(Factory));
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty IsDraggingDockProperty =
+ AvaloniaProperty.Register(nameof(IsDraggingDock));
+
///
public IDockManager DockManager => _dockManager;
@@ -94,6 +100,15 @@ public IFactory? Factory
set => SetValue(FactoryProperty, value);
}
+ ///
+ /// Gets or sets whether any dock is being dragged.
+ ///
+ public bool IsDraggingDock
+ {
+ get => GetValue(IsDraggingDockProperty);
+ set => SetValue(IsDraggingDockProperty, value);
+ }
+
///
/// Initialize the new instance of the .
///
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/HostWindow.axaml b/src/Dock.Avalonia/Controls/HostWindow.axaml
index c9ca9cc00..d64af0d17 100644
--- a/src/Dock.Avalonia/Controls/HostWindow.axaml
+++ b/src/Dock.Avalonia/Controls/HostWindow.axaml
@@ -1,6 +1,7 @@
+ xmlns:core="using:Dock.Model.Core"
+ xmlns:controls="clr-namespace:Dock.Model.Controls;assembly=Dock.Model">
@@ -14,6 +15,7 @@
+
diff --git a/src/Dock.Avalonia/Controls/HostWindow.axaml.cs b/src/Dock.Avalonia/Controls/HostWindow.axaml.cs
index c49308947..336387290 100644
--- a/src/Dock.Avalonia/Controls/HostWindow.axaml.cs
+++ b/src/Dock.Avalonia/Controls/HostWindow.axaml.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Controls;
@@ -6,6 +7,7 @@
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Styling;
+using Avalonia.VisualTree;
using Dock.Avalonia.Internal;
using Dock.Model;
using Dock.Model.Core;
@@ -269,7 +271,15 @@ public void Present(bool isDialog)
Window.Factory?.OnWindowOpened(Window);
}
- Show();
+ var ownerDockControl = Window?.Layout?.Factory?.DockControls.FirstOrDefault();
+ if (ownerDockControl is Control control && control.GetVisualRoot() is Window parentWindow)
+ {
+ Show(parentWindow);
+ }
+ else
+ {
+ Show();
+ }
}
}
}
@@ -327,12 +337,6 @@ public void GetSize(out double width, out double height)
height = Height;
}
- ///
- public void SetTopmost(bool topmost)
- {
- Topmost = topmost;
- }
-
///
public void SetTitle(string title)
{
diff --git a/src/Dock.Avalonia/Controls/PinnedDockControl.axaml b/src/Dock.Avalonia/Controls/PinnedDockControl.axaml
new file mode 100644
index 000000000..de2a1b6db
--- /dev/null
+++ b/src/Dock.Avalonia/Controls/PinnedDockControl.axaml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Dock.Avalonia/Controls/PinnedDockControl.axaml.cs b/src/Dock.Avalonia/Controls/PinnedDockControl.axaml.cs
new file mode 100644
index 000000000..cc90832b1
--- /dev/null
+++ b/src/Dock.Avalonia/Controls/PinnedDockControl.axaml.cs
@@ -0,0 +1,91 @@
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+using Dock.Model.Core;
+
+namespace Dock.Avalonia.Controls;
+
+///
+/// Interaction logic for xaml.
+///
+[TemplatePart("PART_PinnedDock", typeof(ContentControl)/*, IsRequired = true*/)]
+[TemplatePart("PART_PinnedDockGrid", typeof(Grid)/*, IsRequired = true*/)]
+public class PinnedDockControl : TemplatedControl
+{
+ ///
+ /// Define the property.
+ ///
+ public static readonly StyledProperty PinnedDockAlignmentProperty = AvaloniaProperty.Register(nameof(PinnedDockAlignment));
+
+ ///
+ /// Gets or sets pinned dock alignment
+ ///
+ public Alignment PinnedDockAlignment
+ {
+ get => GetValue(PinnedDockAlignmentProperty);
+ set => SetValue(PinnedDockAlignmentProperty, value);
+ }
+
+ private Grid? _pinnedDockGrid;
+ private ContentControl? _pinnedDock;
+
+ static PinnedDockControl()
+ {
+ PinnedDockAlignmentProperty.Changed.AddClassHandler((control, e) => control.UpdateGrid());
+ }
+
+ private void UpdateGrid()
+ {
+ if (_pinnedDockGrid == null || _pinnedDock == null)
+ return;
+
+ _pinnedDockGrid.RowDefinitions.Clear();
+ _pinnedDockGrid.ColumnDefinitions.Clear();
+ switch (PinnedDockAlignment)
+ {
+ case Alignment.Unset:
+ case Alignment.Left:
+ _pinnedDockGrid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto) { MinWidth = 50 });
+ _pinnedDockGrid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto));
+ _pinnedDockGrid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Star) { MinWidth = 50 });
+ Grid.SetColumn(_pinnedDock, 0);
+ Grid.SetRow(_pinnedDock, 0);
+ break;
+ case Alignment.Bottom:
+ _pinnedDockGrid.RowDefinitions.Add(new RowDefinition(GridLength.Star) { MinHeight = 50 });
+ _pinnedDockGrid.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
+ _pinnedDockGrid.RowDefinitions.Add(new RowDefinition(GridLength.Auto) { MinHeight = 50 });
+ Grid.SetColumn(_pinnedDock, 0);
+ Grid.SetRow(_pinnedDock, 2);
+ break;
+ case Alignment.Right:
+ _pinnedDockGrid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Star) { MinWidth = 50 });
+ _pinnedDockGrid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto));
+ _pinnedDockGrid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto) { MinWidth = 50 });
+ Grid.SetColumn(_pinnedDock, 2);
+ Grid.SetRow(_pinnedDock, 0);
+ break;
+ case Alignment.Top:
+ _pinnedDockGrid.RowDefinitions.Add(new RowDefinition(GridLength.Auto) { MinHeight = 50 });
+ _pinnedDockGrid.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
+ _pinnedDockGrid.RowDefinitions.Add(new RowDefinition(GridLength.Star) { MinHeight = 50 });
+ Grid.SetColumn(_pinnedDock, 1);
+ Grid.SetRow(_pinnedDock, 0);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ ///
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+ _pinnedDockGrid = e.NameScope.Get("PART_PinnedDockGrid");
+ _pinnedDock = e.NameScope.Get("PART_PinnedDock");
+ UpdateGrid();
+ }
+}
+
diff --git a/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml b/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml
index 20f3e25a4..32976b692 100644
--- a/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml
+++ b/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml
@@ -8,17 +8,21 @@
-
-
-
-
+
-
diff --git a/src/Dock.Avalonia/Controls/ProportionalStackPanel.cs b/src/Dock.Avalonia/Controls/ProportionalStackPanel.cs
index 85a1bc139..f937bcb27 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 IsCollapsed attached property.
+ ///
+ public static readonly AttachedProperty IsCollapsedProperty =
+ AvaloniaProperty.RegisterAttached("IsCollapsed", false, false, BindingMode.TwoWay);
+
+ ///
+ /// Gets the value of the IsCollapsed attached property on the specified control.
+ ///
+ /// The control.
+ /// The IsCollapsed attached property.
+ public static bool GetIsCollapsed(AvaloniaObject control)
+ {
+ return control.GetValue(IsCollapsedProperty);
+ }
+
+ ///
+ /// Sets the value of the IsCollapsed attached property on the specified control.
+ ///
+ /// The control.
+ /// The value of the IsCollapsed property.
+ public static void SetIsCollapsed(AvaloniaObject control, bool value)
+ {
+ control.SetValue(IsCollapsedProperty, value);
+ }
+
private void AssignProportions(global::Avalonia.Controls.Controls children)
{
var assignedProportion = 0.0;
@@ -36,14 +89,14 @@ 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 isCollapsed = GetIsCollapsed(control);
var isSplitter = ProportionalStackPanelSplitter.IsSplitter(control, out _);
if (!isSplitter)
{
- var proportion = ProportionalStackPanelSplitter.GetControlProportion(control);
+ var proportion = GetProportion(control);
- if (isEmpty)
+ if (isCollapsed)
{
proportion = 0.0;
}
@@ -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 isCollapsed = GetIsCollapsed(c);
+ return !isCollapsed && 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);
- return !isEmpty && !ProportionalStackPanelSplitter.IsSplitter(c, out _);
+ var isCollapsed = GetIsCollapsed(c);
+ return !isCollapsed && !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,19 +154,19 @@ private void AssignProportions(global::Avalonia.Controls.Controls children)
foreach (var child in children.Where(c =>
{
- var isEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(c);
- return !isEmpty && !ProportionalStackPanelSplitter.IsSplitter(c, out _);
+ var isCollapsed = GetIsCollapsed(c);
+ return !isCollapsed && !ProportionalStackPanelSplitter.IsSplitter(c, out _);
}))
{
- var proportion = ProportionalStackPanelSplitter.GetControlProportion(child) - toRemove;
- ProportionalStackPanelSplitter.SetControlProportion(child, proportion);
+ var proportion = GetProportion(child) - toRemove;
+ SetProportion(child, proportion);
}
}
}
private double GetTotalSplitterThickness(global::Avalonia.Controls.Controls children)
{
- var previousIsEmpty = false;
+ var previousisCollapsed = false;
var totalThickness = 0.0;
for (var i = 0; i < children.Count; i++)
@@ -123,17 +176,17 @@ private double GetTotalSplitterThickness(global::Avalonia.Controls.Controls chil
if (isSplitter && proportionalStackPanelSplitter is not null)
{
- if (previousIsEmpty)
+ if (previousisCollapsed)
{
- previousIsEmpty = false;
+ previousisCollapsed = false;
continue;
}
if (i + 1 < Children.Count)
{
var nextControl = Children[i + 1];
- var nextIsEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(nextControl);
- if (nextIsEmpty)
+ var nextisCollapsed = GetIsCollapsed(nextControl);
+ if (nextisCollapsed)
{
continue;
}
@@ -144,7 +197,7 @@ private double GetTotalSplitterThickness(global::Avalonia.Controls.Controls chil
}
else
{
- previousIsEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(c);
+ previousisCollapsed = GetIsCollapsed(c);
}
}
@@ -171,7 +224,7 @@ protected override Size MeasureOverride(Size constraint)
AssignProportions(Children);
- var previousIsEmpty = false;
+ var previousisCollapsed = false;
// Measure each of the Children
for (var i = 0; i < Children.Count; i++)
@@ -184,13 +237,13 @@ 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);
- if (isEmpty)
+ var isCollapsed = !isSplitter && GetIsCollapsed(control);
+ if (isCollapsed)
{
// TODO: Also handle next is empty.
- previousIsEmpty = true;
+ previousisCollapsed = true;
var size = new Size();
control.Measure(size);
continue;
@@ -220,25 +273,25 @@ protected override Size MeasureOverride(Size constraint)
}
else
{
- var nextIsEmpty = false;
+ var nextisCollapsed = false;
if (i + 1 < Children.Count)
{
var nextControl = Children[i + 1];
- nextIsEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(nextControl);
+ nextisCollapsed = !ProportionalStackPanelSplitter.IsSplitter(nextControl, out _ ) && GetIsCollapsed(nextControl);
}
- if (previousIsEmpty || nextIsEmpty)
+ if (previousisCollapsed || nextisCollapsed)
{
var size = new Size();
control.Measure(size);
- previousIsEmpty = true;
+ previousisCollapsed = true;
continue;
}
control.Measure(remainingSize);
}
- previousIsEmpty = false;
+ previousisCollapsed = false;
var desiredSize = control.DesiredSize;
@@ -298,33 +351,33 @@ protected override Size ArrangeOverride(Size arrangeSize)
AssignProportions(Children);
- var previousIsEmpty = false;
+ var previousisCollapsed = false;
for (var i = 0; i < Children.Count; i++)
{
var control = Children[i];
- var isEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(control);
- if (isEmpty)
+ var isSplitter = ProportionalStackPanelSplitter.IsSplitter(control, out _);
+
+ var isCollapsed = !isSplitter && GetIsCollapsed(control);
+ if (isCollapsed)
{
// TODO: Also handle next is empty.
- previousIsEmpty = true;
+ previousisCollapsed = true;
var rect = new Rect();
control.Arrange(rect);
index++;
continue;
}
- var isSplitter = ProportionalStackPanelSplitter.IsSplitter(control, out _);
-
- var nextIsEmpty = false;
+ var nextisCollapsed = false;
if (i + 1 < Children.Count)
{
var nextControl = Children[i + 1];
- nextIsEmpty = ProportionalStackPanelSplitter.GetControlIsEmpty(nextControl);
+ nextisCollapsed = !ProportionalStackPanelSplitter.IsSplitter(nextControl, out _) && GetIsCollapsed(nextControl);
}
- if (isSplitter && (previousIsEmpty || nextIsEmpty))
+ if (isSplitter && (previousisCollapsed || nextisCollapsed))
{
var rect = new Rect();
control.Arrange(rect);
@@ -332,7 +385,7 @@ protected override Size ArrangeOverride(Size arrangeSize)
continue;
}
- previousIsEmpty = false;
+ previousisCollapsed = false;
// Determine the remaining space left to arrange the element
var remainingRect = new Rect(
@@ -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(IsCollapsedProperty);
+ AffectsParentArrange(IsCollapsedProperty);
+ 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/RootDockControl.axaml b/src/Dock.Avalonia/Controls/RootDockControl.axaml
index a3908a24e..5a5605cc0 100644
--- a/src/Dock.Avalonia/Controls/RootDockControl.axaml
+++ b/src/Dock.Avalonia/Controls/RootDockControl.axaml
@@ -30,7 +30,10 @@
Orientation="Horizontal"
Items="{Binding BottomPinnedDockables}"
IsVisible="{Binding !!BottomPinnedDockables.Count}" />
-
+
+
+
+
diff --git a/src/Dock.Avalonia/Controls/RootDockControl.axaml.cs b/src/Dock.Avalonia/Controls/RootDockControl.axaml.cs
index 71558223f..159cc76ca 100644
--- a/src/Dock.Avalonia/Controls/RootDockControl.axaml.cs
+++ b/src/Dock.Avalonia/Controls/RootDockControl.axaml.cs
@@ -1,10 +1,26 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
+using Avalonia.Interactivity;
+using Dock.Model.Controls;
namespace Dock.Avalonia.Controls;
///
/// Interaction logic for xaml.
///
+[TemplatePart("PART_MainContent", typeof(ContentControl)/*, IsRequired = true*/)]
public class RootDockControl : TemplatedControl
{
+ ///
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+ var mainContent = e.NameScope.Get("PART_MainContent");
+ mainContent.AddHandler(PointerPressedEvent, (_, _) =>
+ {
+ if (DataContext is IRootDock rootDock)
+ rootDock.Factory?.HidePreviewingDockables(rootDock);
+ }, RoutingStrategies.Tunnel);
+ }
}
diff --git a/src/Dock.Avalonia/Controls/ToolChromeControl.axaml b/src/Dock.Avalonia/Controls/ToolChromeControl.axaml
index c65511a89..f871bceb5 100644
--- a/src/Dock.Avalonia/Controls/ToolChromeControl.axaml
+++ b/src/Dock.Avalonia/Controls/ToolChromeControl.axaml
@@ -6,29 +6,51 @@
-
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -43,7 +65,7 @@
@@ -51,24 +73,45 @@
-
-
+
+
+
+
+
+
@@ -162,37 +205,39 @@
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/Dock.Avalonia/Controls/ToolChromeControl.axaml.cs b/src/Dock.Avalonia/Controls/ToolChromeControl.axaml.cs
index 8431d4389..1b86f3534 100644
--- a/src/Dock.Avalonia/Controls/ToolChromeControl.axaml.cs
+++ b/src/Dock.Avalonia/Controls/ToolChromeControl.axaml.cs
@@ -12,7 +12,7 @@ namespace Dock.Avalonia.Controls;
///
/// Dock tool chrome content control.
///
-[PseudoClasses(":floating", ":active")]
+[PseudoClasses(":floating", ":active", ":pinned", ":maximized")]
public class ToolChromeControl : ContentControl
{
///
@@ -27,12 +27,57 @@ public class ToolChromeControl : ContentControl
public static readonly StyledProperty IsActiveProperty =
AvaloniaProperty.Register(nameof(IsActive));
+ ///
+ /// Define the property.
+ ///
+ public static readonly StyledProperty IsPinnedProperty =
+ AvaloniaProperty.Register(nameof(IsPinned));
+
+ ///
+ /// Define the property.
+ ///
+ public static readonly StyledProperty IsFloatingProperty =
+ AvaloniaProperty.Register(nameof(IsFloating));
+
+ ///
+ /// Define the property.
+ ///
+ public static readonly StyledProperty IsMaximizedProperty =
+ AvaloniaProperty.Register(nameof(IsMaximized));
+
+ ///
+ /// Gets or sets is pinned
+ ///
+ public bool IsPinned
+ {
+ get => GetValue(IsPinnedProperty);
+ set => SetValue(IsPinnedProperty, value);
+ }
+
+ ///
+ /// Gets or sets is floating
+ ///
+ public bool IsFloating
+ {
+ get => GetValue(IsFloatingProperty);
+ set => SetValue(IsFloatingProperty, value);
+ }
+
+ ///
+ /// Gets or sets is maximized
+ ///
+ public bool IsMaximized
+ {
+ get => GetValue(IsMaximizedProperty);
+ set => SetValue(IsMaximizedProperty, value);
+ }
+
///
/// Initialize the new instance of the .
///
public ToolChromeControl()
{
- UpdatePseudoClasses(IsActive);
+ UpdatePseudoClasses();
}
///
@@ -89,7 +134,21 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
window.AttachGrip(this);
- PseudoClasses.Set(":floating", true);
+ SetCurrentValue(IsFloatingProperty, true);
+ }
+
+ var maximizeRestoreButton = e.NameScope.Get
+public class EitherNotNullConverter : IMultiValueConverter
+{
+ ///
+ /// Gets instance.
+ ///
+ public static readonly EitherNotNullConverter Instance = new EitherNotNullConverter();
+
+ ///
+ public object? Convert(IList
IDockable? Owner { get; set; }
+ ///
+ /// Gets or sets dockable original owner.
+ ///
+ IDockable? OriginalOwner { get; set; }
+
///
/// Gets or sets dockable factory.
///
diff --git a/src/Dock.Model/Core/IFactory.cs b/src/Dock.Model/Core/IFactory.cs
index 94cc856da..f9d319b98 100644
--- a/src/Dock.Model/Core/IFactory.cs
+++ b/src/Dock.Model/Core/IFactory.cs
@@ -184,7 +184,7 @@ public partial interface IFactory
/// The dockable to find root for.
/// The predicate to filter root docks.
/// The root dockable instance or null if root dockable was not found.
- IRootDock? FindRoot(IDockable dockable, Func predicate);
+ IRootDock? FindRoot(IDockable dockable, Func? predicate = null);
///
/// Searches dock for dockable.
@@ -266,11 +266,36 @@ public partial interface IFactory
void SwapDockable(IDock sourceDock, IDock targetDock, IDockable sourceDockable, IDockable targetDockable);
///
- /// Pins dockable.
+ /// Pins or unpins a dockable.
///
- /// The dockable to pin.
+ /// The dockable to pin/unpin.
void PinDockable(IDockable dockable);
+ ///
+ /// Unpins a dockable.
+ ///
+ /// The dockable to unpin.
+ void UnpinDockable(IDockable dockable);
+
+ ///
+ /// Temporarily shows a pinned dockable.
+ ///
+ /// The dockable to show.
+ void PreviewPinnedDockable(IDockable dockable);
+
+ ///
+ /// Hides all temporarily shown pinned dockables.
+ ///
+ /// The owner of the pinned dockables
+ void HidePreviewingDockables(IRootDock rootDock);
+
+ ///
+ /// Returns true if dockable is pinned.
+ ///
+ /// The dockable to check.
+ /// The root dock. If null, the root will be automatically found.
+ bool IsDockablePinned(IDockable dockable, IRootDock? rootDock = null);
+
///
/// Floats dockable.
///
diff --git a/src/Dock.Model/Core/IHostWindow.cs b/src/Dock.Model/Core/IHostWindow.cs
index ffaff405f..eef221c7d 100644
--- a/src/Dock.Model/Core/IHostWindow.cs
+++ b/src/Dock.Model/Core/IHostWindow.cs
@@ -64,12 +64,6 @@ public interface IHostWindow
/// The host height.
void GetSize(out double width, out double height);
- ///
- /// Sets host topmost.
- ///
- /// The host topmost.
- void SetTopmost(bool topmost);
-
///
/// Sets host title.
///
diff --git a/src/Dock.Model/DockManager.cs b/src/Dock.Model/DockManager.cs
index dc45141d9..c830f060e 100644
--- a/src/Dock.Model/DockManager.cs
+++ b/src/Dock.Model/DockManager.cs
@@ -80,6 +80,7 @@ private void SplitToolDockable(IDockable sourceDockable, IDock sourceDockableOwn
var targetToolDock = factory.CreateToolDock();
targetToolDock.Title = nameof(IToolDock);
+ targetToolDock.Alignment = operation.ToAlignment();
targetToolDock.VisibleDockables = factory.CreateList();
factory.MoveDockable(sourceDockableOwner, targetToolDock, sourceDockable, null);
factory.SplitToDock(targetDock, targetToolDock, operation);
diff --git a/src/Dock.Model/FactoryBase.Dockable.cs b/src/Dock.Model/FactoryBase.Dockable.cs
index e002a05d0..c987ef63b 100644
--- a/src/Dock.Model/FactoryBase.Dockable.cs
+++ b/src/Dock.Model/FactoryBase.Dockable.cs
@@ -1,4 +1,6 @@
-using Dock.Model.Controls;
+using System.Diagnostics;
+using System.Linq;
+using Dock.Model.Controls;
using Dock.Model.Core;
namespace Dock.Model;
@@ -13,8 +15,7 @@ public virtual void AddDockable(IDock dock, IDockable dockable)
{
InitDockable(dockable, dock);
dock.VisibleDockables ??= CreateList();
- dock.VisibleDockables.Add(dockable);
- dock.IsEmpty = dock.VisibleDockables.Count == 0;
+ AddVisibleDockable(dock, dockable);
OnDockableAdded(dockable);
}
@@ -25,8 +26,7 @@ public virtual void InsertDockable(IDock dock, IDockable dockable, int index)
{
InitDockable(dockable, dock);
dock.VisibleDockables ??= CreateList();
- dock.VisibleDockables.Insert(index, dockable);
- dock.IsEmpty = dock.VisibleDockables.Count == 0;
+ InsertVisibleDockable(dock, index, dockable);
OnDockableAdded(dockable);
}
}
@@ -34,6 +34,9 @@ public virtual void InsertDockable(IDock dock, IDockable dockable, int index)
///
public virtual void RemoveDockable(IDockable dockable, bool collapse)
{
+ // to correctly remove a pinned dockable, it needs to be unpinned
+ UnpinDockable(dockable);
+
if (dockable.Owner is not IDock dock || dock.VisibleDockables is null)
{
return;
@@ -45,8 +48,7 @@ public virtual void RemoveDockable(IDockable dockable, bool collapse)
return;
}
- dock.VisibleDockables.Remove(dockable);
- dock.IsEmpty = dock.VisibleDockables.Count == 0;
+ RemoveVisibleDockable(dock, dockable);
OnDockableRemoved(dockable);
var indexActiveDockable = index > 0 ? index - 1 : 0;
@@ -102,11 +104,9 @@ public virtual void MoveDockable(IDock dock, IDockable sourceDockable, IDockable
if (sourceIndex >= 0 && targetIndex >= 0 && sourceIndex != targetIndex)
{
- dock.VisibleDockables.RemoveAt(sourceIndex);
- dock.IsEmpty = dock.VisibleDockables.Count == 0;
+ RemoveVisibleDockableAt(dock, sourceIndex);
OnDockableRemoved(sourceDockable);
- dock.VisibleDockables.Insert(targetIndex, sourceDockable);
- dock.IsEmpty = dock.VisibleDockables.Count == 0;
+ InsertVisibleDockable(dock, targetIndex, sourceDockable);
OnDockableAdded(sourceDockable);
OnDockableMoved(sourceDockable);
dock.ActiveDockable = sourceDockable;
@@ -116,6 +116,8 @@ public virtual void MoveDockable(IDock dock, IDockable sourceDockable, IDockable
///
public virtual void MoveDockable(IDock sourceDock, IDock targetDock, IDockable sourceDockable, IDockable? targetDockable)
{
+ UnpinDockable(sourceDockable);
+
if (targetDock.VisibleDockables is null)
{
targetDock.VisibleDockables = CreateList();
@@ -177,11 +179,9 @@ public virtual void MoveDockable(IDock sourceDock, IDock targetDock, IDockable s
var sourceIndex = sourceDock.VisibleDockables.IndexOf(sourceDockable);
if (sourceIndex < targetIndex)
{
- targetDock.VisibleDockables.Insert(targetIndex + 1, sourceDockable);
- targetDock.IsEmpty = targetDock.VisibleDockables.Count == 0;
+ InsertVisibleDockable(targetDock, targetIndex + 1, sourceDockable);
OnDockableAdded(sourceDockable);
- targetDock.VisibleDockables.RemoveAt(sourceIndex);
- targetDock.IsEmpty = targetDock.VisibleDockables.Count == 0;
+ RemoveVisibleDockableAt(targetDock, sourceIndex);
OnDockableRemoved(sourceDockable);
OnDockableMoved(sourceDockable);
}
@@ -190,11 +190,9 @@ public virtual void MoveDockable(IDock sourceDock, IDock targetDock, IDockable s
var removeIndex = sourceIndex + 1;
if (targetDock.VisibleDockables.Count + 1 > removeIndex)
{
- targetDock.VisibleDockables.Insert(targetIndex, sourceDockable);
- targetDock.IsEmpty = targetDock.VisibleDockables.Count == 0;
+ InsertVisibleDockable(targetDock, targetIndex, sourceDockable);
OnDockableAdded(sourceDockable);
- targetDock.VisibleDockables.RemoveAt(removeIndex);
- targetDock.IsEmpty = targetDock.VisibleDockables.Count == 0;
+ RemoveVisibleDockableAt(targetDock, removeIndex);
OnDockableRemoved(sourceDockable);
OnDockableMoved(sourceDockable);
}
@@ -203,8 +201,7 @@ public virtual void MoveDockable(IDock sourceDock, IDock targetDock, IDockable s
else
{
RemoveDockable(sourceDockable, true);
- targetDock.VisibleDockables.Insert(targetIndex, sourceDockable);
- targetDock.IsEmpty = targetDock.VisibleDockables.Count == 0;
+ InsertVisibleDockable(targetDock, targetIndex, sourceDockable);
OnDockableAdded(sourceDockable);
OnDockableMoved(sourceDockable);
InitDockable(sourceDockable, targetDock);
@@ -230,11 +227,9 @@ public virtual void SwapDockable(IDock dock, IDockable sourceDockable, IDockable
var originalTargetDockable = dock.VisibleDockables[targetIndex];
dock.VisibleDockables[targetIndex] = originalSourceDockable;
- dock.IsEmpty = dock.VisibleDockables.Count == 0;
OnDockableRemoved(originalTargetDockable);
OnDockableAdded(originalSourceDockable);
dock.VisibleDockables[sourceIndex] = originalTargetDockable;
- dock.IsEmpty = dock.VisibleDockables.Count == 0;
OnDockableAdded(originalTargetDockable);
OnDockableSwapped(originalSourceDockable);
OnDockableSwapped(originalTargetDockable);
@@ -259,7 +254,7 @@ public virtual void SwapDockable(IDock sourceDock, IDock targetDock, IDockable s
var originalTargetDockable = targetDock.VisibleDockables[targetIndex];
sourceDock.VisibleDockables[sourceIndex] = originalTargetDockable;
targetDock.VisibleDockables[targetIndex] = originalSourceDockable;
-
+
InitDockable(originalSourceDockable, targetDock);
InitDockable(originalTargetDockable, sourceDock);
@@ -271,8 +266,19 @@ public virtual void SwapDockable(IDock sourceDock, IDock targetDock, IDockable s
}
}
- private bool IsDockablePinned(IDockable dockable, IRootDock rootDock)
+ ///
+ public bool IsDockablePinned(IDockable dockable, IRootDock? rootDock = null)
{
+ if (rootDock == null)
+ {
+ rootDock = FindRoot(dockable);
+
+ if (rootDock == null)
+ {
+ return false;
+ }
+ }
+
if (rootDock.LeftPinnedDockables is not null)
{
if (rootDock.LeftPinnedDockables.Contains(dockable))
@@ -308,6 +314,51 @@ private bool IsDockablePinned(IDockable dockable, IRootDock rootDock)
return false;
}
+ ///
+ public void HidePreviewingDockables(IRootDock rootDock)
+ {
+ if (rootDock.PinnedDock == null)
+ return;
+
+ if (rootDock.PinnedDock.VisibleDockables != null)
+ {
+ foreach (var dockable in rootDock.PinnedDock.VisibleDockables)
+ {
+ dockable.Owner = dockable.OriginalOwner;
+ dockable.OriginalOwner = null;
+ }
+ RemoveAllVisibleDockables(rootDock.PinnedDock);
+ }
+ }
+
+ ///
+ public void PreviewPinnedDockable(IDockable dockable)
+ {
+ var rootDock = FindRoot(dockable, _ => true);
+ if (rootDock is null)
+ {
+ return;
+ }
+
+ HidePreviewingDockables(rootDock);
+
+ var alignment = (dockable.Owner as IToolDock)?.Alignment ?? Alignment.Unset;
+
+ if (rootDock.PinnedDock == null)
+ {
+ rootDock.PinnedDock = CreateToolDock();
+ InitDockable(rootDock.PinnedDock, rootDock);
+ }
+ rootDock.PinnedDock.Alignment = alignment;
+
+ Debug.Assert(rootDock.PinnedDock != null);
+
+ RemoveAllVisibleDockables(rootDock.PinnedDock);
+
+ dockable.OriginalOwner = dockable.Owner;
+ AddVisibleDockable(rootDock.PinnedDock, dockable);
+ }
+
///
public virtual void PinDockable(IDockable dockable)
{
@@ -320,7 +371,7 @@ public virtual void PinDockable(IDockable dockable)
{
return;
}
-
+
var isVisible = false;
if (toolDock.VisibleDockables is not null)
@@ -330,7 +381,9 @@ public virtual void PinDockable(IDockable dockable)
var isPinned = IsDockablePinned(dockable, rootDock);
- var alignment = toolDock.Alignment;
+ var originalToolDock = dockable.OriginalOwner as IToolDock;
+
+ var alignment = originalToolDock?.Alignment ?? toolDock.Alignment;
if (isVisible && !isPinned)
{
@@ -360,11 +413,10 @@ public virtual void PinDockable(IDockable dockable)
break;
}
}
-
+
if (toolDock.VisibleDockables is not null)
{
- toolDock.VisibleDockables.Remove(dockable);
- toolDock.IsEmpty = toolDock.VisibleDockables.Count == 0;
+ RemoveVisibleDockable(toolDock, dockable);
OnDockableRemoved(dockable);
}
@@ -417,7 +469,7 @@ public virtual void PinDockable(IDockable dockable)
// TODO: Handle IsExpanded property of IToolDock.
// TODO: Handle AutoHide property of IToolDock.
}
- else if (!isVisible && isPinned)
+ else if (isPinned)
{
// Unpin dockable.
@@ -468,10 +520,20 @@ public virtual void PinDockable(IDockable dockable)
}
}
- toolDock.VisibleDockables.Add(dockable);
- toolDock.IsEmpty = toolDock.VisibleDockables.Count == 0;
+ if (!isVisible)
+ {
+ AddVisibleDockable(toolDock, dockable);
+ }
+ else
+ {
+ Debug.Assert(dockable.OriginalOwner is IDock);
+ var originalOwner = (IDock)dockable.OriginalOwner;
+ HidePreviewingDockables(rootDock);
+ AddVisibleDockable(originalOwner, dockable);
+ }
+
OnDockableAdded(dockable);
-
+
// TODO: Handle ActiveDockable state.
// TODO: Handle IsExpanded property of IToolDock.
// TODO: Handle AutoHide property of IToolDock.
@@ -486,6 +548,15 @@ public virtual void PinDockable(IDockable dockable)
}
}
+ ///
+ public void UnpinDockable(IDockable dockable)
+ {
+ if (IsDockablePinned(dockable))
+ {
+ PinDockable(dockable);
+ }
+ }
+
///
public virtual void FloatDockable(IDockable dockable)
{
@@ -494,6 +565,8 @@ public virtual void FloatDockable(IDockable dockable)
return;
}
+ UnpinDockable(dockable);
+
dock.GetPointerScreenPosition(out var dockPointerScreenX, out var dockPointerScreenY);
dockable.GetPointerScreenPosition(out var dockablePointerScreenX, out var dockablePointerScreenY);
@@ -532,7 +605,7 @@ public virtual void FloatDockable(IDockable dockable)
///
public virtual void CloseDockable(IDockable dockable)
{
- if (dockable.OnClose())
+ if (dockable.CanClose && dockable.OnClose())
{
RemoveDockable(dockable, true);
OnDockableClosed(dockable);
@@ -590,7 +663,7 @@ public virtual void CloseLeftDockables(IDockable dockable)
{
return;
}
-
+
CloseDockablesRange(dock, 0, indexOf - 1);
}
@@ -607,7 +680,88 @@ public virtual void CloseRightDockables(IDockable dockable)
{
return;
}
-
+
CloseDockablesRange(dock, indexOf + 1, dock.VisibleDockables.Count - 1);
}
+
+ ///
+ /// Adds the dockable to the visible dockables list of the dock.
+ ///
+ protected void AddVisibleDockable(IDock dock, IDockable dockable)
+ {
+ if (dock.VisibleDockables == null)
+ {
+ dock.VisibleDockables = CreateList();
+ }
+ dock.VisibleDockables.Add(dockable);
+ UpdateIsEmpty(dock);
+ }
+
+ ///
+ /// Inserts the dockable to the visible dockables list of the dock at the specified index.
+ ///
+ protected void InsertVisibleDockable(IDock dock, int index, IDockable dockable)
+ {
+ if (dock.VisibleDockables == null)
+ {
+ dock.VisibleDockables = CreateList();
+ }
+ dock.VisibleDockables.Insert(index, dockable);
+ UpdateIsEmpty(dock);
+ }
+
+ ///
+ /// Removes the dockable from the visible dockables list of the dock.
+ ///
+ protected void RemoveVisibleDockable(IDock dock, IDockable dockable)
+ {
+ if (dock.VisibleDockables != null)
+ {
+ dock.VisibleDockables.Remove(dockable);
+ UpdateIsEmpty(dock);
+ }
+ }
+
+ ///
+ /// Removes all visible dockable of the dock.
+ ///
+ protected void RemoveAllVisibleDockables(IDock dock)
+ {
+ if (dock.VisibleDockables != null)
+ {
+ if (dock.VisibleDockables.Count > 0)
+ {
+ dock.VisibleDockables.Clear();
+ UpdateIsEmpty(dock);
+ }
+ }
+ }
+
+ ///
+ /// Removes the dockable at the specified index from the visible dockables list of the dock.
+ ///
+ protected void RemoveVisibleDockableAt(IDock dock, int index)
+ {
+ if (dock.VisibleDockables != null)
+ {
+ dock.VisibleDockables.RemoveAt(index);
+ UpdateIsEmpty(dock);
+ }
+ }
+
+ private void UpdateIsEmpty(IDock dock)
+ {
+ bool oldIsEmpty = dock.IsEmpty;
+
+ var newIsEmpty = dock.VisibleDockables == null
+ || dock.VisibleDockables?.Count == 0
+ || dock.VisibleDockables!.All(x => x is IDock { IsEmpty: true } or IProportionalDockSplitter);
+
+ if (oldIsEmpty != newIsEmpty)
+ {
+ dock.IsEmpty = newIsEmpty;
+ if (dock.Owner is IDock parent)
+ UpdateIsEmpty(parent);
+ }
+ }
}
diff --git a/src/Dock.Model/FactoryBase.Init.cs b/src/Dock.Model/FactoryBase.Init.cs
index 634ceb812..9b0ec5096 100644
--- a/src/Dock.Model/FactoryBase.Init.cs
+++ b/src/Dock.Model/FactoryBase.Init.cs
@@ -54,7 +54,7 @@ public virtual void InitDockable(IDockable dockable, IDockable? owner)
InitDockable(child, dockable);
}
- dock.IsEmpty = dock.VisibleDockables.Count == 0;
+ UpdateIsEmpty(dock);
}
}
diff --git a/src/Dock.Model/FactoryBase.Locator.cs b/src/Dock.Model/FactoryBase.Locator.cs
index 3e0893370..b3169a070 100644
--- a/src/Dock.Model/FactoryBase.Locator.cs
+++ b/src/Dock.Model/FactoryBase.Locator.cs
@@ -74,13 +74,13 @@ public abstract partial class FactoryBase
}
///
- public virtual IRootDock? FindRoot(IDockable dockable, Func predicate)
+ public virtual IRootDock? FindRoot(IDockable dockable, Func? predicate = null)
{
if (dockable.Owner is null)
{
return null;
}
- if (dockable.Owner is IRootDock rootDock && predicate(rootDock))
+ if (dockable.Owner is IRootDock rootDock && (predicate?.Invoke(rootDock) ?? true))
{
return rootDock;
}
diff --git a/src/Dock.Model/FactoryBase.cs b/src/Dock.Model/FactoryBase.cs
index 15fc26aa0..9b21cfd16 100644
--- a/src/Dock.Model/FactoryBase.cs
+++ b/src/Dock.Model/FactoryBase.cs
@@ -125,8 +125,7 @@ public virtual IDock CreateSplitLayout(IDock dock, IDockable dockable, DockOpera
split.VisibleDockables = CreateList();
if (split.VisibleDockables is not null)
{
- split.VisibleDockables.Add(dockable);
- split.IsEmpty = split.VisibleDockables.Count == 0;
+ AddVisibleDockable(split, dockable);
OnDockableAdded(dockable);
split.ActiveDockable = dockable;
}
@@ -166,8 +165,7 @@ public virtual IDock CreateSplitLayout(IDock dock, IDockable dockable, DockOpera
{
if (layout.VisibleDockables is not null)
{
- layout.VisibleDockables.Add(split);
- layout.IsEmpty = layout.VisibleDockables.Count == 0;
+ AddVisibleDockable(layout, split);
OnDockableAdded(split);
layout.ActiveDockable = split;
}
@@ -179,8 +177,7 @@ public virtual IDock CreateSplitLayout(IDock dock, IDockable dockable, DockOpera
{
if (layout.VisibleDockables is not null)
{
- layout.VisibleDockables.Add(dock);
- layout.IsEmpty = layout.VisibleDockables.Count == 0;
+ AddVisibleDockable(layout, dock);
OnDockableAdded(dock);
layout.ActiveDockable = dock;
}
@@ -189,8 +186,7 @@ public virtual IDock CreateSplitLayout(IDock dock, IDockable dockable, DockOpera
}
}
- layout.VisibleDockables?.Add(splitter);
- layout.IsEmpty = layout.VisibleDockables?.Count == 0;
+ AddVisibleDockable(layout, splitter);
OnDockableAdded(splitter);
switch (operation)
@@ -200,8 +196,7 @@ public virtual IDock CreateSplitLayout(IDock dock, IDockable dockable, DockOpera
{
if (layout.VisibleDockables is not null)
{
- layout.VisibleDockables.Add(dock);
- layout.IsEmpty = layout.VisibleDockables.Count == 0;
+ AddVisibleDockable(layout, dock);
OnDockableAdded(dock);
layout.ActiveDockable = dock;
}
@@ -213,8 +208,7 @@ public virtual IDock CreateSplitLayout(IDock dock, IDockable dockable, DockOpera
{
if (layout.VisibleDockables is not null)
{
- layout.VisibleDockables.Add(split);
- layout.IsEmpty = layout.VisibleDockables.Count == 0;
+ AddVisibleDockable(layout, split);
OnDockableAdded(split);
layout.ActiveDockable = split;
}
@@ -242,11 +236,9 @@ public virtual void SplitToDock(IDock dock, IDockable dockable, DockOperation op
if (index >= 0)
{
var layout = CreateSplitLayout(dock, dockable, operation);
- ownerDock.VisibleDockables.RemoveAt(index);
- ownerDock.IsEmpty = ownerDock.VisibleDockables.Count == 0;
+ RemoveVisibleDockableAt(ownerDock, index);
OnDockableRemoved(dockable);
- ownerDock.VisibleDockables.Insert(index, layout);
- layout.IsEmpty = layout.VisibleDockables?.Count == 0;
+ InsertVisibleDockable(ownerDock, index, layout);
OnDockableAdded(dockable);
InitDockable(layout, ownerDock);
ownerDock.ActiveDockable = layout;
@@ -263,7 +255,6 @@ public virtual void SplitToDock(IDock dock, IDockable dockable, DockOperation op
public virtual IDockWindow? CreateWindowFrom(IDockable dockable)
{
IDockable? target;
- bool topmost;
switch (dockable)
{
@@ -276,13 +267,11 @@ public virtual void SplitToDock(IDock dock, IDockable dockable, DockOperation op
dock.VisibleDockables = CreateList();
if (dock.VisibleDockables is not null)
{
- dock.VisibleDockables.Add(dockable);
- dock.IsEmpty = dock.VisibleDockables.Count == 0;
+ AddVisibleDockable(dock, dockable);
OnDockableAdded(dockable);
dock.ActiveDockable = dockable;
}
}
- topmost = true;
break;
}
case IDocument:
@@ -309,43 +298,36 @@ public virtual void SplitToDock(IDock dock, IDockable dockable, DockOperation op
}
if (dock.VisibleDockables is not null)
{
- dock.VisibleDockables.Add(dockable);
- dock.IsEmpty = dock.VisibleDockables.Count == 0;
+ AddVisibleDockable(dock, dockable);
OnDockableAdded(dockable);
dock.ActiveDockable = dockable;
}
}
- topmost = false;
break;
}
case IToolDock:
{
target = dockable;
- topmost = true;
break;
}
case IDocumentDock:
{
target = dockable;
- topmost = false;
break;
}
case IProportionalDock proportionalDock:
{
target = proportionalDock;
- topmost = false;
break;
}
case IDockDock dockDock:
{
target = dockDock;
- topmost = false;
break;
}
case IRootDock rootDock:
{
target = rootDock.ActiveDockable;
- topmost = false;
break;
}
default:
@@ -359,8 +341,7 @@ public virtual void SplitToDock(IDock dock, IDockable dockable, DockOperation op
root.VisibleDockables = CreateList();
if (root.VisibleDockables is not null && target is not null)
{
- root.VisibleDockables.Add(target);
- root.IsEmpty = root.VisibleDockables.Count == 0;
+ AddVisibleDockable(root, target);
OnDockableAdded(target);
root.ActiveDockable = target;
root.DefaultDockable = target;
@@ -372,7 +353,6 @@ public virtual void SplitToDock(IDock dock, IDockable dockable, DockOperation op
window.Title = "";
window.Width = double.NaN;
window.Height = double.NaN;
- window.Topmost = topmost;
window.Layout = root;
root.Window = window;
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
}
}
},