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