Документация проекта отображения.
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
namespace Pf2eViewer;
|
||||
|
||||
/// <summary>
|
||||
/// Элемент управления прокрутки страниц
|
||||
/// </summary>
|
||||
internal sealed class CoreBindingNavigator : BindingNavigator
|
||||
{
|
||||
public CoreBindingNavigator()
|
||||
|
||||
@@ -5,33 +5,63 @@ using Pf2eModel.Model.Enum;
|
||||
|
||||
namespace Pf2eViewer.DatabaseViewer;
|
||||
|
||||
/// <summary>
|
||||
/// Класс, выполняющий страничные запросы к базе данных.
|
||||
/// </summary>
|
||||
internal class DatabaseQueryPerformer
|
||||
{
|
||||
/// <summary>
|
||||
/// Тип сущностей, которую необходимо получить.
|
||||
/// </summary>
|
||||
public required EntityType Type;
|
||||
|
||||
/// <summary>
|
||||
/// Номер страницы, которую необходимо получить.
|
||||
/// </summary>
|
||||
public required int Page;
|
||||
|
||||
/// <summary>
|
||||
/// Количество элементов на странице.
|
||||
/// </summary>
|
||||
public required int PerPage;
|
||||
|
||||
/// <summary>
|
||||
/// Поисковая строка, по которой будет производиться фильтрация.
|
||||
/// </summary>
|
||||
public required string Search = "";
|
||||
|
||||
/// <summary>
|
||||
/// Поток, в котором будет выполняться запрос к базе данных.
|
||||
/// </summary>
|
||||
private readonly Thread _queryThread;
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор класса, который инициализирует поток для выполнения запроса к базе данных.
|
||||
/// </summary>
|
||||
public DatabaseQueryPerformer()
|
||||
{
|
||||
_queryThread = new(PerformQuery);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Запускает поток для выполнения запроса к базе данных.
|
||||
/// </summary>
|
||||
public void Query()
|
||||
{
|
||||
_queryThread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Прерывает выполнение потока, если он ещё не завершён.
|
||||
/// </summary>
|
||||
public void Abort()
|
||||
{
|
||||
_queryThread.Interrupt();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет запрос к базе данных и обрабатывает результаты.
|
||||
/// </summary>
|
||||
private void PerformQuery()
|
||||
{
|
||||
try
|
||||
@@ -65,16 +95,36 @@ internal class DatabaseQueryPerformer
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Делегат, который будет вызван по завершении выполнения запроса к базе данных.
|
||||
/// </summary>
|
||||
/// <param name="sender">Объект класса, выполневшего запрос.</param>
|
||||
/// <param name="result">Результат запроса.</param>
|
||||
public delegate void DatabaseQueryPerformerDelegate(
|
||||
DatabaseQueryPerformer sender,
|
||||
DatabaseQueryResult result
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Событие, которое будет вызвано по завершении выполнения запроса к базе данных.
|
||||
/// </summary>
|
||||
public event DatabaseQueryPerformerDelegate? OnQueryCompleted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Класс, который содержит результат выполнения запроса к базе данных.
|
||||
/// </summary>
|
||||
/// <param name="count">Общее количество найденных сущностей.</param>
|
||||
/// <param name="entities">Набор найденных сущностей.</param>
|
||||
internal class DatabaseQueryResult(int count, ICollection<Entity> entities)
|
||||
{
|
||||
/// <summary>
|
||||
/// Общее количество найденных сущностей.
|
||||
/// </summary>
|
||||
public int Count { get; } = count;
|
||||
|
||||
/// <summary>
|
||||
/// Набор найденных сущностей.
|
||||
/// </summary>
|
||||
public ConcurrentBag<Entity> Entities { get; } = new(entities);
|
||||
}
|
||||
|
||||
@@ -8,10 +8,21 @@ using Timer = System.Windows.Forms.Timer;
|
||||
|
||||
namespace Pf2eViewer.DatabaseViewer;
|
||||
|
||||
/// <summary>
|
||||
/// Класс элемента управления, который отображает сущности в виде таблицы.
|
||||
/// </summary>
|
||||
public partial class DatabaseViewerControl : UserControl
|
||||
{
|
||||
#region "Поля и свойства"
|
||||
|
||||
/// <summary>
|
||||
/// Контекст базы данных, который используется для получения сущностей.
|
||||
/// </summary>
|
||||
public readonly Pf2eDbContext Context = new();
|
||||
|
||||
/// <summary>
|
||||
/// Количество элементов на странице.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public int PerPage
|
||||
{
|
||||
@@ -24,6 +35,9 @@ public partial class DatabaseViewerControl : UserControl
|
||||
}
|
||||
private int _perPage = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Номер страницы, которую необходимо отобразить.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public int Page
|
||||
{
|
||||
@@ -36,6 +50,9 @@ public partial class DatabaseViewerControl : UserControl
|
||||
}
|
||||
private int _page = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Тип сущности, которую необходимо отобразить.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public EntityType Type
|
||||
{
|
||||
@@ -49,23 +66,49 @@ public partial class DatabaseViewerControl : UserControl
|
||||
}
|
||||
private EntityType _type;
|
||||
|
||||
/// <summary>
|
||||
/// Количество элементов, которые были получены из базы данных.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public int Count { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Номер последней страницы, которая может быть отображена.
|
||||
/// </summary>
|
||||
public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage));
|
||||
|
||||
/// <summary>
|
||||
/// Выбранная в элементе управления сущность.
|
||||
/// </summary>
|
||||
[AllowNull]
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public EntityView CurrentEntity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, что элемент управления занят выполнением запроса к базе данных.
|
||||
/// </summary>
|
||||
private bool _isBusy = true;
|
||||
|
||||
/// <summary>
|
||||
/// Объект, который выполняет запрос к базе данных.
|
||||
/// </summary>
|
||||
private DatabaseQueryPerformer? _performer;
|
||||
|
||||
/// <summary>
|
||||
/// Таймер, который используется для задержки обработки события изменения текста в поле поиска.
|
||||
/// </summary>
|
||||
private Timer? _typingTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Строка, которая хранит последнее значение, введенное в поле поиска.
|
||||
/// </summary>
|
||||
private string _lastSearch = "";
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор класса, который инициализирует элемент управления и связывает его с источником данных.
|
||||
/// </summary>
|
||||
public DatabaseViewerControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -77,8 +120,13 @@ public partial class DatabaseViewerControl : UserControl
|
||||
_isBusy = false;
|
||||
}
|
||||
|
||||
// Control events
|
||||
#region "События элемента управления"
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события, который вызывается при изменении текущей страницы в элементе управления.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="e">Объект, содержащий данные события.</param>
|
||||
private void BindingNavigator_RefreshItems(object sender, EventArgs e)
|
||||
{
|
||||
if (_isBusy)
|
||||
@@ -90,6 +138,11 @@ public partial class DatabaseViewerControl : UserControl
|
||||
UpdateView();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события, который вызывается при изменении выделенной строки в элементе управления DataGridView.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="e">Объект, содержащий данные события.</param>
|
||||
private void DataGridView_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e)
|
||||
{
|
||||
if (_isBusy)
|
||||
@@ -100,6 +153,11 @@ public partial class DatabaseViewerControl : UserControl
|
||||
UpdateEntity();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события, который вызывается когда поле поиска теряет фокус.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="e">Объект, содержащий данные события.</param>
|
||||
private void SearchTextBox_Leave(object sender, EventArgs e)
|
||||
{
|
||||
if (_isBusy || SearchTextBox.Text == _lastSearch)
|
||||
@@ -110,6 +168,11 @@ public partial class DatabaseViewerControl : UserControl
|
||||
UpdateView();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события, который вызывается при изменении текста в поле поиска.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="e">Объект, содержащий данные события.</param>
|
||||
private void SearchTextBox_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_isBusy)
|
||||
@@ -128,6 +191,11 @@ public partial class DatabaseViewerControl : UserControl
|
||||
_typingTimer.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события, который вызывается при истечении времени таймера ввода текста.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="e">Объект, содержащий данные события.</param>
|
||||
private void HandleTypingTimerTimeout(object? sender, EventArgs e)
|
||||
{
|
||||
if (_isBusy || sender is not Timer timer)
|
||||
@@ -139,8 +207,14 @@ public partial class DatabaseViewerControl : UserControl
|
||||
timer.Stop();
|
||||
}
|
||||
|
||||
// Updater
|
||||
#endregion
|
||||
|
||||
#region "Обновление данных"
|
||||
|
||||
/// <summary>
|
||||
/// Устанавливает статус занятости элемента управления.
|
||||
/// </summary>
|
||||
/// <param name="busy">Новый статус занятости.</param>
|
||||
private void SetBusyStatus(bool busy)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
@@ -160,6 +234,10 @@ public partial class DatabaseViewerControl : UserControl
|
||||
: DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновляет данные в элементе управления.
|
||||
/// </summary>
|
||||
/// <param name="force">Проигнорировать флаг занятости.</param>
|
||||
public void UpdateView(bool force = false)
|
||||
{
|
||||
if (_isBusy && !force)
|
||||
@@ -184,6 +262,11 @@ public partial class DatabaseViewerControl : UserControl
|
||||
_performer.Query();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события, который вызывается по завершении выполнения запроса к базе данных.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="result">Результат выполнения события</param>
|
||||
private void HandleQueryCompleted(DatabaseQueryPerformer sender, DatabaseQueryResult result)
|
||||
{
|
||||
if (sender != _performer)
|
||||
@@ -223,6 +306,12 @@ public partial class DatabaseViewerControl : UserControl
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывает действие на указанном элементе управления в нужном потоке , если это необходимо.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="control"></param>
|
||||
/// <param name="action"></param>
|
||||
private void InvokeControl<T>(T control, Action<T> action)
|
||||
where T : Control
|
||||
{
|
||||
@@ -236,6 +325,9 @@ public partial class DatabaseViewerControl : UserControl
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновить сущность, которая была выбрана в элементе управления DataGridView.
|
||||
/// </summary>
|
||||
private void UpdateEntity()
|
||||
{
|
||||
if (DataGridView.SelectedRows.Count == 0)
|
||||
@@ -257,18 +349,42 @@ public partial class DatabaseViewerControl : UserControl
|
||||
EntityChange?.Invoke(CurrentEntity);
|
||||
}
|
||||
|
||||
// Output events
|
||||
#endregion
|
||||
|
||||
#region Output events
|
||||
|
||||
/// <summary>
|
||||
/// Делегат, который будет вызван при изменении выбранной сущности.
|
||||
/// </summary>
|
||||
/// <param name="newEntity">Новая выбранная сущность.</param>
|
||||
public delegate void EntityChangeHandler(EntityView newEntity);
|
||||
|
||||
/// <summary>
|
||||
/// Событие, которое вызывается при изменении выбранной сущности.
|
||||
/// </summary>
|
||||
public event EntityChangeHandler? EntityChange;
|
||||
|
||||
internal class EntityGridRow(Entity e)
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Класс, который представляет строку в таблице сущностей.
|
||||
/// </summary>
|
||||
/// <param name="entity">Сущность, связанная со строкой</param>
|
||||
internal class EntityGridRow(Entity entity)
|
||||
{
|
||||
public int Id { get; set; } = e.Id;
|
||||
/// <summary>
|
||||
/// Идентификатор сущности.
|
||||
/// </summary>
|
||||
public int Id { get; set; } = entity.Id;
|
||||
|
||||
public string Name { get; set; } = e.Name[LocaleType.English];
|
||||
/// <summary>
|
||||
/// Название сущности.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = entity.Name[LocaleType.English];
|
||||
|
||||
public EntityRarity Rarity { get; set; } = e.Rarity;
|
||||
/// <summary>
|
||||
/// Редкость сущности.
|
||||
/// </summary>
|
||||
public EntityRarity Rarity { get; set; } = entity.Rarity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Pf2eViewer.EntityTreeViewer;
|
||||
|
||||
/// <summary>
|
||||
/// Элемент управления для отображения структуры сущности.
|
||||
/// </summary>
|
||||
public sealed partial class EntityTreeViewerControl : UserControl
|
||||
{
|
||||
#region "Поля элемента управления"
|
||||
|
||||
/// <summary>
|
||||
/// Высота строки поля структуры.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[Category("Entity Render")]
|
||||
[Description("The height of each item in the tree view.")]
|
||||
@@ -19,6 +26,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
}
|
||||
private int _itemHeight = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Сущность, которая будет отображаться в элементе управления.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
[Browsable(false)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
@@ -33,6 +43,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
}
|
||||
private EntityView? _entityView;
|
||||
|
||||
/// <summary>
|
||||
/// Ширина отступа глубины.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[Category("Entity Render")]
|
||||
[Description("The size of tab.")]
|
||||
@@ -47,6 +60,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
}
|
||||
private int _depthSize = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Размер шрифта типа поля сущности.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[Category("Entity Render")]
|
||||
[Description("The size of tab.")]
|
||||
@@ -61,6 +77,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
}
|
||||
private float _typeFontSize = 9.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Размер кнопки плюс/минус.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[Category("Entity Render")]
|
||||
[Description("The size of plus/minus button.")]
|
||||
@@ -75,11 +94,17 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
}
|
||||
private int _buttonSize = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Ширина имени поля сущности.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
[Browsable(false)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int NameWidth { get; private set; } = 200;
|
||||
|
||||
/// <summary>
|
||||
/// Цвет фона строки поля при наведении на элемент управления.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[Category("Entity Render")]
|
||||
[Description("Color on item hover.")]
|
||||
@@ -94,21 +119,50 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
}
|
||||
private Color _hoverColor = SystemColors.ControlDark;
|
||||
|
||||
[Browsable(false)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int ContentWidth => Width - SystemInformation.VerticalScrollBarWidth - 2;
|
||||
|
||||
private readonly List<StructureField> _rows = [];
|
||||
|
||||
private List<EntityTreeViewerControlRow> _viewerRows = [];
|
||||
|
||||
private bool _rendering;
|
||||
|
||||
/// <summary>
|
||||
/// Изображение, используемое для отображения содержимого элемента управления.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
[Browsable(false)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Bitmap ContentImage { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Вычисляемые поля"
|
||||
|
||||
/// <summary>
|
||||
/// Ширина области содержимого элемента управления.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int ContentWidth => Width - SystemInformation.VerticalScrollBarWidth - 2;
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Приватные поля"
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Коллекция полей структуры.
|
||||
/// </summary>
|
||||
private readonly List<StructureField> _rows = [];
|
||||
|
||||
/// <summary>
|
||||
/// Коллекция строк полей структуры, которые будут отображаться в элементе управления.
|
||||
/// </summary>
|
||||
private List<EntityTreeViewerControlRow> _viewerRows = [];
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, что элемент управления в данный момент перерисовывается.
|
||||
/// </summary>
|
||||
private bool _rendering;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор класса <see cref="EntityTreeViewerControl"/>.
|
||||
/// </summary>
|
||||
public EntityTreeViewerControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -120,6 +174,11 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
ContentPanel.MouseWheel += ContentPanel_MouseWheel;
|
||||
}
|
||||
|
||||
#region "Методы обновления"
|
||||
|
||||
/// <summary>
|
||||
/// Обновление данных в элементе управления.
|
||||
/// </summary>
|
||||
public void UpdateData()
|
||||
{
|
||||
if (EntityView?.Entity is null)
|
||||
@@ -148,6 +207,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
UpdateRenders();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновление отрисовок в элементе управления.
|
||||
/// </summary>
|
||||
public void UpdateRenders()
|
||||
{
|
||||
int maxNameWidth = -1;
|
||||
@@ -171,6 +233,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
UpdateView();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновление отображения в элементе управления.
|
||||
/// </summary>
|
||||
public void UpdateView()
|
||||
{
|
||||
_viewerRows.ForEach(row => row.UpdateButton());
|
||||
@@ -203,6 +268,14 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
_rendering = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Вспомогательные методы"
|
||||
|
||||
/// <summary>
|
||||
/// Построение лестницы для строк полей структуры.
|
||||
/// </summary>
|
||||
/// <param name="rows">Набор строк полей структуры.</param>
|
||||
private void BuildLadder(List<EntityTreeViewerControlRow> rows)
|
||||
{
|
||||
List<LadderType> lastLadder = [];
|
||||
@@ -248,8 +321,15 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
}
|
||||
}
|
||||
|
||||
// Events
|
||||
#endregion
|
||||
|
||||
#region "События элемента управления"
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события изменения размера элемента управления.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="e">Объект события.</param>
|
||||
private void EntityTreeViewerControl_Resize(object sender, EventArgs e)
|
||||
{
|
||||
if (_rendering)
|
||||
@@ -262,6 +342,11 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
UpdateRenders();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события прокрутки элемента управления.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="e">Объект события.</param>
|
||||
private void ContentPanel_MouseWheel(object? sender, MouseEventArgs e)
|
||||
{
|
||||
/*
|
||||
@@ -281,6 +366,11 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события движения мыши над элементом управления.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="e">Объект события.</param>
|
||||
private void ContentPanel_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
PointF p = new(e.X, e.Y);
|
||||
@@ -299,4 +389,6 @@ public sealed partial class EntityTreeViewerControl : UserControl
|
||||
row.Hovered = contained;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -5,13 +5,26 @@ using Image = System.Drawing.Image;
|
||||
|
||||
namespace Pf2eViewer.EntityTreeViewer;
|
||||
|
||||
/// <summary>
|
||||
/// Элемент управления для отображения строки поля сущности.
|
||||
/// </summary>
|
||||
internal class EntityTreeViewerControlRow
|
||||
{
|
||||
/// <summary>
|
||||
/// Элемент управления, которому принадлежит эта строка.
|
||||
/// </summary>
|
||||
public readonly EntityTreeViewerControl ParentControl;
|
||||
|
||||
/// <summary>
|
||||
/// Отображаемая строка поля сущности.
|
||||
/// </summary>
|
||||
public readonly StructureField Item;
|
||||
|
||||
// Local fields
|
||||
#region "Поля строки"
|
||||
|
||||
/// <summary>
|
||||
/// Кэш изображения строки.
|
||||
/// </summary>
|
||||
public Image Cache
|
||||
{
|
||||
get
|
||||
@@ -26,6 +39,9 @@ internal class EntityTreeViewerControlRow
|
||||
}
|
||||
private Image? _cache;
|
||||
|
||||
/// <summary>
|
||||
/// Кэш изображения строки при наведении.
|
||||
/// </summary>
|
||||
public Image HoveredCache
|
||||
{
|
||||
get
|
||||
@@ -40,6 +56,9 @@ internal class EntityTreeViewerControlRow
|
||||
}
|
||||
private Image? _hoveredCache;
|
||||
|
||||
/// <summary>
|
||||
/// Список типов лестницы.
|
||||
/// </summary>
|
||||
public List<LadderType> Ladder
|
||||
{
|
||||
get => _ladder;
|
||||
@@ -51,6 +70,9 @@ internal class EntityTreeViewerControlRow
|
||||
}
|
||||
private List<LadderType> _ladder = [];
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, находится ли курсор над строкой.
|
||||
/// </summary>
|
||||
public bool Hovered
|
||||
{
|
||||
get => _hovered;
|
||||
@@ -65,59 +87,103 @@ internal class EntityTreeViewerControlRow
|
||||
}
|
||||
private bool _hovered = false;
|
||||
|
||||
/// <summary>
|
||||
/// Индекс строки в представлении.
|
||||
/// </summary>
|
||||
public int ViewIndex { get; set; } // = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Кнопка раскрытия/сворачивания.
|
||||
/// </summary>
|
||||
public IconButton? Button { get; set; } = null;
|
||||
|
||||
// Inherited fields
|
||||
#endregion
|
||||
|
||||
#region "Поля для рендеринга"
|
||||
|
||||
/// <inheritdoc cref="EntityTreeViewerControl.Font"/>
|
||||
public Font Font
|
||||
{
|
||||
get => ParentControl.Font;
|
||||
set => ParentControl.Font = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityTreeViewerControl.ForeColor"/>
|
||||
public Color ForeColor
|
||||
{
|
||||
get => ParentControl.ForeColor;
|
||||
set => ParentControl.ForeColor = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityTreeViewerControl.BackColor"/>
|
||||
public Color BackColor
|
||||
{
|
||||
get => ParentControl.BackColor;
|
||||
set => ParentControl.BackColor = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityTreeViewerControl.HoverColor"/>
|
||||
public Color HoverColor
|
||||
{
|
||||
get => ParentControl.HoverColor;
|
||||
set => ParentControl.HoverColor = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityTreeViewerControl.ItemHeight"/>
|
||||
public int ItemHeight
|
||||
{
|
||||
get => ParentControl.ItemHeight;
|
||||
set => ParentControl.ItemHeight = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityTreeViewerControl.DepthSize"/>
|
||||
public int DepthSize
|
||||
{
|
||||
get => ParentControl.DepthSize;
|
||||
set => ParentControl.DepthSize = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityTreeViewerControl.ButtonSize"/>
|
||||
public int ButtonSize
|
||||
{
|
||||
get => ParentControl.ButtonSize;
|
||||
set => ParentControl.ButtonSize = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityTreeViewerControl.TypeFontSize"/>
|
||||
public float TypeFontSize
|
||||
{
|
||||
get => ParentControl.TypeFontSize;
|
||||
set => ParentControl.TypeFontSize = value;
|
||||
}
|
||||
|
||||
// ValueText functions
|
||||
#endregion
|
||||
|
||||
#region "Вычисляемые поля"
|
||||
|
||||
/// <summary>
|
||||
/// Ширина строки.
|
||||
/// </summary>
|
||||
public int Width => ParentControl.ContentWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Высота строки.
|
||||
/// </summary>
|
||||
public int Height => ItemHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Флаг наличия кнопки.
|
||||
/// </summary>
|
||||
public bool HasButton => Item.FieldType == StructureFieldType.Complex;
|
||||
|
||||
/// <summary>
|
||||
/// Глубина отображения строки.
|
||||
/// </summary>
|
||||
public int Depth => Item.Depth;
|
||||
|
||||
/// <summary>
|
||||
/// Ширина имени в строке.
|
||||
/// </summary>
|
||||
public int NameWidth
|
||||
{
|
||||
get
|
||||
@@ -127,6 +193,9 @@ internal class EntityTreeViewerControlRow
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Строковое представление типа поля.
|
||||
/// </summary>
|
||||
public string TypeText
|
||||
{
|
||||
get
|
||||
@@ -140,10 +209,19 @@ internal class EntityTreeViewerControlRow
|
||||
}
|
||||
}
|
||||
|
||||
// public Font TypeFont => new(Font.FontFamily, TypeFontSize, FontStyle.Italic);
|
||||
/// <summary>
|
||||
/// Шрифт для отображения типа поля.
|
||||
/// </summary>
|
||||
public Font TypeFont => new(Font.FontFamily, TypeFontSize);
|
||||
|
||||
/// <summary>
|
||||
/// Строковое представление значения поля.
|
||||
/// </summary>
|
||||
public string ValueText => Item.GetValueString();
|
||||
|
||||
/// <summary>
|
||||
/// Шрифт для отображения значения поля.
|
||||
/// </summary>
|
||||
public Font ValueFont =>
|
||||
new(
|
||||
Font.FontFamily,
|
||||
@@ -151,16 +229,31 @@ internal class EntityTreeViewerControlRow
|
||||
Item.FieldType == StructureFieldType.Complex ? FontStyle.Italic : FontStyle.Regular
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Прямоугольник, занимаемый строкой в представлении.
|
||||
/// </summary>
|
||||
public RectangleF Target => new(0, ViewIndex * Height, Width, Height);
|
||||
|
||||
/// <summary>
|
||||
/// Прямоугольник, занимаемый строкой в родительском представлении.
|
||||
/// </summary>
|
||||
public RectangleF RealTarget =>
|
||||
new(0, ViewIndex * Height - ParentControl.VerticalScroll.Value, Width, Height);
|
||||
|
||||
// Simple fields
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Список данных для рендеринга строк.
|
||||
/// </summary>
|
||||
private readonly List<StringRenderData> _renderData = [];
|
||||
|
||||
// Main methods
|
||||
#region "Основные методы"
|
||||
|
||||
/// <summary>
|
||||
/// Создание элемента управления строки поля сущности.
|
||||
/// </summary>
|
||||
/// <param name="parent">Родительский элемент управления.</param>
|
||||
/// <param name="item">Строка поля структуры объекта.</param>
|
||||
public EntityTreeViewerControlRow(EntityTreeViewerControl parent, StructureField item)
|
||||
{
|
||||
ParentControl = parent;
|
||||
@@ -171,6 +264,9 @@ internal class EntityTreeViewerControlRow
|
||||
UpdateButton();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Применение свойств к элементу управления.
|
||||
/// </summary>
|
||||
private void ApplyItem()
|
||||
{
|
||||
_renderData.Clear();
|
||||
@@ -200,12 +296,20 @@ internal class EntityTreeViewerControlRow
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновление кэша изображения строки.
|
||||
/// </summary>
|
||||
public void UpdateCache()
|
||||
{
|
||||
Cache = RenderCache(BackColor);
|
||||
HoveredCache = RenderCache(HoverColor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создание кэша изображения строки.
|
||||
/// </summary>
|
||||
/// <param name="backColor">Цвет заднего фона.</param>
|
||||
/// <returns>Кеш изображения строки.</returns>
|
||||
private Image RenderCache(Color backColor)
|
||||
{
|
||||
Image cache = new Bitmap(Width, Height);
|
||||
@@ -220,6 +324,10 @@ internal class EntityTreeViewerControlRow
|
||||
return cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Запрос на перерисовку строки.
|
||||
/// </summary>
|
||||
/// <param name="g">Графический объект.</param>
|
||||
public void Invalidate(Graphics? g = null)
|
||||
{
|
||||
g ??= Graphics.FromImage(ParentControl.ContentImage);
|
||||
@@ -229,8 +337,13 @@ internal class EntityTreeViewerControlRow
|
||||
UpdateButton();
|
||||
}
|
||||
|
||||
// Button
|
||||
#endregion
|
||||
|
||||
#region "Кнопка"
|
||||
|
||||
/// <summary>
|
||||
/// Обновление кнопки раскрытия/сворачивания.
|
||||
/// </summary>
|
||||
public void UpdateButton()
|
||||
{
|
||||
if (Button is null)
|
||||
@@ -250,6 +363,11 @@ internal class EntityTreeViewerControlRow
|
||||
Button.Visible = Item.Visible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Событие нажатия кнопки раскрытия/сворачивания.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="e">Событие нажатие кнопки</param>
|
||||
private void Button_Click(object? sender, EventArgs e)
|
||||
{
|
||||
if (Item is StructureFieldComplex complex)
|
||||
@@ -259,8 +377,14 @@ internal class EntityTreeViewerControlRow
|
||||
}
|
||||
}
|
||||
|
||||
// Render
|
||||
#endregion
|
||||
|
||||
#region "Отрисовка"
|
||||
|
||||
/// <summary>
|
||||
/// Отрисовка лестницы.
|
||||
/// </summary>
|
||||
/// <param name="g">Графический объект.</param>
|
||||
private void RenderLadder(Graphics g)
|
||||
{
|
||||
int n = Depth;
|
||||
@@ -301,6 +425,10 @@ internal class EntityTreeViewerControlRow
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Отрисовка содержимого строки.
|
||||
/// </summary>
|
||||
/// <param name="g">Графический объект.</param>
|
||||
private void RenderContent(Graphics g)
|
||||
{
|
||||
float[] xs =
|
||||
@@ -326,13 +454,28 @@ internal class EntityTreeViewerControlRow
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
#endregion
|
||||
|
||||
#region Функции-помощники в отрисовке
|
||||
|
||||
/// <summary>
|
||||
/// Смешивание цвета с цветом текста.
|
||||
/// </summary>
|
||||
/// <param name="color">Цвет оттенка.</param>
|
||||
/// <param name="rate">Степень смешения.</param>
|
||||
/// <returns>Смешенный цвет.</returns>
|
||||
private Color BlendColor(Color color, double rate = 0.5)
|
||||
{
|
||||
return BlendColor(ForeColor, color, rate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Смешивание цветов.
|
||||
/// </summary>
|
||||
/// <param name="color1">Первый смешиваемый цвет.</param>
|
||||
/// <param name="color2">Второй смешиваемый цвет.</param>
|
||||
/// <param name="rate">Степень смешения.</param>
|
||||
/// <returns>Смешенный цвет.</returns>
|
||||
private Color BlendColor(Color color1, Color color2, double rate)
|
||||
{
|
||||
return Color.FromArgb(
|
||||
@@ -345,6 +488,10 @@ internal class EntityTreeViewerControlRow
|
||||
int Mix(int a, int b) => Convert.ToInt32(Math.Round(a * rate + b * (1 - rate)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение цвета текста по типу значения поля.
|
||||
/// </summary>
|
||||
/// <returns>Цвет текста.</returns>
|
||||
public Color GetValueColor()
|
||||
{
|
||||
return Item switch
|
||||
@@ -363,19 +510,59 @@ internal class EntityTreeViewerControlRow
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Класс для хранения данных рендеринга строки.
|
||||
/// </summary>
|
||||
/// <param name="text">Текст отображаемой строки.</param>
|
||||
/// <param name="font">Шрифт отображаемой строки.</param>
|
||||
/// <param name="color">Цвет отображаемой строки.</param>
|
||||
private class StringRenderData(string text, Font font, Color color)
|
||||
{
|
||||
/// <summary>
|
||||
/// Текст отображаемой строки.
|
||||
/// </summary>
|
||||
public string Text { get; } = text;
|
||||
|
||||
/// <summary>
|
||||
/// Шрифт отображаемой строки.
|
||||
/// </summary>
|
||||
public Font Font { get; } = font;
|
||||
|
||||
/// <summary>
|
||||
/// Цвет отображаемой строки.
|
||||
/// </summary>
|
||||
public Color Color { get; } = color;
|
||||
|
||||
/// <summary>
|
||||
/// Левый край отображаемой строки.
|
||||
/// </summary>
|
||||
public float Left { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ширина отображаемой строки.
|
||||
/// </summary>
|
||||
public float Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Высота отображаемой строки.
|
||||
/// </summary>
|
||||
public float Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Прямоугольник, занимаемый строкой.
|
||||
/// </summary>
|
||||
public RectangleF Rect => new(Left, 0, Width, Height);
|
||||
|
||||
private string Fit(Graphics g, RectangleF rect)
|
||||
/// <summary>
|
||||
/// Уместить строку в занимаемый прямоугольник.
|
||||
/// </summary>
|
||||
/// <param name="g">Элемент графики.</param>
|
||||
/// <param name="rect">Занимаемый прямоугольник</param>
|
||||
/// <param name="filler">Строка, на которую будет заканчиваться значнеие при переполнении.</param>
|
||||
/// <returns></returns>
|
||||
private string Fit(Graphics g, RectangleF rect, string filler = "…")
|
||||
{
|
||||
if (Text.Length == 0 || g.MeasureString(Text, Font).Width <= rect.Width)
|
||||
{
|
||||
@@ -386,16 +573,20 @@ internal class EntityTreeViewerControlRow
|
||||
|
||||
int i = txt.Length;
|
||||
|
||||
while (g.MeasureString(txt + "...", Font).Width > rect.Width)
|
||||
while (g.MeasureString(txt + filler, Font).Width > rect.Width)
|
||||
{
|
||||
txt = txt[..--i];
|
||||
if (i == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return txt + "...";
|
||||
return txt + filler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Отрисовка строки.
|
||||
/// </summary>
|
||||
/// <param name="g">Элемент графики.</param>
|
||||
public void Draw(Graphics g)
|
||||
{
|
||||
g.DrawString(
|
||||
|
||||
@@ -5,10 +5,23 @@ using Pf2eModel.Model.Common.I18n;
|
||||
|
||||
namespace Pf2eViewer.EntityTreeViewer;
|
||||
|
||||
/// <summary>
|
||||
/// Класс для построения структуры данных класса.
|
||||
/// </summary>
|
||||
internal class StructureBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Список обработанных объектов.
|
||||
/// </summary>
|
||||
private readonly List<StructureProcessedObject> _processedObjects = [];
|
||||
|
||||
/// <summary>
|
||||
/// Создание поля структуры.
|
||||
/// </summary>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="depth">Глубина, на котором поле отображается.</param>
|
||||
/// <returns>Поле структуры.</returns>
|
||||
public StructureFieldComplex Create(string name, object value, int depth = 0)
|
||||
{
|
||||
_ = RegisterProcessed(value);
|
||||
@@ -20,6 +33,11 @@ internal class StructureBuilder
|
||||
return field;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Регистрация обработанного объекта.
|
||||
/// </summary>
|
||||
/// <param name="value">Регистрируемый объект.</param>
|
||||
/// <returns>`true`, если объект был добавлен; `false`, если объект был добавлен ранее.</returns>
|
||||
public bool RegisterProcessed(object value)
|
||||
{
|
||||
if (_processedObjects.Any(p => p.Value == value))
|
||||
@@ -34,6 +52,11 @@ internal class StructureBuilder
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Дополнить обработанный объект.
|
||||
/// </summary>
|
||||
/// <param name="value">Значение.</param>
|
||||
/// <param name="field">Поле объекта.</param>
|
||||
public void CompleteProcessed(object value, StructureFieldComplex field)
|
||||
{
|
||||
StructureProcessedObject? q = _processedObjects.FirstOrDefault(p => p.Value == value);
|
||||
@@ -46,6 +69,13 @@ internal class StructureBuilder
|
||||
q.Field = field;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выбор поля структуры по значению.
|
||||
/// </summary>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="depth">Глубина, на котором поле отображается.</param>
|
||||
/// <returns></returns>
|
||||
public StructureField Resolve(string name, object? value, int depth)
|
||||
{
|
||||
switch (GetFieldType(value))
|
||||
@@ -87,6 +117,11 @@ internal class StructureBuilder
|
||||
throw new("Impossible");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить тип поля структуры по значению.
|
||||
/// </summary>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <returns>Тип поля структуры.</returns>
|
||||
public static StructureFieldType GetFieldType(object? value)
|
||||
{
|
||||
if (value is null)
|
||||
@@ -110,6 +145,10 @@ internal class StructureBuilder
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Класс для хранения обработанного объекта.
|
||||
/// </summary>
|
||||
/// <param name="value">Объект.</param>
|
||||
internal class StructureProcessedObject(object value)
|
||||
{
|
||||
public readonly object Value = value;
|
||||
@@ -142,38 +181,109 @@ internal class StructureProcessedObject(object value)
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тип поля структуры.
|
||||
/// </summary>
|
||||
internal enum StructureFieldType
|
||||
{
|
||||
/// <summary>
|
||||
/// Пустое поле.
|
||||
/// </summary>
|
||||
Null,
|
||||
|
||||
/// <summary>
|
||||
/// Простое поле.
|
||||
/// </summary>
|
||||
Primitive,
|
||||
|
||||
/// <summary>
|
||||
/// Поле с перечислением.
|
||||
/// </summary>
|
||||
Enum,
|
||||
|
||||
/// <summary>
|
||||
/// Поле с объектом.
|
||||
/// </summary>
|
||||
Complex,
|
||||
|
||||
/// <summary>
|
||||
/// Поле с ссылкой на объект.
|
||||
/// </summary>
|
||||
Reference,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тип лестницы глубины.
|
||||
/// </summary>
|
||||
internal enum LadderType
|
||||
{
|
||||
/// <summary>
|
||||
/// Пустой элемент: [ ].
|
||||
/// </summary>
|
||||
Empty,
|
||||
|
||||
/// <summary>
|
||||
/// Последний элемент: [└].
|
||||
/// </summary>
|
||||
Last,
|
||||
|
||||
/// <summary>
|
||||
/// Проходной элемент: [├] или [│].
|
||||
/// </summary>
|
||||
Through,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Базовый класс для полей структуры.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal abstract class StructureField(StructureBuilder builder, string name, int depth)
|
||||
{
|
||||
/// <summary>
|
||||
/// Название поля.
|
||||
/// </summary>
|
||||
public readonly string Name = name;
|
||||
|
||||
/// <summary>
|
||||
/// Тип поля структуры.
|
||||
/// </summary>
|
||||
public abstract StructureFieldType FieldType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Класс построения структуры данных.
|
||||
/// </summary>
|
||||
public readonly StructureBuilder Builder = builder;
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, отображается ли поле.
|
||||
/// </summary>
|
||||
public virtual bool Visible { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Глубина поля структуры.
|
||||
/// </summary>
|
||||
public readonly int Depth = depth;
|
||||
|
||||
/// <summary>
|
||||
/// Получить строку, представляющую тип поля.
|
||||
/// </summary>
|
||||
/// <returns>Строка, представляющая тип поля.</returns>
|
||||
public abstract string GetTypeString();
|
||||
|
||||
/// <summary>
|
||||
/// Получить строку, представляющую значение поля.
|
||||
/// </summary>
|
||||
/// <returns>Строка, представляющая значение поля.</returns>
|
||||
public abstract string GetValueString();
|
||||
|
||||
/// <summary>
|
||||
/// Получить набор строк, используя результат функция.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Тип результирующей строки.</typeparam>
|
||||
/// <param name="func">Функция, оборажения поля в результирующую строку.</param>
|
||||
/// <returns>Коллекция, содержащая набор строк.</returns>
|
||||
public virtual ICollection<T> GetRows<T>(Func<StructureField, T> func)
|
||||
{
|
||||
StructureField[] q = [this];
|
||||
@@ -181,16 +291,32 @@ internal abstract class StructureField(StructureBuilder builder, string name, in
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Пустое поле структуры.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldNull(StructureBuilder builder, string name, int depth)
|
||||
: StructureField(builder, name, depth)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString() => "null";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() => "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Простое поле структуры.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldPrimitive(
|
||||
StructureBuilder builder,
|
||||
object value,
|
||||
@@ -198,12 +324,20 @@ internal class StructureFieldPrimitive(
|
||||
int depth
|
||||
) : StructureField(builder, name, depth)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// Значение поля.
|
||||
/// </summary>
|
||||
public readonly object Value = value;
|
||||
|
||||
/// <summary>
|
||||
/// Тип значения поля.
|
||||
/// </summary>
|
||||
public readonly Type Type = value.GetType();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString()
|
||||
{
|
||||
return Type.GetTypeCode(Type) switch
|
||||
@@ -216,6 +350,7 @@ internal class StructureFieldPrimitive(
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString()
|
||||
{
|
||||
if (Type == typeof(string))
|
||||
@@ -227,28 +362,57 @@ internal class StructureFieldPrimitive(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поле структуры с перечислением.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldEnum(StructureBuilder builder, object value, string name, int depth)
|
||||
: StructureField(builder, name, depth)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Enum;
|
||||
|
||||
/// <summary>
|
||||
/// Значение поля.
|
||||
/// </summary>
|
||||
public readonly object Value = value;
|
||||
|
||||
/// <summary>
|
||||
/// Тип значения поля.
|
||||
/// </summary>
|
||||
public readonly Type Type = value.GetType();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString() => "enum";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() => $"{Type.Name}.{Value}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поле стркутуры с объектом.
|
||||
/// </summary>
|
||||
internal class StructureFieldComplex : StructureField
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Complex;
|
||||
|
||||
/// <summary>
|
||||
/// Набор полей объекта.
|
||||
/// </summary>
|
||||
public readonly List<StructureField> Fields = [];
|
||||
|
||||
/// <summary>
|
||||
/// Тип значения поля.
|
||||
/// </summary>
|
||||
public readonly Type Type;
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, отображается ли поля объекта.
|
||||
/// </summary>
|
||||
public bool Collapsed
|
||||
{
|
||||
get => _collapsed;
|
||||
@@ -260,6 +424,7 @@ internal class StructureFieldComplex : StructureField
|
||||
}
|
||||
private bool _collapsed = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
@@ -271,6 +436,13 @@ internal class StructureFieldComplex : StructureField
|
||||
}
|
||||
private bool _visible = true;
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор класса поля структуры с объектом.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
public StructureFieldComplex(StructureBuilder builder, object value, string name, int depth)
|
||||
: base(builder, name, depth)
|
||||
{
|
||||
@@ -279,6 +451,9 @@ internal class StructureFieldComplex : StructureField
|
||||
SetFields((dynamic)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновить видимость полей структуры.
|
||||
/// </summary>
|
||||
private void UpdateFieldVisibility()
|
||||
{
|
||||
foreach (StructureField field in Fields)
|
||||
@@ -287,6 +462,10 @@ internal class StructureFieldComplex : StructureField
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры `LocaleString`.
|
||||
/// </summary>
|
||||
/// <param name="localeString">Объект строки локализации.</param>
|
||||
private void SetFields(LocaleString localeString)
|
||||
{
|
||||
SetFieldsFromRecords(
|
||||
@@ -296,6 +475,10 @@ internal class StructureFieldComplex : StructureField
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры `IEnumerable`.
|
||||
/// </summary>
|
||||
/// <param name="enumerable">Объект-перечисление.</param>
|
||||
private void SetFields(IEnumerable enumerable)
|
||||
{
|
||||
int i = 0;
|
||||
@@ -310,6 +493,10 @@ internal class StructureFieldComplex : StructureField
|
||||
SetFieldsFromRecords(records);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры общего объекта.
|
||||
/// </summary>
|
||||
/// <param name="value">Объект.</param>
|
||||
private void SetFields(object value)
|
||||
{
|
||||
Type type = value.GetType();
|
||||
@@ -325,6 +512,10 @@ internal class StructureFieldComplex : StructureField
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры из списка записей.
|
||||
/// </summary>
|
||||
/// <param name="records"></param>
|
||||
private void SetFieldsFromRecords(List<FieldRecord> records)
|
||||
{
|
||||
foreach (
|
||||
@@ -346,22 +537,35 @@ internal class StructureFieldComplex : StructureField
|
||||
}
|
||||
}
|
||||
|
||||
public override ICollection<T> GetRows<T>(Func<StructureField, T> func)
|
||||
{
|
||||
return [.. base.GetRows(func), .. Fields.SelectMany(f => f.GetRows(func)).ToList()];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Запись поля структуры.
|
||||
/// </summary>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
private class FieldRecord(string name, object? value)
|
||||
{
|
||||
/// <summary>
|
||||
/// Название поля.
|
||||
/// </summary>
|
||||
public readonly string Name = name;
|
||||
|
||||
/// <summary>
|
||||
/// Значение поля.
|
||||
/// </summary>
|
||||
public readonly object? Value = value;
|
||||
|
||||
/// <summary>
|
||||
/// Тип поля структуры.
|
||||
/// </summary>
|
||||
public StructureFieldType FieldType => StructureBuilder.GetFieldType(Value);
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, использовать ли поле.
|
||||
/// </summary>
|
||||
public bool Allowed = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString()
|
||||
{
|
||||
if (Type.IsGenericEnumerableType())
|
||||
@@ -376,6 +580,7 @@ internal class StructureFieldComplex : StructureField
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString()
|
||||
{
|
||||
if (Type.IsGenericEnumerableType())
|
||||
@@ -384,8 +589,21 @@ internal class StructureFieldComplex : StructureField
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ICollection<T> GetRows<T>(Func<StructureField, T> func)
|
||||
{
|
||||
return [.. base.GetRows(func), .. Fields.SelectMany(f => f.GetRows(func)).ToList()];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поле структуры с ссылкой на объект.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="reference">Ссылка на объект.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldReference(
|
||||
StructureBuilder builder,
|
||||
string name,
|
||||
@@ -393,11 +611,17 @@ internal class StructureFieldReference(
|
||||
int depth
|
||||
) : StructureField(builder, name, depth)
|
||||
{
|
||||
/// <summary>
|
||||
/// Ссылка на объект.
|
||||
/// </summary>
|
||||
public StructureFieldComplex? Reference = reference;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Reference;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString() => Reference?.GetTypeString() ?? "???";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() => "<Reference>";
|
||||
}
|
||||
|
||||
@@ -7,11 +7,27 @@ using Pf2eModel.Model.Structure;
|
||||
|
||||
namespace Pf2eViewer;
|
||||
|
||||
/// <summary>
|
||||
/// Класс, обрабатывающий запросы к сущностям и подключающий необходимые данные.
|
||||
/// </summary>
|
||||
public class EntityView
|
||||
{
|
||||
public Entity? Entity;
|
||||
public IEntity? SubEntity;
|
||||
/// <summary>
|
||||
/// Корневая сущность.
|
||||
/// </summary>
|
||||
public Entity? Entity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Основная сущность.
|
||||
/// </summary>
|
||||
public IEntity? SubEntity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор класса, который получает сущность из базы данных и заполняет её данными.
|
||||
/// </summary>
|
||||
/// <param name="context">Контекст базы данных.</param>
|
||||
/// <param name="type">Тип запрашиваемой сущности.</param>
|
||||
/// <param name="id">Идентификатор запрашиваемой сущности.</param>
|
||||
public EntityView(Pf2eDbContext context, EntityType type, int id)
|
||||
{
|
||||
Getter? getter = _getters.FirstOrDefault(g => g.Type == type);
|
||||
@@ -37,6 +53,9 @@ public class EntityView
|
||||
SubEntity = subEntity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Словарь, содержащий методы получения сущностей из базы данных.
|
||||
/// </summary>
|
||||
private readonly ICollection<Getter> _getters =
|
||||
[
|
||||
new Getter<Ancestry>(
|
||||
@@ -90,6 +109,10 @@ public class EntityView
|
||||
),
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Абстрактный класс, который определяет метод получения сущности из базы данных.
|
||||
/// </summary>
|
||||
/// <param name="type">Связанный тип сущности.</param>
|
||||
private abstract class Getter(EntityType type)
|
||||
{
|
||||
public readonly EntityType Type = type;
|
||||
@@ -97,6 +120,12 @@ public class EntityView
|
||||
public abstract IEntity? Get(int id, Pf2eDbContext context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Класс, определяющий метод получения сущности из базы данных.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Тип данных основной сущности.</typeparam>
|
||||
/// <param name="type">Связанный тип сущности.</param>
|
||||
/// <param name="method">Метод получения сущности из базы данных.</param>
|
||||
private class Getter<T>(EntityType type, Func<Pf2eDbContext, IQueryable<T>> method)
|
||||
: Getter(type)
|
||||
where T : IEntity
|
||||
|
||||
@@ -4,10 +4,19 @@ using Pf2eModel.Model.Enum;
|
||||
|
||||
namespace Pf2eViewer;
|
||||
|
||||
/// <summary>
|
||||
/// Основная форма приложения.
|
||||
/// </summary>
|
||||
public partial class MainViewForm : Form
|
||||
{
|
||||
/// <summary>
|
||||
/// Контекст базы данных.
|
||||
/// </summary>
|
||||
public Pf2eDbContext Context = new();
|
||||
|
||||
/// <summary>
|
||||
/// Элемент управления для отображения дерева сущностей.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public EntityView? CurrentView
|
||||
{
|
||||
@@ -19,6 +28,9 @@ public partial class MainViewForm : Form
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор класса <see cref="MainViewForm"/>.
|
||||
/// </summary>
|
||||
public MainViewForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -28,6 +40,9 @@ public partial class MainViewForm : Form
|
||||
Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Сброс состояния формы.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
Context.Dispose();
|
||||
@@ -41,6 +56,11 @@ public partial class MainViewForm : Form
|
||||
TypesListBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события изменения выбранного элемента в списке типов сущностей.
|
||||
/// </summary>
|
||||
/// <param name="sender">Источник события.</param>
|
||||
/// <param name="_">Данные события.</param>
|
||||
private void TypesListBox_SelectedIndexChanged(object sender, EventArgs _)
|
||||
{
|
||||
if (TypesListBox.SelectedItem is null)
|
||||
@@ -53,6 +73,10 @@ public partial class MainViewForm : Form
|
||||
DatabaseViewer.Type = (EntityType)TypesListBox.SelectedItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик события изменения выбранной сущности в дереве сущностей.
|
||||
/// </summary>
|
||||
/// <param name="newEntity">Новая выбранная сущность.</param>
|
||||
private void DatabaseViewer_EntityChange(EntityView newEntity)
|
||||
{
|
||||
CurrentView = newEntity;
|
||||
|
||||
Reference in New Issue
Block a user