From 5357add72ded3d72dd8f8b482b3661e836850292 Mon Sep 17 00:00:00 2001 From: safronov Date: Fri, 11 Feb 2022 20:17:44 +0300 Subject: [PATCH] Refactoring for production summary group support --- .../ProductionSummaryFlatHierarchy.cs | 18 +++ .../ProductionSummaryView.cs | 21 ++-- .../ProductionTableFlatHierarchy.cs | 94 +++++++++------- YAFCmodel/Model/ProductionSummary.cs | 104 ++++++++++++------ 4 files changed, 157 insertions(+), 80 deletions(-) create mode 100644 YAFC/Workspace/ProductionSummary/ProductionSummaryFlatHierarchy.cs rename YAFC/Workspace/{ => ProductionSummary}/ProductionSummaryView.cs (95%) diff --git a/YAFC/Workspace/ProductionSummary/ProductionSummaryFlatHierarchy.cs b/YAFC/Workspace/ProductionSummary/ProductionSummaryFlatHierarchy.cs new file mode 100644 index 00000000..7d253a8a --- /dev/null +++ b/YAFC/Workspace/ProductionSummary/ProductionSummaryFlatHierarchy.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using YAFC.Model; +using YAFC.UI; + +namespace YAFC +{ + public class ProductionSummaryFlatHierarchy : FlatHierarchy + { + public ProductionSummaryFlatHierarchy(DataGrid grid, Action drawTableHeader) : base(grid, drawTableHeader) {} + protected override bool Expanded(ProductionSummaryGroup group) => group.expanded; + protected override ProductionSummaryGroup Subgroup(ProductionSummaryEntry row) => row.group; + protected override List Elements(ProductionSummaryGroup @group) => group.list; + protected override void SetOwner(ProductionSummaryEntry row, ProductionSummaryGroup newOwner) => row.SetOwner(newOwner); + protected override bool Filter(ProductionSummaryEntry row) => row.filterMatch; + protected override string emptyGroupMessage => "This is an empty group"; + } +} \ No newline at end of file diff --git a/YAFC/Workspace/ProductionSummaryView.cs b/YAFC/Workspace/ProductionSummary/ProductionSummaryView.cs similarity index 95% rename from YAFC/Workspace/ProductionSummaryView.cs rename to YAFC/Workspace/ProductionSummary/ProductionSummaryView.cs index 68d46936..67a7e432 100644 --- a/YAFC/Workspace/ProductionSummaryView.cs +++ b/YAFC/Workspace/ProductionSummary/ProductionSummaryView.cs @@ -10,6 +10,7 @@ namespace YAFC public class ProductionSummaryView : ProjectPageView { private readonly DataGrid grid; + private readonly ProductionSummaryFlatHierarchy flatHierarchy; private readonly SearchableList pagesDropdown; private SearchQuery searchQuery; private Goods filteredGoods; @@ -23,6 +24,7 @@ public ProductionSummaryView() firstColumn = new SummaryColumn(this); lastColumn = new RestGoodsColumn(this); grid = new DataGrid(firstColumn, lastColumn) {headerHeight = 4.2f}; + flatHierarchy = new ProductionSummaryFlatHierarchy(grid, null); pagesDropdown = new SearchableList(30f, new Vector2(20f, 2f), PagesDropdownDrawer, PagesDropdownFilter); } @@ -65,7 +67,7 @@ public override void BuildElement(ImGui gui, ProductionSummaryEntry entry) if (tgui.BuildButton("Go to page") && tgui.CloseDropdown()) MainScreen.Instance.SetActivePage(entry.page.page); if (tgui.BuildRedButton("Remove") && tgui.CloseDropdown()) - view.model.RecordUndo().list.Remove(entry); + view.model.group.RecordUndo().list.Remove(entry); }); using (gui.EnterFixedPositioning(3f, 2f, default)) @@ -171,7 +173,7 @@ private void PagesDropdownDrawer(ImGui gui, ProjectPage element, int index) if (gui.BuildButton(gui.lastRect, SchemeColor.BackgroundAlt, SchemeColor.Background)) { - model.RecordUndo().list.Add(new ProductionSummaryEntry(model, new PageReference(element))); + model.group.RecordUndo().list.Add(new ProductionSummaryEntry(model.group, new PageReference(element))); } } @@ -245,16 +247,13 @@ protected override void BuildContent(ImGui gui) if (model == null) return; - var hasReorder = grid.BuildContent(gui, model.list, out var reorder, out var rect, filteredGoodsFilter); + flatHierarchy.Build(gui); gui.SetMinWidth(grid.width); - - if (hasReorder) - model.RecordUndo(true).list.MoveListElement(reorder.from, reorder.to); - + gui.AllocateSpacing(1f); using (gui.EnterGroup(new Padding(1))) { - if (model.list.Count == 0) + if (model.group.list.Count == 0) gui.BuildText("Add your existing sheets here to keep track of what you have in your base and to see what shortages you may have"); else gui.BuildText("List of goods produced/consumed by added blocks. Click on any of these to add it to (or remove it from) the table."); using (var igrid = gui.EnterInlineGrid(3f, 1f)) @@ -270,6 +269,12 @@ protected override void BuildContent(ImGui gui) if (gui.isBuilding) gui.DrawRectangle(gui.lastRect, SchemeColor.Background, RectangleBorder.Thin); } + + public override void Rebuild(bool visuaOnly = false) + { + flatHierarchy.SetData(model.group); + base.Rebuild(visuaOnly); + } private void AddProductionTableDropdown(ImGui gui) { diff --git a/YAFC/Workspace/ProductionTable/ProductionTableFlatHierarchy.cs b/YAFC/Workspace/ProductionTable/ProductionTableFlatHierarchy.cs index 3483f4fb..7b161079 100644 --- a/YAFC/Workspace/ProductionTable/ProductionTableFlatHierarchy.cs +++ b/YAFC/Workspace/ProductionTable/ProductionTableFlatHierarchy.cs @@ -1,35 +1,41 @@ using System; using System.Collections.Generic; using YAFC.Model; -using YAFC.Parser; using YAFC.UI; namespace YAFC { - public class ProductionTableFlatHierarchy + public abstract class FlatHierarchy where TRow : ModelObject where TGroup:ModelObject { - private readonly DataGrid grid; - private readonly List flatRecipes = new List(); - private readonly List flatGroups = new List(); - private RecipeRow draggingRecipe; - private ProductionTable root; + private readonly DataGrid grid; + private readonly List flatRecipes = new List(); + private readonly List flatGroups = new List(); + private TRow draggingRecipe; + private TGroup root; private bool rebuildRequired; - private readonly Action drawTableHeader; + private readonly Action drawTableHeader; - public ProductionTableFlatHierarchy(DataGrid grid, Action drawTableHeader) + protected abstract bool Expanded(TGroup group); + protected abstract TGroup Subgroup(TRow row); + protected abstract List Elements(TGroup group); + protected abstract void SetOwner(TRow row, TGroup newOwner); + protected abstract bool Filter(TRow row); + protected abstract string emptyGroupMessage { get; } + + protected FlatHierarchy(DataGrid grid, Action drawTableHeader) { this.grid = grid; this.drawTableHeader = drawTableHeader; } public float width => grid.width; - public void SetData(ProductionTable table) + public void SetData(TGroup table) { root = table; rebuildRequired = true; } - private (ProductionTable, int) FindDragginRecipeParentAndIndex() + private (TGroup, int) FindDragginRecipeParentAndIndex() { var index = flatRecipes.IndexOf(draggingRecipe); if (index == -1) @@ -37,13 +43,14 @@ public void SetData(ProductionTable table) var currentIndex = 0; for (var i = index - 1; i >= 0; i--) { - if (flatRecipes[i] is RecipeRow recipe) + if (flatRecipes[i] is TRow recipe) { - if (recipe.hasVisibleChildren) - return (recipe.subgroup, currentIndex); + var group = Subgroup(recipe); + if (group != null && Expanded(group)) + return (group, currentIndex); } else - i = flatRecipes.LastIndexOf(flatGroups[i].owner as RecipeRow, i); + i = flatRecipes.LastIndexOf(flatGroups[i].owner as TRow, i); currentIndex++; } return (root, currentIndex); @@ -54,15 +61,15 @@ private void ActuallyMoveDraggingRecipe() var (parent, index) = FindDragginRecipeParentAndIndex(); if (parent == null) return; - if (draggingRecipe.owner == parent && parent.recipes[index] == draggingRecipe) + if (draggingRecipe.owner == parent && Elements(parent)[index] == draggingRecipe) return; - draggingRecipe.owner.RecordUndo().recipes.Remove(draggingRecipe); - draggingRecipe.SetOwner(parent); - parent.RecordUndo().recipes.Insert(index, draggingRecipe); + Elements(draggingRecipe.owner.RecordUndo()).Remove(draggingRecipe); + SetOwner(draggingRecipe, parent); + Elements(parent.RecordUndo()).Insert(index, draggingRecipe); } - private void MoveFlatHierarchy(RecipeRow from, RecipeRow to) + private void MoveFlatHierarchy(TRow from, TRow to) { draggingRecipe = from; var indexFrom = flatRecipes.IndexOf(from); @@ -71,7 +78,7 @@ private void MoveFlatHierarchy(RecipeRow from, RecipeRow to) flatGroups.MoveListElementIndex(indexFrom, indexTo); } - private void MoveFlatHierarchy(RecipeRow from, ProductionTable to) + private void MoveFlatHierarchy(TRow from, TGroup to) { draggingRecipe = from; var indexFrom = flatRecipes.IndexOf(from); @@ -104,7 +111,7 @@ public void Build(ImGui gui) var item = flatGroups[i]; if (recipe != null) { - if (!recipe.searchMatch) + if (!Filter(recipe)) { if (item != null) i = flatGroups.LastIndexOf(item); @@ -123,23 +130,22 @@ public void Build(ImGui gui) if (item == null && gui.InitiateDrag(rect, rect, recipe, bgColor)) draggingRecipe = recipe; else if (gui.ConsumeDrag(rect.Center, recipe)) - MoveFlatHierarchy(gui.GetDraggingObject(), recipe); + MoveFlatHierarchy(gui.GetDraggingObject(), recipe); if (item != null) { - if (item.recipes.Count == 0) + if (Elements(item).Count == 0) { using (gui.EnterGroup(new Padding(0.5f+depWidth, 0.5f, 0.5f, 0.5f))) { - gui.BuildText("This is a nested group. You can drag&drop recipes here. Nested groups can have its own linked materials", wrap:true); - if (gui.BuildLink("Delete empty nested group")) - { - recipe.RecordUndo().subgroup = null; - rebuildRequired = true; - } + gui.BuildText(emptyGroupMessage, wrap:true); } } - using (gui.EnterGroup(new Padding(0.5f+depWidth, 0.5f, 0.5f, 0.5f))) - drawTableHeader(gui, item); + + if (drawTableHeader != null) + { + using (gui.EnterGroup(new Padding(0.5f+depWidth, 0.5f, 0.5f, 0.5f))) + drawTableHeader(gui, item); + } } } else @@ -167,17 +173,18 @@ private void Rebuild() rebuildRequired = false; } - private void BuildFlatHierarchy(ProductionTable table) + private void BuildFlatHierarchy(TGroup table) { - foreach (var recipe in table.recipes) + foreach (var recipe in Elements(table)) { flatRecipes.Add(recipe); - if (recipe.hasVisibleChildren) + var sub = Subgroup(recipe); + if (sub != null && Expanded(sub)) { - flatGroups.Add(recipe.subgroup); - BuildFlatHierarchy(recipe.subgroup); + flatGroups.Add(sub); + BuildFlatHierarchy(sub); flatRecipes.Add(null); - flatGroups.Add(recipe.subgroup); + flatGroups.Add(sub); } else flatGroups.Add(null); } @@ -188,4 +195,15 @@ public void BuildHeader(ImGui gui) grid.BuildHeader(gui); } } + + public class ProductionTableFlatHierarchy : FlatHierarchy + { + public ProductionTableFlatHierarchy(DataGrid grid, Action drawTableHeader) : base(grid, drawTableHeader) {} + protected override bool Expanded(ProductionTable group) => group.expanded; + protected override ProductionTable Subgroup(RecipeRow row) => row.subgroup; + protected override List Elements(ProductionTable @group) => group.recipes; + protected override void SetOwner(RecipeRow row, ProductionTable newOwner) => row.SetOwner(newOwner); + protected override bool Filter(RecipeRow row) => row.searchMatch; + protected override string emptyGroupMessage => "This is a nested group. You can drag&drop recipes here. Nested groups can have its own linked materials"; + } } \ No newline at end of file diff --git a/YAFCmodel/Model/ProductionSummary.cs b/YAFCmodel/Model/ProductionSummary.cs index 522e38ba..98cc0b68 100644 --- a/YAFCmodel/Model/ProductionSummary.cs +++ b/YAFCmodel/Model/ProductionSummary.cs @@ -5,18 +5,54 @@ namespace YAFC.Model { - public class ProductionSummaryEntry : ModelObject + public class ProductionSummaryGroup : ModelObject { - public ProductionSummaryEntry(ProductionSummary owner, PageReference page) : base(owner) + public ProductionSummaryGroup(ModelObject owner) : base(owner) {} + public List list { get; } = new List(); + public bool expanded { get; set; } + + public void CollectSolvingTasks(List listToFill) + { + foreach (var element in list) + { + var solutionTask = element.SolveIfNessessary(); + if (solutionTask != null) + listToFill.Add(solutionTask); + if (element.group != null) + element.group.CollectSolvingTasks(listToFill); + } + } + + public void Solve(Dictionary totalFlow) + { + foreach (var element in list) + element.RefreshFlow(); + totalFlow.Clear(); + foreach (var row in list) + { + foreach (var (item, amount) in row.flow) + { + totalFlow.TryGetValue(item, out var prev); + totalFlow[item] = prev + amount; + } + } + } + } + + public class ProductionSummaryEntry : ModelObject + { + public ProductionSummaryEntry(ProductionSummaryGroup owner, PageReference page) : base(owner) { this.page = page ?? throw new ArgumentNullException(nameof(page), "Page reference does not exist"); } public float multiplier { get; set; } = 1; public PageReference page { get; } + public ProductionSummaryGroup group { get; set; } [SkipSerialization] public Dictionary flow { get; } = new Dictionary(); private bool needRefreshFlow = true; + public bool filterMatch = true; public Icon icon { @@ -60,23 +96,36 @@ public void RefreshFlow() { if (!needRefreshFlow) return; + needRefreshFlow = false; flow.Clear(); - var spage = page?.page?.content as ProductionTable; - if (spage == null) - return; - - foreach (var flowEntry in spage.flow) + if (group != null) { - if (flowEntry.amount != 0) - flow[flowEntry.goods] = flowEntry.amount * multiplier; + group.Solve(flow); } + else + { + var spage = page?.page?.content as ProductionTable; + if (spage == null) + return; - foreach (var link in spage.links) - if (link.amount != 0) + foreach (var flowEntry in spage.flow) { - flow.TryGetValue(link.goods, out var prevValue); - flow[link.goods] = prevValue + link.amount * multiplier; + if (flowEntry.amount != 0) + flow[flowEntry.goods] = flowEntry.amount * multiplier; } + + foreach (var link in spage.links) + if (link.amount != 0) + { + flow.TryGetValue(link.goods, out var prevValue); + flow[link.goods] = prevValue + link.amount * multiplier; + } + } + } + + public void SetOwner(ProductionSummaryGroup newOwner) + { + owner = newOwner; } } @@ -91,8 +140,11 @@ public ProductionSummaryColumn(ProductionSummary owner, Goods goods) : base(owne public class ProductionSummary : ProjectPageContents, IComparer<(Goods goods, float amount)> { - public ProductionSummary(ModelObject page) : base(page) {} - public List list { get; } = new List(); + public ProductionSummary(ModelObject page) : base(page) + { + group = new ProductionSummaryGroup(this); + } + public ProductionSummaryGroup group { get; } public List columns { get; } = new List(); [SkipSerialization] public List<(Goods goods, float amount)> sortedFlow { get; } = new List<(Goods goods, float amount)>(); @@ -105,32 +157,16 @@ public override void InitNew() base.InitNew(); } - public float GetTotalFlow(Goods goods) => totalFlow == null ? 0 : totalFlow.TryGetValue(goods, out var amount) ? amount : 0; + public float GetTotalFlow(Goods goods) => totalFlow.TryGetValue(goods, out var amount) ? amount : 0; public override async Task Solve(ProjectPage page) { var taskList = new List(); - foreach (var element in list) - { - var solutionTask = element.SolveIfNessessary(); - if (solutionTask != null) - taskList.Add(solutionTask); - } - + group.CollectSolvingTasks(taskList); if (taskList.Count > 0) await Task.WhenAll(taskList); - foreach (var element in list) - element.RefreshFlow(); - totalFlow.Clear(); columnsExist.Clear(); - foreach (var row in list) - { - foreach (var (item, amount) in row.flow) - { - totalFlow.TryGetValue(item, out var prev); - totalFlow[item] = prev + amount; - } - } + group.Solve(totalFlow); foreach (var column in columns) columnsExist.Add(column.goods);