fix(dock): address reviewer feedback on tree renderer
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||||
<ResourceDictionary Source="/Themes/Generic.xaml" />
|
<ResourceDictionary Source="/Themes/Generic.xaml" />
|
||||||
|
<ResourceDictionary Source="/View/Controls/DockLayout.xaml" />
|
||||||
<core:ControlsDictionary />
|
<core:ControlsDictionary />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using Ghost.Editor.Core.Controls.Internal.Docking;
|
using Ghost.Editor.Core.Controls.Internal.Docking;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
|
||||||
namespace Ghost.Editor.View.Controls;
|
namespace Ghost.Editor.View.Controls;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A control that renders a docking layout tree.
|
||||||
|
/// </summary>
|
||||||
|
[TemplatePart(Name = PART_ROOT_GRID, Type = typeof(Grid))]
|
||||||
public sealed partial class DockLayout : Control
|
public sealed partial class DockLayout : Control
|
||||||
{
|
{
|
||||||
|
private const string PART_ROOT_GRID = "PART_RootGrid";
|
||||||
|
|
||||||
public DockLayout()
|
public DockLayout()
|
||||||
{
|
{
|
||||||
DefaultStyleKey = typeof(DockLayout);
|
DefaultStyleKey = typeof(DockLayout);
|
||||||
@@ -24,13 +35,79 @@ public sealed partial class DockLayout : Control
|
|||||||
{
|
{
|
||||||
if (d is DockLayout layout)
|
if (d is DockLayout layout)
|
||||||
{
|
{
|
||||||
|
if (e.OldValue is DockGroupNode oldRoot)
|
||||||
|
{
|
||||||
|
layout.UnsubscribeFromNode(oldRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.NewValue is DockGroupNode newRoot)
|
||||||
|
{
|
||||||
|
layout.SubscribeToNode(newRoot);
|
||||||
|
}
|
||||||
|
|
||||||
layout.RenderTree();
|
layout.RenderTree();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SubscribeToNode(DockNode node)
|
||||||
|
{
|
||||||
|
node.PropertyChanged += OnNodePropertyChanged;
|
||||||
|
|
||||||
|
if (node is DockGroupNode groupNode)
|
||||||
|
{
|
||||||
|
((INotifyCollectionChanged)groupNode.Children).CollectionChanged += OnChildrenCollectionChanged;
|
||||||
|
foreach (var child in groupNode.Children)
|
||||||
|
{
|
||||||
|
SubscribeToNode(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnsubscribeFromNode(DockNode node)
|
||||||
|
{
|
||||||
|
node.PropertyChanged -= OnNodePropertyChanged;
|
||||||
|
|
||||||
|
if (node is DockGroupNode groupNode)
|
||||||
|
{
|
||||||
|
((INotifyCollectionChanged)groupNode.Children).CollectionChanged -= OnChildrenCollectionChanged;
|
||||||
|
foreach (var child in groupNode.Children)
|
||||||
|
{
|
||||||
|
UnsubscribeFromNode(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNodePropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
// Re-render on relevant property changes (e.g. Orientation)
|
||||||
|
RenderTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
// Handle subscriptions for new/removed children
|
||||||
|
if (e.OldItems != null)
|
||||||
|
{
|
||||||
|
foreach (DockNode oldNode in e.OldItems)
|
||||||
|
{
|
||||||
|
UnsubscribeFromNode(oldNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.NewItems != null)
|
||||||
|
{
|
||||||
|
foreach (DockNode newNode in e.NewItems)
|
||||||
|
{
|
||||||
|
SubscribeToNode(newNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderTree();
|
||||||
|
}
|
||||||
|
|
||||||
private void RenderTree()
|
private void RenderTree()
|
||||||
{
|
{
|
||||||
if (GetTemplateChild("PART_RootGrid") is Grid rootGrid)
|
if (GetTemplateChild(PART_ROOT_GRID) is Grid rootGrid)
|
||||||
{
|
{
|
||||||
rootGrid.Children.Clear();
|
rootGrid.Children.Clear();
|
||||||
if (Root != null)
|
if (Root != null)
|
||||||
@@ -55,16 +132,32 @@ public sealed partial class DockLayout : Control
|
|||||||
}
|
}
|
||||||
else if (node is DockPanelNode panelNode)
|
else if (node is DockPanelNode panelNode)
|
||||||
{
|
{
|
||||||
// NOTE: NavigationTabView is expected to be implemented in a future task or exists in a namespace not yet fully visible.
|
var tabView = new Ghost.Editor.Controls.NavigationTabView
|
||||||
// For now, we use a placeholder if it's not found, but the task specifies using it.
|
|
||||||
// If it fails to compile, I will check for the correct namespace.
|
|
||||||
return new Ghost.Editor.Controls.NavigationTabView
|
|
||||||
{
|
{
|
||||||
TabItemsSource = panelNode.Items,
|
TabItemsSource = panelNode.Items,
|
||||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||||
VerticalAlignment = VerticalAlignment.Stretch
|
VerticalAlignment = VerticalAlignment.Stretch
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Bind selection state
|
||||||
|
tabView.SetBinding(Selector.SelectedIndexProperty, new Binding
|
||||||
|
{
|
||||||
|
Source = panelNode,
|
||||||
|
Path = new PropertyPath(nameof(DockPanelNode.SelectedIndex)),
|
||||||
|
Mode = BindingMode.TwoWay
|
||||||
|
});
|
||||||
|
|
||||||
|
tabView.SetBinding(Selector.SelectedItemProperty, new Binding
|
||||||
|
{
|
||||||
|
Source = panelNode,
|
||||||
|
Path = new PropertyPath(nameof(DockPanelNode.SelectedItem)),
|
||||||
|
Mode = BindingMode.TwoWay
|
||||||
|
});
|
||||||
|
|
||||||
|
return tabView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug.Fail($"Unsupported node type: {node.GetType().Name}");
|
||||||
return new Grid(); // Fallback
|
return new Grid(); // Fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user