Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/dock-dockable-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Dockable items such as documents, tools and docks implement the `IDockable` inte
| `IsEmpty` | Indicates a placeholder dockable with no content. |
| `IsCollapsable` | When `false`, the dock will remain even if it contains no children. |
| `Proportion` | Size ratio used by `ProportionalDock`. |
| `MinWidth` | Optional minimum width. Overrides the current proportion if larger. |
| `MaxWidth` | Optional maximum width. Overrides the proportion if smaller. |
| `MinHeight` | Optional minimum height. Overrides the current proportion if larger. |
| `MaxHeight` | Optional maximum height. Overrides the proportion if smaller. |
| `CanClose` | Whether the user can close the dockable via UI commands. |
| `CanPin` | Allows pinning and unpinning of tools. |
| `CanFloat` | Controls if the item may be detached into a floating window. |
Expand Down
2 changes: 1 addition & 1 deletion docs/dock-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This reference summarizes the most commonly used classes in Dock. It is based on

| Type | Description |
| --- | --- |
| `IDockable` | Base interface for items that can be shown in a dock. Provides `Id`, `Title`, `Context` and lifecycle methods like `OnClose`. |
| `IDockable` | Base interface for items that can be shown in a dock. Provides `Id`, `Title`, `Context`, optional size limits and lifecycle methods like `OnClose`. |
| `IDock` | Extends `IDockable` with collections of visible dockables and commands such as `GoBack`, `GoForward`, `Navigate` or `Close`. |
| `IRootDock` | The top level container. In addition to the `IDock` members it exposes pinned dock collections and commands to manage windows. |
| `IProportionalDock` | A dock that lays out its children horizontally or vertically using a `Proportion` value. |
Expand Down
6 changes: 5 additions & 1 deletion src/Dock.Avalonia/Controls/ProportionalDockControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
Classes="ProportionalStackPanel">
<ItemsControl.Styles>
<Style Selector="ItemsControl.ProportionalStackPanel > ContentPresenter">
<Setter Property="(ProportionalStackPanel.Proportion)"
<Setter Property="(ProportionalStackPanel.Proportion)"
Value="{Binding Proportion}" />
<Setter Property="(ProportionalStackPanel.IsCollapsed)">
<Setter.Value>
Expand All @@ -30,6 +30,10 @@
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="MinWidth" Value="{Binding MinWidth}" />
<Setter Property="MaxWidth" Value="{Binding MaxWidth}" />
<Setter Property="MinHeight" Value="{Binding MinHeight}" />
<Setter Property="MaxHeight" Value="{Binding MaxHeight}" />
</Style>
</ItemsControl.Styles>
<ItemsControl.ItemsPanel>
Expand Down
4 changes: 4 additions & 0 deletions src/Dock.Avalonia/Controls/ToolChromeControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@

<Setter Property="(DockProperties.IsDragEnabled)" Value="{Binding CanDrag}" />
<Setter Property="(DockProperties.IsDropEnabled)" Value="{Binding CanDrop}" />
<Setter Property="MinWidth" Value="{Binding MinWidth}" />
<Setter Property="MaxWidth" Value="{Binding MaxWidth}" />
<Setter Property="MinHeight" Value="{Binding MinHeight}" />
<Setter Property="MaxHeight" Value="{Binding MaxHeight}" />

<Setter Property="Background" Value="{DynamicResource DockThemeBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource DockThemeBorderLowBrush}" />
Expand Down
4 changes: 4 additions & 0 deletions src/Dock.Avalonia/Controls/ToolContentControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@

<Setter Property="(DockProperties.IsDragEnabled)" Value="{Binding CanDrag}" />
<Setter Property="(DockProperties.IsDropEnabled)" Value="{Binding CanDrop}" />
<Setter Property="MinWidth" Value="{Binding MinWidth}" />
<Setter Property="MaxWidth" Value="{Binding MaxWidth}" />
<Setter Property="MinHeight" Value="{Binding MinHeight}" />
<Setter Property="MaxHeight" Value="{Binding MaxHeight}" />

<Setter Property="Template">
<ControlTemplate>
Expand Down
4 changes: 4 additions & 0 deletions src/Dock.Avalonia/Controls/ToolControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

