diff --git a/Pf2eViewer/EntityConverter.cs b/Pf2eViewer/EntityConverter.cs index e022d9e..1b853de 100644 --- a/Pf2eViewer/EntityConverter.cs +++ b/Pf2eViewer/EntityConverter.cs @@ -4,7 +4,9 @@ using Newtonsoft.Json.Linq; using Pf2eModel; using Pf2eModel.Model; using Pf2eModel.Model.Base; +using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Structure; +using System.ComponentModel; namespace Pf2eViewer; internal class EntityConverter : JsonConverter @@ -16,44 +18,62 @@ internal class EntityConverter : JsonConverter return objectType == typeof(Entity) || objectType.IsAssignableTo(typeof(IEntity)); } - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + private Dictionary DictionarySerialization(object source) { - JToken token = JToken.FromObject(value!); + Dictionary dictionary = []; - if (token.Type != JTokenType.Object) + foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source)) { - token.WriteTo(writer); - return; + object? v = property.GetValue(source); + dictionary.Add(property.Name, v); } - JObject obj = (JObject) token; + return dictionary; + } + + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + Dictionary dictionary = DictionarySerialization(value!); + + if (value.GetType() != typeof(Entity)) + { + dictionary.Remove("Entity"); + } switch (value) { - case Entity e: - obj = ProcessEntity(obj, e); + case Entity entity: + ProcessEntity(dictionary, entity); + break; + + case Trait trait: + ProcessTrait(dictionary, trait); break; } - obj.WriteTo(writer); - } + writer.WriteStartObject(); - private JObject ProcessEntity(JObject obj, Entity e) - { - List traitIds = e.Traits.Select(t => t.Id).ToList(); - List traits = _context.Traits.Where(t => traitIds.Contains(t.Id)).Include(t => t.Entity).ToList(); - - foreach (JToken item in (JArray) obj.SelectToken("Traits")!) + foreach (KeyValuePair keyValuePair in dictionary) { - JObject o = (JObject) item; - int id = o.SelectToken("Id")!.ToObject(); - - o.Add("Name", traits.FirstOrDefault(t => t.Id == id)!.Entity.Name); + writer.WritePropertyName(keyValuePair.Key, true); + serializer.Serialize(writer, keyValuePair.Value); } - obj.Remove("Description"); - - return obj; + writer.WriteEndObject(); + } + + private void ProcessTrait(Dictionary dictionary, Trait trait) + { + string name = trait.Entity?.Name ?? _context.Entities.Where(e => e.Id == trait.Id).Select(e => e.Name).FirstOrDefault()!; + + dictionary.Remove("Entity"); + dictionary.Remove("Entities"); + dictionary.Add("Name", name); + } + + private void ProcessEntity(Dictionary dictionary, Entity entity) + { + dictionary.Remove("Description"); } public override bool CanRead => false; diff --git a/Pf2eViewer/MainViewForm.Designer.cs b/Pf2eViewer/MainViewForm.Designer.cs index 103d5aa..9b7a51a 100644 --- a/Pf2eViewer/MainViewForm.Designer.cs +++ b/Pf2eViewer/MainViewForm.Designer.cs @@ -32,6 +32,13 @@ partial class MainViewForm InnerSplitContainer = new SplitContainer(); TypesListBox = new ListBox(); EntityGridView = new DataGridView(); + PaginationPagel = new Panel(); + PageTextBox = new TextBox(); + PaginationLabel = new TextBox(); + LastPageButton = new Button(); + NextPageButton = new Button(); + PrevPageButton = new Button(); + FirstPageButton = new Button(); ResultSplitContainer = new SplitContainer(); EntityTreeView = new TreeView(); DescriptionTextBox = new TextBox(); @@ -44,6 +51,7 @@ partial class MainViewForm InnerSplitContainer.Panel2.SuspendLayout(); InnerSplitContainer.SuspendLayout(); ((System.ComponentModel.ISupportInitialize) EntityGridView).BeginInit(); + PaginationPagel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize) ResultSplitContainer).BeginInit(); ResultSplitContainer.Panel1.SuspendLayout(); ResultSplitContainer.Panel2.SuspendLayout(); @@ -66,7 +74,7 @@ partial class MainViewForm RootSplitContainer.Panel2.Controls.Add(ResultSplitContainer); RootSplitContainer.Panel2MinSize = 400; RootSplitContainer.Size = new Size(1181, 688); - RootSplitContainer.SplitterDistance = 200; + RootSplitContainer.SplitterDistance = 254; RootSplitContainer.TabIndex = 0; // // InnerSplitContainer @@ -83,8 +91,9 @@ partial class MainViewForm // InnerSplitContainer.Panel2 // InnerSplitContainer.Panel2.Controls.Add(EntityGridView); - InnerSplitContainer.Size = new Size(200, 688); - InnerSplitContainer.SplitterDistance = 274; + InnerSplitContainer.Panel2.Controls.Add(PaginationPagel); + InnerSplitContainer.Size = new Size(254, 688); + InnerSplitContainer.SplitterDistance = 102; InnerSplitContainer.TabIndex = 1; // // TypesListBox @@ -94,7 +103,7 @@ partial class MainViewForm TypesListBox.ItemHeight = 15; TypesListBox.Location = new Point(0, 0); TypesListBox.Name = "TypesListBox"; - TypesListBox.Size = new Size(200, 274); + TypesListBox.Size = new Size(254, 102); TypesListBox.TabIndex = 0; TypesListBox.SelectedIndexChanged += TypesListBox_SelectedIndexChanged; // @@ -105,17 +114,96 @@ partial class MainViewForm EntityGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; EntityGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; EntityGridView.Dock = DockStyle.Fill; - EntityGridView.Location = new Point(0, 0); + EntityGridView.Location = new Point(0, 53); EntityGridView.MultiSelect = false; EntityGridView.Name = "EntityGridView"; EntityGridView.ReadOnly = true; EntityGridView.RowHeadersVisible = false; EntityGridView.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders; EntityGridView.ShowEditingIcon = false; - EntityGridView.Size = new Size(200, 410); + EntityGridView.Size = new Size(254, 529); EntityGridView.TabIndex = 0; EntityGridView.RowStateChanged += EntityGridView_RowStateChanged; // + // PaginationPagel + // + PaginationPagel.Controls.Add(PageTextBox); + PaginationPagel.Controls.Add(PaginationLabel); + PaginationPagel.Controls.Add(LastPageButton); + PaginationPagel.Controls.Add(NextPageButton); + PaginationPagel.Controls.Add(PrevPageButton); + PaginationPagel.Controls.Add(FirstPageButton); + PaginationPagel.Dock = DockStyle.Top; + PaginationPagel.Location = new Point(0, 0); + PaginationPagel.Name = "PaginationPagel"; + PaginationPagel.Size = new Size(254, 53); + PaginationPagel.TabIndex = 1; + // + // PageTextBox + // + PageTextBox.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + PageTextBox.Location = new Point(81, 4); + PageTextBox.Name = "PageTextBox"; + PageTextBox.Size = new Size(92, 23); + PageTextBox.TabIndex = 8; + PageTextBox.Text = "1"; + PageTextBox.TextAlign = HorizontalAlignment.Center; + PageTextBox.KeyPress += PageTextBox_KeyPress; + // + // PaginationLabel + // + PaginationLabel.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + PaginationLabel.BorderStyle = BorderStyle.None; + PaginationLabel.Location = new Point(3, 31); + PaginationLabel.Name = "PaginationLabel"; + PaginationLabel.ReadOnly = true; + PaginationLabel.Size = new Size(248, 16); + PaginationLabel.TabIndex = 7; + PaginationLabel.Text = "Всего"; + PaginationLabel.TextAlign = HorizontalAlignment.Center; + // + // LastPageButton + // + LastPageButton.Anchor = AnchorStyles.Top | AnchorStyles.Right; + LastPageButton.Location = new Point(218, 3); + LastPageButton.Name = "LastPageButton"; + LastPageButton.Size = new Size(33, 23); + LastPageButton.TabIndex = 3; + LastPageButton.Text = ">>"; + LastPageButton.UseVisualStyleBackColor = true; + LastPageButton.Click += LastPageButton_Click; + // + // NextPageButton + // + NextPageButton.Anchor = AnchorStyles.Top | AnchorStyles.Right; + NextPageButton.Location = new Point(179, 3); + NextPageButton.Name = "NextPageButton"; + NextPageButton.Size = new Size(33, 23); + NextPageButton.TabIndex = 2; + NextPageButton.Text = ">"; + NextPageButton.UseVisualStyleBackColor = true; + NextPageButton.Click += NextPageButton_Click; + // + // PrevPageButton + // + PrevPageButton.Location = new Point(42, 3); + PrevPageButton.Name = "PrevPageButton"; + PrevPageButton.Size = new Size(33, 23); + PrevPageButton.TabIndex = 1; + PrevPageButton.Text = "<"; + PrevPageButton.UseVisualStyleBackColor = true; + PrevPageButton.Click += PrevPageButton_Click; + // + // FirstPageButton + // + FirstPageButton.Location = new Point(3, 3); + FirstPageButton.Name = "FirstPageButton"; + FirstPageButton.Size = new Size(33, 23); + FirstPageButton.TabIndex = 0; + FirstPageButton.Text = "<<"; + FirstPageButton.UseVisualStyleBackColor = true; + FirstPageButton.Click += FirstPageButton_Click; + // // ResultSplitContainer // ResultSplitContainer.Dock = DockStyle.Fill; @@ -129,8 +217,8 @@ partial class MainViewForm // ResultSplitContainer.Panel2 // ResultSplitContainer.Panel2.Controls.Add(DescriptionTextBox); - ResultSplitContainer.Size = new Size(977, 688); - ResultSplitContainer.SplitterDistance = 495; + ResultSplitContainer.Size = new Size(923, 688); + ResultSplitContainer.SplitterDistance = 467; ResultSplitContainer.TabIndex = 1; // // EntityTreeView @@ -138,7 +226,7 @@ partial class MainViewForm EntityTreeView.Dock = DockStyle.Fill; EntityTreeView.Location = new Point(0, 0); EntityTreeView.Name = "EntityTreeView"; - EntityTreeView.Size = new Size(495, 688); + EntityTreeView.Size = new Size(467, 688); EntityTreeView.TabIndex = 0; // // DescriptionTextBox @@ -149,7 +237,7 @@ partial class MainViewForm DescriptionTextBox.Name = "DescriptionTextBox"; DescriptionTextBox.ReadOnly = true; DescriptionTextBox.ScrollBars = ScrollBars.Vertical; - DescriptionTextBox.Size = new Size(478, 688); + DescriptionTextBox.Size = new Size(452, 688); DescriptionTextBox.TabIndex = 0; // // MainViewForm @@ -169,6 +257,8 @@ partial class MainViewForm ((System.ComponentModel.ISupportInitialize) InnerSplitContainer).EndInit(); InnerSplitContainer.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize) EntityGridView).EndInit(); + PaginationPagel.ResumeLayout(false); + PaginationPagel.PerformLayout(); ResultSplitContainer.Panel1.ResumeLayout(false); ResultSplitContainer.Panel2.ResumeLayout(false); ResultSplitContainer.Panel2.PerformLayout(); @@ -186,4 +276,11 @@ partial class MainViewForm private TreeView EntityTreeView; private SplitContainer ResultSplitContainer; private TextBox DescriptionTextBox; + private Panel PaginationPagel; + private Button LastPageButton; + private Button NextPageButton; + private Button PrevPageButton; + private Button FirstPageButton; + private TextBox PageTextBox; + private TextBox PaginationLabel; } diff --git a/Pf2eViewer/MainViewForm.cs b/Pf2eViewer/MainViewForm.cs index 5dfc237..11f4e40 100644 --- a/Pf2eViewer/MainViewForm.cs +++ b/Pf2eViewer/MainViewForm.cs @@ -1,6 +1,8 @@ using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; using Pf2eModel; using Pf2eModel.Model; using Pf2eModel.Model.Base; @@ -29,10 +31,32 @@ public partial class MainViewForm : Form TypesListBox.SelectedIndex = 0; } - private void TypesListBox_SelectedIndexChanged(object sender, EventArgs _) + public const int PerPage = 20; + + public int Count; + + public int Page { - if (TypesListBox.SelectedItem is null) + get => Convert.ToInt32(PageTextBox.Text); + set { + PageTextBox.Text = value.ToString(); + UpdateTreeView(); + } + } + + public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage)); + + + private void UpdateTreeView() + { + EntityType type = (EntityType) TypesListBox.SelectedItem!; + + Count = Context.Entities.Count(e => e.Type == type); + + if (Page < 1 || Page > MaxPage) + { + Page = Math.Min(MaxPage, Math.Max(1, Page)); return; } @@ -40,15 +64,71 @@ public partial class MainViewForm : Form .Where(e => e.Type == (EntityType) TypesListBox.SelectedItem) .OrderBy(e => e.Id) .Include(e => e.Sources) + .ThenInclude(s => s.Source) .Include(e => e.Aliases) .Include(e => e.Keywords) .Include(e => e.Externals) + .ThenInclude(x => x.Target) .Include(e => e.Traits) + .ThenInclude(t => t.Entity) .Select(e => new EntityGridRow(e)) + .Skip((Page - 1) * PerPage) + .Take(PerPage) .ToList(); + EntityGridView.Rows[0].Selected = true; + + PaginationLabel.Text = $"Всего: {Count} объектов, {MaxPage} страниц"; + + FirstPageButton.Enabled = Page > 1; + PrevPageButton.Enabled = Page > 1; + NextPageButton.Enabled = Page < MaxPage; + LastPageButton.Enabled = Page < MaxPage; } + private void TypesListBox_SelectedIndexChanged(object sender, EventArgs _) + { + if (TypesListBox.SelectedItem is null) + { + return; + } + + Page = 1; + } + private void FirstPageButton_Click(object sender, EventArgs e) + { + Page = 1; + } + + private void PrevPageButton_Click(object sender, EventArgs e) + { + Page--; + } + + private void NextPageButton_Click(object sender, EventArgs e) + { + Page++; + } + + private void LastPageButton_Click(object sender, EventArgs e) + { + Page = MaxPage; + } + + private void PageTextBox_KeyPress(object sender, KeyPressEventArgs e) + { + if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar)) + { + e.Handled = true; + } + + if (e.KeyChar == (char) Keys.Return) + { + UpdateTreeView(); + } + } + + private void EntityGridView_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e) { int rowsCount = EntityGridView.SelectedRows.Count; @@ -72,18 +152,31 @@ public partial class MainViewForm : Form { JsonSerializerSettings settings = new() { - Converters = [new EntityConverter()] + Converters = [ + new EntityConverter(), + new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() }, + ] }; string result = JsonConvert.SerializeObject(entity, settings); Type type = EntityOfTypeAttribute.Resolve(entity); - string subResult = JsonConvert.SerializeObject(Context.Find(type, entity.Id), settings); + string subResult = JsonConvert.SerializeObject(ResolveEntity(entity), settings); return result[..^1] + $",\"SubEntity\":{subResult}}}"; } + public IEntity? ResolveEntity(Entity entity) + { + switch (entity.Type) + { + case EntityType.Ancestry: + return Context.Ancestries + .FirstOrDefault(e => e.Id == entity.Id); + } + return null; + } public static void JsonToTreeview(TreeView treeView, string jsonString, string rootName) { @@ -92,11 +185,11 @@ public partial class MainViewForm : Form : JObject.Parse(jsonString); treeView.Nodes.Clear(); - foreach (TreeNode node in Element2TreeNode(json, rootName).Nodes) { treeView.Nodes.Add(node); } + treeView.ExpandAll(); } private static TreeNode Element2TreeNode(object element, string text = "") @@ -156,4 +249,5 @@ public partial class MainViewForm : Form public Entity GetEntity() => _e; } + }