From f59199f44f515dcdd91eb5d932bb948591063dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Korczy=C5=84ski?= Date: Sun, 10 Mar 2024 21:55:07 +0000 Subject: [PATCH 1/4] Fix host windows dragging on Windows --- src/Dock.Avalonia/Controls/HostWindow.axaml.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Dock.Avalonia/Controls/HostWindow.axaml.cs b/src/Dock.Avalonia/Controls/HostWindow.axaml.cs index 3def25e9c..539a1143e 100644 --- a/src/Dock.Avalonia/Controls/HostWindow.axaml.cs +++ b/src/Dock.Avalonia/Controls/HostWindow.axaml.cs @@ -108,11 +108,6 @@ private void MoveDrag(PointerPressedEventArgs e) PseudoClasses.Set(":dragging", true); _draggingWindow = true; BeginMoveDrag(e); - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - EndDrag(e); - } } private void EndDrag(PointerEventArgs e) @@ -156,9 +151,7 @@ private void HostWindow_PositionChanged(object? sender, PixelPointEventArgs e) { Window.Save(); - if ((_chromeGrip is { } && _chromeGrip.IsPointerOver) - || (_hostWindowTitleBar?.BackgroundControl is { } && (_hostWindowTitleBar?.BackgroundControl?.IsPointerOver ?? false)) - && _mouseDown) + if (_mouseDown) { Window.Factory?.OnWindowMoveDrag(Window); _hostWindowState.Process(Position, EventType.Moved); From 819a1f68fb08be33543555d84318498e2df53e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Korczy=C5=84ski?= Date: Sun, 10 Mar 2024 21:29:52 +0000 Subject: [PATCH 2/4] Correctly set host window's parent --- src/Dock.Avalonia/Controls/HostWindow.axaml | 4 +++- src/Dock.Avalonia/Controls/HostWindow.axaml.cs | 18 +++++++++++------- src/Dock.Model/Adapters/HostAdapter.cs | 1 - src/Dock.Model/Core/IHostWindow.cs | 6 ------ src/Dock.Model/FactoryBase.cs | 9 --------- 5 files changed, 14 insertions(+), 24 deletions(-) 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 539a1143e..a1ebff1df 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; @@ -277,7 +279,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(); + } } } } @@ -335,12 +345,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.Model/Adapters/HostAdapter.cs b/src/Dock.Model/Adapters/HostAdapter.cs index 7cdee8287..3baf45227 100644 --- a/src/Dock.Model/Adapters/HostAdapter.cs +++ b/src/Dock.Model/Adapters/HostAdapter.cs @@ -56,7 +56,6 @@ public void Present(bool isDialog) _window.Host.Present(isDialog); _window.Host.SetPosition(_window.X, _window.Y); _window.Host.SetSize(_window.Width, _window.Height); - _window.Host.SetTopmost(_window.Topmost); _window.Host.SetTitle(_window.Title); _window.Host.SetLayout(_window.Layout); _window.Host.IsTracked = true; 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/FactoryBase.cs b/src/Dock.Model/FactoryBase.cs index 15fc26aa0..6e71f309f 100644 --- a/src/Dock.Model/FactoryBase.cs +++ b/src/Dock.Model/FactoryBase.cs @@ -263,7 +263,6 @@ public virtual void SplitToDock(IDock dock, IDockable dockable, DockOperation op public virtual IDockWindow? CreateWindowFrom(IDockable dockable) { IDockable? target; - bool topmost; switch (dockable) { @@ -282,7 +281,6 @@ public virtual void SplitToDock(IDock dock, IDockable dockable, DockOperation op dock.ActiveDockable = dockable; } } - topmost = true; break; } case IDocument: @@ -315,37 +313,31 @@ public virtual void SplitToDock(IDock dock, IDockable dockable, DockOperation op 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: @@ -372,7 +364,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; From 67730ddd0b1e4a0ce168480884e893205dd47693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Korczy=C5=84ski?= Date: Fri, 8 Mar 2024 02:13:30 +0000 Subject: [PATCH 3/4] Corrected the logic for updating IsEmpty --- src/Dock.Model/FactoryBase.Dockable.cs | 107 ++++++++++++++++++------- src/Dock.Model/FactoryBase.Init.cs | 2 +- src/Dock.Model/FactoryBase.cs | 33 +++----- 3 files changed, 92 insertions(+), 50 deletions(-) diff --git a/src/Dock.Model/FactoryBase.Dockable.cs b/src/Dock.Model/FactoryBase.Dockable.cs index e002a05d0..6b4629972 100644 --- a/src/Dock.Model/FactoryBase.Dockable.cs +++ b/src/Dock.Model/FactoryBase.Dockable.cs @@ -1,4 +1,5 @@ -using Dock.Model.Controls; +using System.Linq; +using Dock.Model.Controls; using Dock.Model.Core; namespace Dock.Model; @@ -13,8 +14,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 +25,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); } } @@ -45,8 +44,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 +100,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; @@ -177,11 +173,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 +184,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 +195,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 +221,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); @@ -363,8 +352,7 @@ public virtual void PinDockable(IDockable dockable) if (toolDock.VisibleDockables is not null) { - toolDock.VisibleDockables.Remove(dockable); - toolDock.IsEmpty = toolDock.VisibleDockables.Count == 0; + RemoveVisibleDockable(toolDock, dockable); OnDockableRemoved(dockable); } @@ -468,8 +456,7 @@ public virtual void PinDockable(IDockable dockable) } } - toolDock.VisibleDockables.Add(dockable); - toolDock.IsEmpty = toolDock.VisibleDockables.Count == 0; + AddVisibleDockable(toolDock, dockable); OnDockableAdded(dockable); // TODO: Handle ActiveDockable state. @@ -610,4 +597,70 @@ public virtual void CloseRightDockables(IDockable dockable) 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 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.cs b/src/Dock.Model/FactoryBase.cs index 6e71f309f..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; @@ -275,8 +267,7 @@ 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; } @@ -307,8 +298,7 @@ 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; } @@ -351,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; From 1d9de4a9141ec89040468185baeea32183de1ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Korczy=C5=84ski?= Date: Mon, 11 Mar 2024 03:49:49 +0000 Subject: [PATCH 4/4] Improve Host Windows management --- .../DockMvvmSample/ViewModels/DockFactory.cs | 11 +++ src/Dock.Avalonia/Controls/HostWindow.axaml | 12 ++- .../Controls/HostWindow.axaml.cs | 81 +++++++++++++++++-- .../Controls/ToolChromeControl.axaml | 2 +- .../Controls/ToolChromeControl.axaml.cs | 33 ++++++-- .../Converters/IntLessThanConverter.cs | 24 ++++++ .../Internal/DockControlState.cs | 8 +- src/Dock.Avalonia/Internal/Extensions.cs | 32 ++++++++ src/Dock.Avalonia/Internal/HostWindowState.cs | 15 +--- src/Dock.Model.Avalonia/Core/DockBase.cs | 16 ++++ src/Dock.Model.Mvvm/Core/DockBase.cs | 9 +++ src/Dock.Model.ReactiveUI/Core/DockBase.cs | 9 +++ src/Dock.Model/Core/IDock.cs | 5 ++ src/Dock.Model/DockManager.cs | 20 +++++ src/Dock.Model/FactoryBase.Dockable.cs | 23 ++++++ 15 files changed, 264 insertions(+), 36 deletions(-) create mode 100644 src/Dock.Avalonia/Converters/IntLessThanConverter.cs create mode 100644 src/Dock.Avalonia/Internal/Extensions.cs diff --git a/samples/DockMvvmSample/ViewModels/DockFactory.cs b/samples/DockMvvmSample/ViewModels/DockFactory.cs index 9098cce70..b04e16bfb 100644 --- a/samples/DockMvvmSample/ViewModels/DockFactory.cs +++ b/samples/DockMvvmSample/ViewModels/DockFactory.cs @@ -137,6 +137,17 @@ public override IRootDock CreateLayout() return rootDock; } + public override IDockWindow? CreateWindowFrom(IDockable dockable) + { + var window = base.CreateWindowFrom(dockable); + + if (window != null) + { + window.Title = "Dock Avalonia Demo"; + } + return window; + } + public override void InitLayout(IDockable layout) { ContextLocator = new Dictionary> diff --git a/src/Dock.Avalonia/Controls/HostWindow.axaml b/src/Dock.Avalonia/Controls/HostWindow.axaml index d64af0d17..cab9c7213 100644 --- a/src/Dock.Avalonia/Controls/HostWindow.axaml +++ b/src/Dock.Avalonia/Controls/HostWindow.axaml @@ -6,6 +6,8 @@ + + @@ -17,7 +19,8 @@ - + + @@ -56,10 +59,13 @@ + diff --git a/src/Dock.Avalonia/Controls/HostWindow.axaml.cs b/src/Dock.Avalonia/Controls/HostWindow.axaml.cs index a1ebff1df..f929318f7 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.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using Avalonia; @@ -6,10 +7,12 @@ using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Input; +using Avalonia.Interactivity; using Avalonia.Styling; using Avalonia.VisualTree; using Dock.Avalonia.Internal; using Dock.Model; +using Dock.Model.Controls; using Dock.Model.Core; namespace Dock.Avalonia.Controls; @@ -22,7 +25,7 @@ public class HostWindow : Window, IHostWindow { private readonly DockManager _dockManager; private readonly HostWindowState _hostWindowState; - private Control? _chromeGrip; + private List _chromeGrips = new(); private HostWindowTitleBar? _hostWindowTitleBar; private bool _mouseDown, _draggingWindow; @@ -32,6 +35,12 @@ public class HostWindow : Window, IHostWindow public static readonly StyledProperty IsToolWindowProperty = AvaloniaProperty.Register(nameof(IsToolWindow)); + /// + /// Define property. + /// + public static readonly StyledProperty ToolChromeControlsWholeWindowProperty = + AvaloniaProperty.Register(nameof(ToolChromeControlsWholeWindow)); + /// protected override Type StyleKeyOverride => typeof(HostWindow); @@ -44,6 +53,15 @@ public bool IsToolWindow set => SetValue(IsToolWindowProperty, value); } + /// + /// Gets or sets if the tool chrome controls the whole window. + /// + public bool ToolChromeControlsWholeWindow + { + get => GetValue(ToolChromeControlsWholeWindowProperty); + set => SetValue(ToolChromeControlsWholeWindowProperty, value); + } + /// public IDockManager DockManager => _dockManager; @@ -66,7 +84,7 @@ public HostWindow() _dockManager = new DockManager(); _hostWindowState = new HostWindowState(_dockManager, this); - UpdatePseudoClasses(IsToolWindow); + UpdatePseudoClasses(IsToolWindow, ToolChromeControlsWholeWindow); } /// @@ -99,6 +117,9 @@ private PixelPoint ClientPointToScreenRelativeToWindow(Point clientPoint) private void MoveDrag(PointerPressedEventArgs e) { + if (!ToolChromeControlsWholeWindow) + return; + if (Window?.Factory?.OnWindowMoveDragBegin(Window) != true) { return; @@ -127,7 +148,7 @@ protected override void OnPointerPressed(PointerPressedEventArgs e) { base.OnPointerPressed(e); - if (_chromeGrip is { } && _chromeGrip.IsPointerOver) + if (_chromeGrips.Any(grip => grip.IsPointerOver)) { if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) { @@ -177,14 +198,35 @@ public void AttachGrip(ToolChromeControl chromeControl) { if (chromeControl.CloseButton is not null) { - chromeControl.CloseButton.Click += (_, _) => Exit(); + chromeControl.CloseButton.Click += ChromeCloseClick; + } + + if (chromeControl.Grip is { } grip) + { + _chromeGrips.Add(grip); } - _chromeGrip = chromeControl.Grip; ((IPseudoClasses)chromeControl.Classes).Add(":floating"); IsToolWindow = true; } + /// + /// Detaches grip to chrome. + /// + /// The chrome control. + public void DetachGrip(ToolChromeControl chromeControl) + { + if (chromeControl.Grip is { } grip) + { + _chromeGrips.Remove(grip); + } + + if (chromeControl.CloseButton is not null) + { + chromeControl.CloseButton.Click -= ChromeCloseClick; + } + } + /// protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { @@ -192,13 +234,36 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (change.Property == IsToolWindowProperty) { - UpdatePseudoClasses(change.GetNewValue()); + UpdatePseudoClasses(change.GetNewValue(), ToolChromeControlsWholeWindow); + } + else if (change.Property == ToolChromeControlsWholeWindowProperty) + { + UpdatePseudoClasses(IsToolWindow, change.GetNewValue()); } } - private void UpdatePseudoClasses(bool isToolWindow) + private void UpdatePseudoClasses(bool isToolWindow, bool toolChromeControlsWholeWindow) { PseudoClasses.Set(":toolwindow", isToolWindow); + PseudoClasses.Set(":toolchromecontrolswindow", toolChromeControlsWholeWindow); + } + + private int CountVisibleToolsAndDocuments(IDockable? dockable) + { + switch (dockable) + { + case ITool: return 1; + case IDocument: return 1; + case IDock dock: + return dock.VisibleDockables?.Sum(CountVisibleToolsAndDocuments) ?? 0; + default: return 0; + } + } + + private void ChromeCloseClick(object? sender, RoutedEventArgs e) + { + if (CountVisibleToolsAndDocuments(DataContext as IRootDock) <= 1) + Exit(); } /// @@ -282,6 +347,8 @@ public void Present(bool isDialog) var ownerDockControl = Window?.Layout?.Factory?.DockControls.FirstOrDefault(); if (ownerDockControl is Control control && control.GetVisualRoot() is Window parentWindow) { + Title = parentWindow.Title; + Icon = parentWindow.Icon; Show(parentWindow); } else diff --git a/src/Dock.Avalonia/Controls/ToolChromeControl.axaml b/src/Dock.Avalonia/Controls/ToolChromeControl.axaml index c65511a89..ddd829f0b 100644 --- a/src/Dock.Avalonia/Controls/ToolChromeControl.axaml +++ b/src/Dock.Avalonia/Controls/ToolChromeControl.axaml @@ -135,7 +135,7 @@