DragAndDrop core
This commit is contained in:
317
Lattice.Serialization.Docking/LayoutConverter.cs
Normal file
317
Lattice.Serialization.Docking/LayoutConverter.cs
Normal file
@@ -0,0 +1,317 @@
|
||||
using Lattice.Core.Docking.Abstractions;
|
||||
using Lattice.Core.Docking.Engine;
|
||||
using Lattice.Core.Docking.Models;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// Предоставляет методы для преобразования между объектной моделью док-системы
|
||||
/// и Data Transfer Objects (DTO) для сериализации.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Этот класс является центральным местом для преобразования между различными представлениями
|
||||
/// данных макета. Он обеспечивает независимость формата сериализации от внутренней структуры
|
||||
/// данных и позволяет легко добавлять новые форматы без изменения логики преобразования.
|
||||
/// </remarks>
|
||||
public static class LayoutConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Преобразует менеджер макета в DTO для сериализации.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для преобразования.</param>
|
||||
/// <returns>
|
||||
/// DTO, содержащий все данные, необходимые для восстановления состояния макета.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">Выбрасывается, если <paramref name="manager"/> равен null.</exception>
|
||||
public static LayoutDto ConvertToDto(LayoutManager manager)
|
||||
{
|
||||
if (manager == null)
|
||||
throw new ArgumentNullException(nameof(manager));
|
||||
|
||||
var dto = new LayoutDto
|
||||
{
|
||||
ApplicationId = GetApplicationIdentifier()
|
||||
};
|
||||
|
||||
if (manager.Root != null)
|
||||
{
|
||||
dto.Root = ConvertElementToDto(manager.Root);
|
||||
}
|
||||
|
||||
dto.FloatingWindows = manager.FloatingWindows
|
||||
.Select(ConvertWindowToDto)
|
||||
.ToList();
|
||||
|
||||
dto.AutoHidePanels = manager.AutoHidePanels
|
||||
.Select(ConvertAutoHidePanelToDto)
|
||||
.ToList();
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Восстанавливает состояние менеджера макета из DTO.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для восстановления состояния.</param>
|
||||
/// <param name="dto">DTO, содержащий данные состояния макета.</param>
|
||||
/// <param name="contentResolver">
|
||||
/// Функция разрешения контента по идентификатору.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="manager"/> или <paramref name="dto"/> равен null.
|
||||
/// </exception>
|
||||
public static void RestoreFromDto(LayoutManager manager, LayoutDto dto,
|
||||
Func<string, IDockContent?> contentResolver)
|
||||
{
|
||||
if (manager == null)
|
||||
throw new ArgumentNullException(nameof(manager));
|
||||
if (dto == null)
|
||||
throw new ArgumentNullException(nameof(dto));
|
||||
|
||||
// Сбрасываем текущее состояние
|
||||
manager.Reset();
|
||||
|
||||
// Восстанавливаем корневой элемент
|
||||
if (dto.Root != null)
|
||||
{
|
||||
manager.Root = ConvertDtoToElement(dto.Root, contentResolver);
|
||||
}
|
||||
|
||||
// Восстанавливаем плавающие окна
|
||||
foreach (var windowDto in dto.FloatingWindows)
|
||||
{
|
||||
var window = new DockWindow
|
||||
{
|
||||
X = windowDto.X,
|
||||
Y = windowDto.Y,
|
||||
Width = windowDto.Width,
|
||||
Height = windowDto.Height,
|
||||
Title = windowDto.Title
|
||||
};
|
||||
|
||||
if (windowDto.Root != null)
|
||||
{
|
||||
window.Root = ConvertDtoToElement(windowDto.Root, contentResolver);
|
||||
}
|
||||
|
||||
manager.FloatingWindows.Add(window);
|
||||
}
|
||||
|
||||
// Восстанавливаем автоскрываемые панели
|
||||
foreach (var panelDto in dto.AutoHidePanels)
|
||||
{
|
||||
var content = contentResolver(panelDto.Content.Id);
|
||||
if (content != null)
|
||||
{
|
||||
if (Enum.TryParse<DockSide>(panelDto.Side, out var side))
|
||||
{
|
||||
var panel = manager.AddAutoHidePanel(content, side);
|
||||
panel.Size = panelDto.Size;
|
||||
panel.IsVisible = panelDto.IsVisible;
|
||||
panel.SlideOffset = panelDto.SlideOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует элемент дерева компоновки в DTO.
|
||||
/// </summary>
|
||||
private static ElementDto ConvertElementToDto(IDockElement element)
|
||||
{
|
||||
if (element is DockGroup group)
|
||||
{
|
||||
return new GroupDto
|
||||
{
|
||||
Id = group.Id,
|
||||
Type = "group",
|
||||
Width = group.Width,
|
||||
Height = group.Height,
|
||||
MinWidth = group.MinWidth,
|
||||
MinHeight = group.MinHeight,
|
||||
First = ConvertElementToDto(group.First),
|
||||
Second = ConvertElementToDto(group.Second),
|
||||
Orientation = group.Orientation.ToString(),
|
||||
SplitRatio = group.SplitRatio
|
||||
};
|
||||
}
|
||||
else if (element is DockLeaf leaf)
|
||||
{
|
||||
return new LeafDto
|
||||
{
|
||||
Id = leaf.Id,
|
||||
Type = "leaf",
|
||||
Width = leaf.Width,
|
||||
Height = leaf.Height,
|
||||
MinWidth = leaf.MinWidth,
|
||||
MinHeight = leaf.MinHeight,
|
||||
Contents = leaf.Children.Select(ConvertContentToDto).ToList(),
|
||||
ActiveContentId = leaf.ActiveContent?.Id,
|
||||
TabPlacement = leaf.TabPlacement.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"Element type {element.GetType().Name} is not supported for serialization");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует контент в DTO ссылки.
|
||||
/// </summary>
|
||||
private static ContentReferenceDto ConvertContentToDto(IDockContent content)
|
||||
{
|
||||
var dto = new ContentReferenceDto
|
||||
{
|
||||
Id = content.Id,
|
||||
TypeId = GetContentTypeId(content),
|
||||
Title = content.Title,
|
||||
CanClose = content.CanClose
|
||||
};
|
||||
|
||||
// Сохраняем дополнительные свойства, если контент поддерживает сериализацию состояния
|
||||
if (content is ISerializableContent serializable)
|
||||
{
|
||||
dto.Properties = serializable.GetSerializableState();
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует окно в DTO.
|
||||
/// </summary>
|
||||
private static WindowDto ConvertWindowToDto(DockWindow window)
|
||||
{
|
||||
return new WindowDto
|
||||
{
|
||||
Id = window.Id,
|
||||
X = window.X,
|
||||
Y = window.Y,
|
||||
Width = window.Width,
|
||||
Height = window.Height,
|
||||
Title = window.Title,
|
||||
Root = window.Root != null ? ConvertElementToDto(window.Root) : null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует автоскрываемую панель в DTO.
|
||||
/// </summary>
|
||||
private static AutoHidePanelDto ConvertAutoHidePanelToDto(AutoHidePanel panel)
|
||||
{
|
||||
return new AutoHidePanelDto
|
||||
{
|
||||
Id = panel.Id,
|
||||
Content = ConvertContentToDto(panel.Content),
|
||||
Side = panel.Side.ToString(),
|
||||
Size = panel.Size,
|
||||
IsVisible = panel.IsVisible,
|
||||
SlideOffset = panel.SlideOffset
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует DTO обратно в элемент дерева.
|
||||
/// </summary>
|
||||
private static IDockElement ConvertDtoToElement(ElementDto dto, Func<string, IDockContent?> contentResolver)
|
||||
{
|
||||
return dto switch
|
||||
{
|
||||
GroupDto groupDto => ConvertGroupDtoToElement(groupDto, contentResolver),
|
||||
LeafDto leafDto => ConvertLeafDtoToElement(leafDto, contentResolver),
|
||||
_ => throw new NotSupportedException($"Unsupported DTO type: {dto.Type}")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует DTO группы в элемент.
|
||||
/// </summary>
|
||||
private static DockGroup ConvertGroupDtoToElement(GroupDto dto, Func<string, IDockContent?> contentResolver)
|
||||
{
|
||||
var group = new DockGroup(
|
||||
ConvertDtoToElement(dto.First, contentResolver),
|
||||
ConvertDtoToElement(dto.Second, contentResolver),
|
||||
Enum.Parse<SplitDirection>(dto.Orientation))
|
||||
{
|
||||
Width = dto.Width,
|
||||
Height = dto.Height,
|
||||
SplitRatio = dto.SplitRatio
|
||||
};
|
||||
|
||||
// Восстанавливаем ID
|
||||
SetElementId(group, dto.Id);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует DTO листа в элемент.
|
||||
/// </summary>
|
||||
private static DockLeaf ConvertLeafDtoToElement(LeafDto dto, Func<string, IDockContent?> contentResolver)
|
||||
{
|
||||
var leaf = new DockLeaf
|
||||
{
|
||||
Width = dto.Width,
|
||||
Height = dto.Height,
|
||||
MinWidth = dto.MinWidth,
|
||||
MinHeight = dto.MinHeight,
|
||||
TabPlacement = Enum.Parse<TabPlacement>(dto.TabPlacement)
|
||||
};
|
||||
|
||||
// Восстанавливаем ID
|
||||
SetElementId(leaf, dto.Id);
|
||||
|
||||
// Восстанавливаем контент
|
||||
foreach (var contentRef in dto.Contents)
|
||||
{
|
||||
var content = contentResolver(contentRef.Id);
|
||||
if (content != null)
|
||||
{
|
||||
// Восстанавливаем состояние контента, если он поддерживает десериализацию
|
||||
if (content is ISerializableContent serializable)
|
||||
{
|
||||
serializable.RestoreFromState(contentRef.Properties);
|
||||
}
|
||||
|
||||
leaf.AddContent(content);
|
||||
}
|
||||
}
|
||||
|
||||
// Восстанавливаем активный контент
|
||||
if (dto.ActiveContentId != null)
|
||||
{
|
||||
leaf.ActiveContent = leaf.Children.FirstOrDefault(c => c.Id == dto.ActiveContentId);
|
||||
}
|
||||
|
||||
return leaf;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает идентификатор типа контента.
|
||||
/// </summary>
|
||||
private static string GetContentTypeId(IDockContent content)
|
||||
{
|
||||
// По умолчанию используем имя типа
|
||||
return content.GetType().Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Устанавливает ID элемента через рефлексию.
|
||||
/// </summary>
|
||||
private static void SetElementId(object element, string id)
|
||||
{
|
||||
var property = element.GetType().GetProperty("Id");
|
||||
if (property != null && property.CanWrite && property.PropertyType == typeof(string))
|
||||
{
|
||||
property.SetValue(element, id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает идентификатор приложения.
|
||||
/// </summary>
|
||||
private static string GetApplicationIdentifier()
|
||||
{
|
||||
return Assembly.GetEntryAssembly()?.GetName().Name ?? "UnknownApp";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user