<Setter Property="(DockProperties.IsDragEnabled)" Value="{Binding CanDrag}" />
<Setter Property="(DockProperties.IsDropEnabled)" Value="{Binding CanDrop}" />
<Setter Property="MinWidth" Value="{Binding MinWidth}" />
<Setter Property="MaxWidth" Value="{Binding MaxWidth}" />
<Setter Property="MinHeight" Value="{Binding MinHeight}" />
<Setter Property="MaxHeight" Value="{Binding MaxHeight}" />

<Setter Property="HeaderTemplate">
<DataTemplate DataType="core:IDockable">
Expand Down
4 changes: 4 additions & 0 deletions src/Dock.Avalonia/Controls/ToolDockControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@

<Setter Property="(DockProperties.IsDragEnabled)" Value="{Binding CanDrag}" />
<Setter Property="(DockProperties.IsDropEnabled)" Value="{Binding CanDrop}" />
<Setter Property="MinWidth" Value="{Binding MinWidth}" />
<Setter Property="MaxWidth" Value="{Binding MaxWidth}" />
<Setter Property="MinHeight" Value="{Binding MinHeight}" />
<Setter Property="MaxHeight" Value="{Binding MaxHeight}" />

<Setter Property="Template">
<ControlTemplate>
Expand Down
58 changes: 46 additions & 12 deletions src/Dock.Controls.ProportionalStackPanel/ProportionalStackPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,27 +87,62 @@ public static void SetIsCollapsed(AvaloniaObject control, bool value)
control.SetValue(IsCollapsedProperty, value);
}

private void AssignProportions()
private void AssignProportions(Size size, double splitterThickness)
{
isAssigningProportions = true;
try
{
AssignProportionsInternal(Children);
AssignProportionsInternal(Children, size, splitterThickness, Orientation);
}
finally
{
isAssigningProportions = false;
}
}

private static void AssignProportionsInternal(Avalonia.Controls.Controls children)
private static double ClampProportion(
Control control,
Orientation orientation,
double available,
double proportion)
{
double min = orientation == Orientation.Horizontal ? control.MinWidth : control.MinHeight;
double max = orientation == Orientation.Horizontal ? control.MaxWidth : control.MaxHeight;

var minProp = !double.IsNaN(min) && min > 0 ? min / available : 0.0;
var maxProp = !double.IsNaN(max) && !double.IsPositiveInfinity(max) ? max / available : double.PositiveInfinity;

#if NETSTANDARD2_0
var clamped = Clamp(proportion, minProp, maxProp);
#else
var clamped = Math.Clamp(proportion, minProp, maxProp);
#endif
return clamped;

#if NETSTANDARD2_0
static double Clamp(double value, double min, double max)
{
if (value < min) return min;
if (value > max) return max;
return value;
}
#endif
}

private static void AssignProportionsInternal(
Avalonia.Controls.Controls children,
Size size,
double splitterThickness,
Orientation orientation)
{
var dimension = orientation == Orientation.Horizontal ? size.Width : size.Height;
var dimensionWithoutSplitters = Math.Max(1.0, dimension - splitterThickness);

var assignedProportion = 0.0;
var unassignedProportions = 0;

for (var i = 0; i < children.Count; i++)
foreach (var control in children.OfType<Control>())
{
var control = children[i];
var isCollapsed = GetIsCollapsed(control);
var isSplitter = ProportionalStackPanelSplitter.IsSplitter(control, out _);

Expand All @@ -126,6 +161,8 @@ private static void AssignProportionsInternal(Avalonia.Controls.Controls childre
}
else
{
proportion = ClampProportion(control, orientation, dimensionWithoutSplitters, proportion);
SetProportion(control, proportion);
assignedProportion += proportion;
}
}
Expand All @@ -143,8 +180,9 @@ private static void AssignProportionsInternal(Avalonia.Controls.Controls childre
if (!ProportionalStackPanelSplitter.IsSplitter(control, out _))
{
var proportion = (1.0 - toAssign) / unassignedProportions;
proportion = ClampProportion(control, orientation, dimensionWithoutSplitters, proportion);
SetProportion(control, proportion);
assignedProportion += (1.0 - toAssign) / unassignedProportions;
assignedProportion += proportion;
}
}
}
Expand All @@ -156,9 +194,7 @@ private static void AssignProportionsInternal(Avalonia.Controls.Controls childre
var isCollapsed = GetIsCollapsed(c);
return !ProportionalStackPanelSplitter.IsSplitter(c, out _) && !isCollapsed;
});

