Добавлен проект UI
This commit is contained in:
71
Lattice.UI/Primitives/DockAnchorOverlay.cs
Normal file
71
Lattice.UI/Primitives/DockAnchorOverlay.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Shapes;
|
||||
|
||||
namespace Lattice.UI.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Визуальный оверлей, отображающий зоны приземления (Drop Zones) и якоря докинга.
|
||||
/// </summary>
|
||||
[TemplatePart(Name = "OverlayCanvas", Type = typeof(Canvas))]
|
||||
[TemplatePart(Name = "DropPreview", Type = typeof(Rectangle))]
|
||||
[TemplatePart(Name = "AnchorGroup", Type = typeof(Grid))]
|
||||
public class DockAnchorOverlay : Control
|
||||
{
|
||||
private Canvas? _overlayCanvas;
|
||||
private Rectangle? _dropPreview;
|
||||
private Grid? _anchorGroup;
|
||||
|
||||
public DockAnchorOverlay()
|
||||
{
|
||||
// Привязываем стиль из Generic.xaml
|
||||
this.DefaultStyleKey = typeof(DockAnchorOverlay);
|
||||
|
||||
// По умолчанию скрыт, показывается только во время Drag-and-Drop
|
||||
this.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
_overlayCanvas = GetTemplateChild("OverlayCanvas") as Canvas;
|
||||
_dropPreview = GetTemplateChild("DropPreview") as Rectangle;
|
||||
_anchorGroup = GetTemplateChild("AnchorGroup") as Grid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Отображает превью будущей зоны закрепления.
|
||||
/// </summary>
|
||||
/// <param name="rect">Координаты и размер зоны.</param>
|
||||
public void ShowPreview(Windows.Foundation.Rect rect)
|
||||
{
|
||||
if (_dropPreview == null) return;
|
||||
|
||||
_dropPreview.Visibility = Visibility.Visible;
|
||||
Canvas.SetLeft(_dropPreview, rect.X);
|
||||
Canvas.SetTop(_dropPreview, rect.Y);
|
||||
_dropPreview.Width = rect.Width;
|
||||
_dropPreview.Height = rect.Height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Скрывает превью зоны.
|
||||
/// </summary>
|
||||
public void HidePreview()
|
||||
{
|
||||
if (_dropPreview != null)
|
||||
_dropPreview.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Центрирует группу якорей (ромб) относительно указанной точки.
|
||||
/// </summary>
|
||||
public void PositionAnchors(Windows.Foundation.Point centerPoint)
|
||||
{
|
||||
if (_anchorGroup == null) return;
|
||||
|
||||
Canvas.SetLeft(_anchorGroup, centerPoint.X - (_anchorGroup.Width / 2));
|
||||
Canvas.SetTop(_anchorGroup, centerPoint.Y - (_anchorGroup.Height / 2));
|
||||
}
|
||||
}
|
||||
16
Lattice.UI/Primitives/LatticeIcon.cs
Normal file
16
Lattice.UI/Primitives/LatticeIcon.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
|
||||
namespace Lattice.UI.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Утилита для быстрого получения иконок в стиле Fluent UI 2.
|
||||
/// </summary>
|
||||
public static class LatticeIcon
|
||||
{
|
||||
public static FontIcon GetIcon(string glyph) => new FontIcon
|
||||
{
|
||||
Glyph = glyph,
|
||||
FontFamily = new FontFamily("Segoe Fluent Icons")
|
||||
};
|
||||
}
|
||||
121
Lattice.UI/Primitives/LayoutPanel.cs
Normal file
121
Lattice.UI/Primitives/LayoutPanel.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using Lattice.Core.Models;
|
||||
using Lattice.Core.Models.Enums;
|
||||
using Lattice.UI.Controls;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Lattice.UI.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Кастомный контейнер, преобразующий иерархию узлов Lattice в визуальные элементы WinUI 3.
|
||||
/// </summary>
|
||||
public class LayoutPanel : Grid
|
||||
{
|
||||
private readonly LatticeDockHost _host;
|
||||
|
||||
/// <summary>
|
||||
/// Создает новый экземпляр панели компоновки.
|
||||
/// </summary>
|
||||
/// <param name="host">Корневой хост, управляющий макетом.</param>
|
||||
public LayoutPanel(LatticeDockHost host)
|
||||
{
|
||||
_host = host;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет рекурсивную отрисовку дерева узлов.
|
||||
/// </summary>
|
||||
public void Build(LayoutNode node)
|
||||
{
|
||||
this.Children.Clear();
|
||||
this.ColumnDefinitions.Clear();
|
||||
this.RowDefinitions.Clear();
|
||||
|
||||
if (node is SplitContainerNode splitContainer)
|
||||
{
|
||||
RenderSplit(splitContainer);
|
||||
}
|
||||
else if (node is ContentNode contentNode)
|
||||
{
|
||||
RenderContent(contentNode);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderSplit(SplitContainerNode container)
|
||||
{
|
||||
for (int i = 0; i < container.Children.Count; i++)
|
||||
{
|
||||
var child = container.Children[i];
|
||||
var childPresenter = new LayoutPanel(_host);
|
||||
|
||||
if (container.Orientation == SplitOrientation.Horizontal)
|
||||
{
|
||||
this.ColumnDefinitions.Add(new ColumnDefinition
|
||||
{
|
||||
Width = child.IsWidthStar ? new GridLength(child.WidthValue, GridUnitType.Star) : new GridLength(child.WidthValue)
|
||||
});
|
||||
Grid.SetColumn(childPresenter, this.ColumnDefinitions.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.RowDefinitions.Add(new RowDefinition
|
||||
{
|
||||
Height = child.IsHeightStar ? new GridLength(child.HeightValue, GridUnitType.Star) : new GridLength(child.HeightValue)
|
||||
});
|
||||
Grid.SetRow(childPresenter, this.RowDefinitions.Count - 1);
|
||||
}
|
||||
|
||||
this.Children.Add(childPresenter);
|
||||
childPresenter.Build(child);
|
||||
|
||||
// Добавляем сплиттер между элементами (кроме последнего)
|
||||
if (i < container.Children.Count - 1)
|
||||
{
|
||||
AddSplitter(container, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSplitter(SplitContainerNode container, int index)
|
||||
{
|
||||
var splitter = new LatticeSplitter
|
||||
{
|
||||
LeftNode = container.Children[index],
|
||||
RightNode = container.Children[index + 1],
|
||||
Orientation = container.Orientation,
|
||||
};
|
||||
|
||||
double thickness = (double)Application.Current.Resources["LatticeSplitterThickness"];
|
||||
|
||||
if (container.Orientation == SplitOrientation.Horizontal)
|
||||
{
|
||||
// Сплиттер занимает очень узкую колонку между основными
|
||||
this.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(thickness) });
|
||||
Grid.SetColumn(splitter, this.ColumnDefinitions.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.RowDefinitions.Add(new RowDefinition { Height = new GridLength(thickness) });
|
||||
Grid.SetRow(splitter, this.RowDefinitions.Count - 1);
|
||||
}
|
||||
|
||||
this.Children.Add(splitter);
|
||||
}
|
||||
|
||||
private void RenderContent(ContentNode node)
|
||||
{
|
||||
var pane = new LatticePane
|
||||
{
|
||||
Title = node.Name,
|
||||
Content = node.Component,
|
||||
DataContext = node,
|
||||
};
|
||||
|
||||
pane.CloseClick += (s, e) =>
|
||||
{
|
||||
_host.Manager?.Remove(node);
|
||||
};
|
||||
|
||||
this.Children.Add(pane);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user