var toAdd = (1.0 - assignedProportion) / numChildren;

foreach (var child in children.Where(c =>
{
var isCollapsed = GetIsCollapsed(c);
Expand All @@ -176,9 +212,7 @@ private static void AssignProportionsInternal(Avalonia.Controls.Controls childre
var isCollapsed = GetIsCollapsed(c);
return !ProportionalStackPanelSplitter.IsSplitter(c, out _) && !isCollapsed;
});

var toRemove = (assignedProportion - 1.0) / numChildren;

foreach (var child in children.Where(c =>
{
var isCollapsed = GetIsCollapsed(c);
Expand Down Expand Up @@ -249,7 +283,7 @@ protected override Size MeasureOverride(Size constraint)
var maximumHeight = 0.0;
var splitterThickness = GetTotalSplitterThickness(Children);

AssignProportions();
AssignProportions(constraint, splitterThickness);

var needsNextSplitter = false;
double sumOfFractions = 0;
Expand Down Expand Up @@ -372,7 +406,7 @@ protected override Size ArrangeOverride(Size arrangeSize)
var splitterThickness = GetTotalSplitterThickness(Children);
var index = 0;

AssignProportions();
AssignProportions(arrangeSize, splitterThickness);

var needsNextSplitter = false;
double sumOfFractions = 0;
Expand Down
57 changes: 0 additions & 57 deletions src/Dock.Model.Avalonia/Controls/Tool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,39 +19,13 @@ namespace Dock.Model.Avalonia.Controls;
[DataContract(IsReference = true)]
public class Tool : DockableBase, ITool, IDocument, IToolContent, ITemplate<Control?>, IRecyclingDataTemplate
{
private double _minWidth = double.NaN;
private double _maxWidth = double.NaN;
private double _minHeight = double.NaN;
private double _maxHeight = double.NaN;
/// <summary>
/// Defines the <see cref="Content"/> property.
/// </summary>
public static readonly StyledProperty<object?> ContentProperty =
AvaloniaProperty.Register<Tool, object?>(nameof(Content));

/// <summary>
/// Defines the <see cref="MinWidth"/> property.
/// </summary>
public static readonly DirectProperty<Tool, double> MinWidthProperty =
AvaloniaProperty.RegisterDirect<Tool, double>(nameof(MinWidth), o => o.MinWidth, (o, v) => o.MinWidth = v, double.NaN);

/// <summary>
/// Defines the <see cref="MaxWidth"/> property.
/// </summary>
public static readonly DirectProperty<Tool, double> MaxWidthProperty =
AvaloniaProperty.RegisterDirect<Tool, double>(nameof(MaxWidth), o => o.MaxWidth, (o, v) => o.MaxWidth = v, double.NaN);

/// <summary>
/// Defines the <see cref="MinHeight"/> property.
/// </summary>
public static readonly DirectProperty<Tool, double> MinHeightProperty =
AvaloniaProperty.RegisterDirect<Tool, double>(nameof(MinHeight), o => o.MinHeight, (o, v) => o.MinHeight = v, double.NaN);

/// <summary>
/// Defines the <see cref="MaxHeight"/> property.
/// </summary>
public static readonly DirectProperty<Tool, double> MaxHeightProperty =
AvaloniaProperty.RegisterDirect<Tool, double>(nameof(MaxHeight), o => o.MaxHeight, (o, v) => o.MaxHeight = v, double.NaN);

/// <summary>
/// Initializes new instance of the <see cref="Tool"/> class.
Expand Down Expand Up @@ -129,35 +103,4 @@ public bool Match(object? data)
return TemplateHelper.Build(Content, this);
}

/// <inheritdoc/>
[DataMember(IsRequired = false, EmitDefaultValue = true)]
public double MinWidth
{
get => _minWidth;
set => SetAndRaise(MinWidthProperty, ref _minWidth, value);
}

/// <inheritdoc/>
[DataMember(IsRequired = false, EmitDefaultValue = true)]
public double MaxWidth
{
get => _maxWidth;
set => SetAndRaise(MaxWidthProperty, ref _maxWidth, value);
}

/// <inheritdoc/>
[DataMember(IsRequired = false, EmitDefaultValue = true)]
public double MinHeight
{
get => _minHeight;
set => SetAndRaise(MinHeightProperty, ref _minHeight, value);
}

/// <inheritdoc/>
[DataMember(IsRequired = false, EmitDefaultValue = true)]
public double MaxHeight
{
get => _maxHeight;
set => SetAndRaise(MaxHeightProperty, ref _maxHeight, value);
}
}
64 changes: 64 additions & 0 deletions src/Dock.Model.Avalonia/Core/DockableBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,30 @@ public abstract class DockableBase : ReactiveBase, IDockable
public static readonly DirectProperty<DockableBase, bool> CanDropProperty =
AvaloniaProperty.RegisterDirect<DockableBase, bool>(nameof(CanDrop), o => o.CanDrop, (o, v) => o.CanDrop = v);

/// <summary>
/// Defines the <see cref="MinWidth"/> property.
/// </summary>
public static readonly DirectProperty<DockableBase, double> MinWidthProperty =
AvaloniaProperty.RegisterDirect<DockableBase, double>(nameof(MinWidth), o => o.MinWidth, (o, v) => o.MinWidth = v, double.NaN);

/// <summary>
/// Defines the <see cref="MaxWidth"/> property.
/// </summary>
public static readonly DirectProperty<DockableBase, double> MaxWidthProperty =
AvaloniaProperty.RegisterDirect<DockableBase, double>(nameof(MaxWidth), o => o.MaxWidth, (o, v) => o.MaxWidth = v, double.NaN);

/// <summary>
/// Defines the <see cref="MinHeight"/> property.
/// </summary>
public static readonly DirectProperty<DockableBase, double> MinHeightProperty =
AvaloniaProperty.RegisterDirect<DockableBase, double>(nameof(MinHeight), o => o.MinHeight, (o, v) => o.MinHeight = v, double.NaN);

/// <summary>
/// Defines the <see cref="MaxHeight"/> property.
/// </summary>
public static readonly DirectProperty<DockableBase, double> MaxHeightProperty =
AvaloniaProperty.RegisterDirect<DockableBase, double>(nameof(MaxHeight), o => o.MaxHeight, (o, v) => o.MaxHeight = v, double.NaN);

private readonly TrackingAdapter _trackingAdapter;
private string _id = string.Empty;
private string _title = string.Empty;
Expand All @@ -125,6 +149,10 @@ public abstract class DockableBase : ReactiveBase, IDockable
private bool _canFloat = true;
private bool _canDrag = true;
private bool _canDrop = true;
private double _minWidth = double.NaN;
private double _maxWidth = double.NaN;
private double _minHeight = double.NaN;
private double _maxHeight = double.NaN;

/// <summary>
/// Initializes new instance of the <see cref="DockableBase"/> class.
Expand Down Expand Up @@ -217,6 +245,42 @@ public double Proportion
set => SetAndRaise(ProportionProperty, ref _proportion, value);
}

/// <inheritdoc/>
[DataMember(IsRequired = false, EmitDefaultValue = true)]
[JsonPropertyName("MinWidth")]
public double MinWidth
{
get => _minWidth;
set => SetAndRaise(MinWidthProperty, ref _minWidth, value);
}

/// <inheritdoc/>
[DataMember(IsRequired = false, EmitDefaultValue = true)]
[JsonPropertyName("MaxWidth")]
public double MaxWidth
{
get => _maxWidth;
set => SetAndRaise(MaxWidthProperty, ref _maxWidth, value);
}

/// <inheritdoc/>
[DataMember(IsRequired = false, EmitDefaultValue = true)]
[JsonPropertyName("MinHeight")]
public double MinHeight
{
get => _minHeight;
set => SetAndRaise(MinHeightProperty, ref _minHeight, value);
}

/// <inheritdoc/>
[DataMember(IsRequired = false, EmitDefaultValue = true)]
[JsonPropertyName("MaxHeight")]
public double MaxHeight
{
get => _maxHeight;
set => SetAndRaise(MaxHeightProperty, ref _maxHeight, value);
}

/// <inheritdoc/>
[DataMember(IsRequired = false, EmitDefaultValue = true)]
[JsonPropertyName("CanClose")]
Expand Down
Loading
Loading