From 6f536825794f069c4d33248d36903a2a610f1f71 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Wed, 29 Dec 2021 16:01:39 +0100 Subject: [PATCH 01/16] Add Summary tab (with an empty page) --- YAFC/Windows/MainScreen.cs | 80 ++++++++++++++++-------- YAFC/Windows/ProjectPageSettingsPanel.cs | 18 +++--- YAFC/Workspace/SummaryView.cs | 26 ++++++++ YAFCmodel/Model/Summary.cs | 15 +++++ 4 files changed, 104 insertions(+), 35 deletions(-) create mode 100644 YAFC/Workspace/SummaryView.cs create mode 100644 YAFCmodel/Model/Summary.cs diff --git a/YAFC/Windows/MainScreen.cs b/YAFC/Windows/MainScreen.cs index 45668085..ee79e4cb 100644 --- a/YAFC/Windows/MainScreen.cs +++ b/YAFC/Windows/MainScreen.cs @@ -14,6 +14,8 @@ namespace YAFC { public class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string)> { + public static readonly Guid SummaryGuid = Guid.Parse("9bdea333-4be2-4be3-b708-b36a64672a40"); + public static MainScreen Instance { get; private set; } private readonly ObjectTooltip objectTooltip = new ObjectTooltip(); private readonly List pseudoScreens = new List(); @@ -30,7 +32,7 @@ public class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string) private ProjectPage _secondaryPage; public ProjectPage secondaryPage => _secondaryPage; private ProjectPageView secondaryPageView; - + private bool analysisUpdatePending; private SearchQuery pageSearch; private SearchQuery pageListSearch; @@ -46,11 +48,12 @@ public MainScreen(int display, Project project) : base(default) RegisterPageView(new ProductionTableView()); RegisterPageView(new AutoPlannerView()); RegisterPageView(new ProductionSummaryView()); - searchGui = new ImGui(BuildSearch, new Padding(1f)) {boxShadow = RectangleBorder.Thin, boxColor = SchemeColor.Background}; + RegisterPageView(new SummaryView()); + searchGui = new ImGui(BuildSearch, new Padding(1f)) { boxShadow = RectangleBorder.Thin, boxColor = SchemeColor.Background }; Instance = this; tabBar = new MainScreenTabBar(this); - allPages = new VirtualScrollList(30, new Vector2(0f, 2f), BuildPage, collapsible:true); - Create("Yet Another Factorio Calculator v"+YafcLib.version, display); + allPages = new VirtualScrollList(30, new Vector2(0f, 2f), BuildPage, collapsible: true); + Create("Yet Another Factorio Calculator v" + YafcLib.version, display); SetProject(project); } @@ -71,13 +74,16 @@ private void SetProject(Project project) if (project.pages.Count == 0) { + var firstPage = new ProjectPage(project, typeof(ProductionTable)); project.pages.Add(firstPage); } if (project.displayPages.Count == 0) + { project.displayPages.Add(project.pages[0].guid); - + } + SetActivePage(project.FindPage(project.displayPages[0])); project.metaInfoChanged += ProjectOnMetaInfoChanged; project.settings.changed += ProjectSettingsChanged; @@ -109,9 +115,9 @@ private void BuildPage(ImGui gui, ProjectPage element, int index) { if (element.icon != null) gui.BuildIcon(element.icon.icon); - gui.RemainingRow().BuildText(element.name, color:element.visible ? SchemeColor.BackgroundText : SchemeColor.BackgroundTextFaint); + gui.RemainingRow().BuildText(element.name, color: element.visible ? SchemeColor.BackgroundText : SchemeColor.BackgroundTextFaint); } - var evt = gui.BuildButton(gui.lastRect, SchemeColor.PureBackground, SchemeColor.Grey, button:0); + var evt = gui.BuildButton(gui.lastRect, SchemeColor.PureBackground, SchemeColor.Grey, button: 0); if (evt) { if (gui.actionParameter == SDL.SDL_BUTTON_MIDDLE) @@ -178,7 +184,7 @@ public void RegisterPageView(ProjectPageView pageView) where T : ProjectPageC { registeredPageViews[typeof(T)] = pageView; } - + public void RebuildProjectView() { rootGui.MarkEverythingForRebuild(); @@ -189,7 +195,7 @@ public void RebuildProjectView() } protected override void BuildContent(ImGui gui) - { + { if (pseudoScreens.Count > 0) { var top = pseudoScreens[0]; @@ -270,12 +276,13 @@ private void BuildPage(ImGui gui) vsize.Y /= 2f; _activePageView.Build(gui, vsize); secondaryPageView.Build(gui, vsize); - } else + } + else _activePageView.Build(gui, pageVisibleSize); if (pageSearch.query != null && gui.isBuilding) { var searchSize = searchGui.CalculateState(30, gui.pixelsPerUnit); - gui.DrawPanel(new Rect(pageVisibleSize.X-searchSize.X, usedHeaderSpace, searchSize.X, searchSize.Y), searchGui); + gui.DrawPanel(new Rect(pageVisibleSize.X - searchSize.X, usedHeaderSpace, searchSize.X, searchSize.Y), searchGui); } } else @@ -287,10 +294,10 @@ private void BuildPage(ImGui gui) } } } - + public ProjectPage AddProjectPage(string name, FactorioObject icon, Type contentType, bool setActive, bool initNew) { - var page = new ProjectPage(project, contentType) {name = name, icon = icon}; + var page = new ProjectPage(project, contentType) { name = name, icon = icon }; if (initNew) page.content.InitNew(); project.RecordUndo().pages.Add(page); @@ -320,7 +327,7 @@ private void MissingPagesDropdown(ImGui gui) } allPages.Build(gui); } - + public void BuildSubHeader(ImGui gui, string text) { using (gui.EnterGroup(ObjectTooltip.contentPadding)) @@ -370,7 +377,7 @@ private void BuildSearch(ImGui gui) private void SettingsDropdown(ImGui gui) { gui.boxColor = SchemeColor.Background; - if (gui.BuildContextMenuButton("Undo", "Ctrl+" +ImGuiUtils.ScanToString(SDL.SDL_Scancode.SDL_SCANCODE_Z)) && gui.CloseDropdown()) + if (gui.BuildContextMenuButton("Undo", "Ctrl+" + ImGuiUtils.ScanToString(SDL.SDL_Scancode.SDL_SCANCODE_Z)) && gui.CloseDropdown()) project.undo.PerformUndo(); if (gui.BuildContextMenuButton("Save", "Ctrl+" + ImGuiUtils.ScanToString(SDL.SDL_Scancode.SDL_SCANCODE_S)) && gui.CloseDropdown()) SaveProject().CaptureException(); @@ -389,22 +396,25 @@ private void SettingsDropdown(ImGui gui) if (gui.BuildContextMenuButton("Preferences") && gui.CloseDropdown()) PreferencesScreen.Show(); + if (gui.BuildContextMenuButton("Summary") && gui.CloseDropdown()) + ShowSummaryTab(); + if (gui.BuildContextMenuButton("Never Enough Items Explorer", "Ctrl+" + ImGuiUtils.ScanToString(SDL.SDL_Scancode.SDL_SCANCODE_N)) && gui.CloseDropdown()) ShowNeie(); if (gui.BuildContextMenuButton("Dependency Explorer") && gui.CloseDropdown()) SelectObjectPanel.Select(Database.objects.all, "Open Dependency Explorer", DependencyExplorer.Show); - + BuildSubHeader(gui, "Extra"); if (gui.BuildContextMenuButton("Run Factorio")) { var factorioPath = DataUtils.dataPath + "/../bin/x64/factorio"; var args = string.IsNullOrEmpty(DataUtils.modsPath) ? null : "--mod-directory \"" + DataUtils.modsPath + "\""; - Process.Start(new ProcessStartInfo(factorioPath, args) {UseShellExecute = true}); + Process.Start(new ProcessStartInfo(factorioPath, args) { UseShellExecute = true }); gui.CloseDropdown(); } - + if (gui.BuildContextMenuButton("Check for updates") && gui.CloseDropdown()) DoCheckForUpdates(); @@ -501,6 +511,22 @@ public bool ShowPseudoScreen(PseudoScreen screen) return true; } + public void ShowSummaryTab() + { + + var summaryPage = project.FindPage(SummaryGuid); + if (summaryPage == null) + { + + summaryPage = new ProjectPage(project, typeof(Summary), SummaryGuid); + summaryPage.name = "Summary"; + project.pages.Add(summaryPage); + // project.displayPages.Add(summaryPage.guid); + } + + SetActivePage(summaryPage); + } + public void ClosePseudoScreen(PseudoScreen screen) { pseudoScreens.Remove(screen); @@ -523,12 +549,14 @@ public bool KeyDown(SDL.SDL_Keysym key) else project.undo.PerformUndo(); _activePageView?.Rebuild(false); secondaryPageView?.Rebuild(false); - } else if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_Y) + } + else if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_Y) { project.undo.PerformRedo(); _activePageView?.Rebuild(false); secondaryPageView?.Rebuild(false); - } else if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_N) + } + else if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_N) ShowNeie(); else if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_F) ShowSearch(); @@ -568,7 +596,7 @@ private Task SaveProject() return SaveProjectAs(); } - + private async void LoadProjectLight() { if (project.unsavedChangesCount > 0 && !await ConfirmUnsavedChanges()) @@ -604,9 +632,9 @@ private async void LoadProjectHeavy() public bool TextInput(string input) => true; public bool KeyUp(SDL.SDL_Keysym key) => true; - public void FocusChanged(bool focused) {} + public void FocusChanged(bool focused) { } private new void MainRender() => base.MainRender(); - + private class FadeDrawer : IRenderable { private SDL.SDL_Rect srcRect; @@ -621,8 +649,8 @@ public void CreateDownscaledImage() Instance.surface.EndRenderToTexture(); for (var i = 0; i < 2; i++) { - var halfSize = new SDL.SDL_Rect() {w = size.w/2, h = size.h/2}; - var halfTexture = Instance.surface.CreateTexture(SDL.SDL_PIXELFORMAT_RGBA8888, (int) SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_TARGET, halfSize.w, halfSize.h); + var halfSize = new SDL.SDL_Rect() { w = size.w / 2, h = size.h / 2 }; + var halfTexture = Instance.surface.CreateTexture(SDL.SDL_PIXELFORMAT_RGBA8888, (int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_TARGET, halfSize.w, halfSize.h); SDL.SDL_SetRenderTarget(renderer, halfTexture.handle); var bgColor = SchemeColor.PureBackground.ToSdlColor(); SDL.SDL_SetRenderDrawColor(renderer, bgColor.r, bgColor.g, bgColor.b, bgColor.a); @@ -661,7 +689,7 @@ public void ShowTooltip(ImGui gui, ProjectPage page, bool isMiddleEdit, Rect rec { pageView.BuildPageTooltip(x, page.content); if (isMiddleEdit) - x.BuildText("Middle mouse button to edit", Font.text, true, color:SchemeColor.BackgroundTextFaint); + x.BuildText("Middle mouse button to edit", Font.text, true, color: SchemeColor.BackgroundTextFaint); }); } } diff --git a/YAFC/Windows/ProjectPageSettingsPanel.cs b/YAFC/Windows/ProjectPageSettingsPanel.cs index 264c3ca5..0bd58eb4 100644 --- a/YAFC/Windows/ProjectPageSettingsPanel.cs +++ b/YAFC/Windows/ProjectPageSettingsPanel.cs @@ -20,7 +20,7 @@ public class ProjectPageSettingsPanel : PseudoScreen private string name; private FactorioObject icon; private Action callback; - + public static void Build(ImGui gui, ref string name, FactorioObject icon, Action setIcon) { gui.BuildTextInput(name, out name, "Input name"); @@ -41,7 +41,7 @@ public static void Show(ProjectPage page, Action callbac Instance.callback = callback; MainScreen.Instance.ShowPseudoScreen(Instance); } - + public override void Build(ImGui gui) { gui.spacing = 3f; @@ -54,13 +54,13 @@ public override void Build(ImGui gui) using (gui.EnterRow(0.5f, RectAllocator.RightRow)) { - if (editingPage == null && gui.BuildButton("Create", active:!string.IsNullOrEmpty(name))) + if (editingPage == null && gui.BuildButton("Create", active: !string.IsNullOrEmpty(name))) { callback?.Invoke(name, icon); Close(); } - if (editingPage != null && gui.BuildButton("OK", active:!string.IsNullOrEmpty(name))) + if (editingPage != null && gui.BuildButton("OK", active: !string.IsNullOrEmpty(name))) { if (editingPage.name != name || editingPage.icon != icon) { @@ -73,7 +73,7 @@ public override void Build(ImGui gui) if (gui.BuildButton("Cancel", SchemeColor.Grey)) Close(); - if (editingPage != null && gui.BuildButton("Other tools", SchemeColor.Grey, active:!string.IsNullOrEmpty(name))) + if (editingPage != null && gui.BuildButton("Other tools", SchemeColor.Grey, active: !string.IsNullOrEmpty(name))) { gui.ShowDropDown(OtherToolsDropdown); } @@ -89,7 +89,7 @@ public override void Build(ImGui gui) private void OtherToolsDropdown(ImGui gui) { - if (gui.BuildContextMenuButton("Duplicate page")) + if (editingPage.guid != MainScreen.SummaryGuid && gui.BuildContextMenuButton("Duplicate page")) { gui.CloseDropdown(); var project = editingPage.owner; @@ -108,7 +108,7 @@ private void OtherToolsDropdown(ImGui gui) } } - if (gui.BuildContextMenuButton("Share (export string to clipboard)")) + if (editingPage.guid != MainScreen.SummaryGuid && gui.BuildContextMenuButton("Share (export string to clipboard)")) { gui.CloseDropdown(); var data = JsonUtils.SaveToJson(editingPage); @@ -162,7 +162,7 @@ public static void LoadProjectPageFromClipboard() DataUtils.ReadLine(bytes, ref index); // reserved 1 if (DataUtils.ReadLine(bytes, ref index) != "") // reserved 2 but this time it is requried to be empty throw new NotSupportedException("Share string was created with future version of YAFC (" + version + ") and is incompatible"); - page = JsonUtils.LoadFromJson(new ReadOnlySpan(bytes, index, (int) ms.Length - index), project, collector); + page = JsonUtils.LoadFromJson(new ReadOnlySpan(bytes, index, (int)ms.Length - index), project, collector); } } } @@ -173,7 +173,7 @@ public static void LoadProjectPageFromClipboard() if (page != null) { - var existing = project.FindPage(page.guid); + var existing = project.FindPage(page.guid); if (existing != null) { MessageBox.Show((haveChoice, choice) => diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs new file mode 100644 index 00000000..a9206ee9 --- /dev/null +++ b/YAFC/Workspace/SummaryView.cs @@ -0,0 +1,26 @@ +using System; +using YAFC.Model; +using YAFC.UI; + +namespace YAFC +{ + public class SummaryView : ProjectPageView + { + public override void SetModel(ProjectPage page) + { + base.SetModel(page); + } + protected override void BuildPageTooltip(ImGui gui, Summary contents) + { + } + + protected override void BuildContent(ImGui gui) + { + + } + + public override void CreateModelDropdown(ImGui gui, Type type, Project project) + { + } + } +} \ No newline at end of file diff --git a/YAFCmodel/Model/Summary.cs b/YAFCmodel/Model/Summary.cs new file mode 100644 index 00000000..7eea9c58 --- /dev/null +++ b/YAFCmodel/Model/Summary.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; + +namespace YAFC.Model +{ + public class Summary : ProjectPageContents + { + + public Summary(ModelObject page) : base(page) { } + + public override async Task Solve(ProjectPage page) + { + return "Summary"; + } + } +} \ No newline at end of file From cc21634a8188d21019a5491fe2ef951e98fd7acb Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Wed, 29 Dec 2021 23:02:57 +0100 Subject: [PATCH 02/16] Also use 'over color' when defalt background is used --- YAFC/Widgets/ImmediateWidgets.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/YAFC/Widgets/ImmediateWidgets.cs b/YAFC/Widgets/ImmediateWidgets.cs index b120a606..c27a5bef 100644 --- a/YAFC/Widgets/ImmediateWidgets.cs +++ b/YAFC/Widgets/ImmediateWidgets.cs @@ -66,8 +66,10 @@ public static bool BuildFactorioObjectButton(this ImGui gui, Rect rect, Factorio else { overColor = bgColor + 1; - if (MainScreen.Instance.IsSameObjectHovered(gui, obj)) - bgColor = overColor; + } + if (MainScreen.Instance.IsSameObjectHovered(gui, obj)) + { + bgColor = overColor; } var evt = gui.BuildButton(rect, bgColor, overColor, button: 0); if (evt == ButtonEvent.MouseOver && obj != null) From 2d1c8f5d2a968cc3b97a93a9caafb8ae73f793a9 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Wed, 29 Dec 2021 23:24:25 +0100 Subject: [PATCH 03/16] Show link and flow information in SummaryView --- YAFC/Windows/MainScreen.cs | 9 +- .../ProductionTable/ProductionTableView.cs | 4 +- YAFC/Workspace/SummaryView.cs | 113 +++++++++++++++++- 3 files changed, 121 insertions(+), 5 deletions(-) diff --git a/YAFC/Windows/MainScreen.cs b/YAFC/Windows/MainScreen.cs index ee79e4cb..a1b7b751 100644 --- a/YAFC/Windows/MainScreen.cs +++ b/YAFC/Windows/MainScreen.cs @@ -48,7 +48,7 @@ public MainScreen(int display, Project project) : base(default) RegisterPageView(new ProductionTableView()); RegisterPageView(new AutoPlannerView()); RegisterPageView(new ProductionSummaryView()); - RegisterPageView(new SummaryView()); + RegisterPageView(new SummaryView(this)); searchGui = new ImGui(BuildSearch, new Padding(1f)) { boxShadow = RectangleBorder.Thin, boxColor = SchemeColor.Background }; Instance = this; tabBar = new MainScreenTabBar(this); @@ -84,6 +84,13 @@ private void SetProject(Project project) project.displayPages.Add(project.pages[0].guid); } + // Hack to activate all page solvers for the summary view + foreach (var page in project.pages) + { + page.SetActive(true); + page.SetActive(false); + } + SetActivePage(project.FindPage(project.displayPages[0])); project.metaInfoChanged += ProjectOnMetaInfoChanged; project.settings.changed += ProjectSettingsChanged; diff --git a/YAFC/Workspace/ProductionTable/ProductionTableView.cs b/YAFC/Workspace/ProductionTable/ProductionTableView.cs index 16134a45..4276db95 100644 --- a/YAFC/Workspace/ProductionTable/ProductionTableView.cs +++ b/YAFC/Workspace/ProductionTable/ProductionTableView.cs @@ -770,10 +770,10 @@ private void DrawDesiredProduct(ImGui gui, ProductionLink element) element.RecordUndo().amount = newAmount; } - public override void Rebuild(bool visuaOnly = false) + public override void Rebuild(bool visualOnly = false) { flatHierarchyBuilder.SetData(model); - base.Rebuild(visuaOnly); + base.Rebuild(visualOnly); } private void BuildGoodsIcon(ImGui gui, Goods goods, ProductionLink link, float amount, ProductDropdownType dropdownType, RecipeRow recipe, ProductionTable context, Goods[] variants = null) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index a9206ee9..c185f9e0 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -6,10 +6,114 @@ namespace YAFC { public class SummaryView : ProjectPageView { - public override void SetModel(ProjectPage page) + private class SummaryTabColumn : TextDataColumn { - base.SetModel(page); + public SummaryTabColumn() : base("Tab", 6f) + { + } + + public override void BuildElement(ImGui gui, ProjectPage page) + { + if (page?.contentType != typeof(ProductionTable)) + { + return; + } + + using (gui.EnterGroup(new Padding(0.5f, 0.2f, 0.2f, 0.5f))) + { + gui.spacing = 0.2f; + if (page.icon != null) + gui.BuildIcon(page.icon.icon); + else gui.AllocateRect(0f, 1.5f); + gui.BuildText(page.name); + } + } + } + + private class SummaryDataColumn : TextDataColumn + { + protected readonly SummaryView view; + private ProjectPage invokedPage; + + public SummaryDataColumn(SummaryView view) : base("Linked", float.MaxValue) + { + this.view = view; + } + + public override void BuildElement(ImGui gui, ProjectPage page) + { + if (page?.contentType != typeof(ProductionTable)) + { + return; + } + + using var grid = gui.EnterInlineGrid(3f, 1f); + foreach (ProductionLink link in (page.content as ProductionTable).links) + { + if (link.amount != 0f) + { + grid.Next(); + DrawProvideProduct(gui, link, page); + } + + } + + foreach (ProductionTableFlow flow in (page.content as ProductionTable).flow) + { + if (flow.amount >= -1e-5f) + break; + grid.Next(); + DrawRequestProduct(gui, flow); + } + } + + private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page) + { + gui.allocator = RectAllocator.Stretch; + gui.spacing = 0f; + GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, SchemeColor.Primary); + if (evt == GoodsWithAmountEvent.TextEditing && newAmount != 0) + { + element.RecordUndo().amount = newAmount; + // Hack: Force recalculate the page (and make sure to catch the content change event caused by the recalculation) + invokedPage = page; + page.contentChanged += RebuildInvoked; + page.SetActive(true); + page.SetToRecalculate(); + page.SetActive(false); + } + } + + static private void DrawRequestProduct(ImGui gui, ProductionTableFlow flow) + { + gui.allocator = RectAllocator.Stretch; + gui.spacing = 0f; + gui.BuildFactorioObjectWithAmount(flow.goods, -flow.amount, flow.goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, SchemeColor.None); + } + + private void RebuildInvoked(bool visualOnly = false) + { + view.Rebuild(visualOnly); + invokedPage.contentChanged -= RebuildInvoked; + } + } + + private readonly MainScreen screen; + + private readonly DataGrid mainGrid; + + + public SummaryView(MainScreen screen) + { + this.screen = screen; + var columns = new TextDataColumn[] + { + new SummaryTabColumn(), + new SummaryDataColumn(this), + }; + mainGrid = new DataGrid(columns); } + protected override void BuildPageTooltip(ImGui gui, Summary contents) { } @@ -17,6 +121,11 @@ protected override void BuildPageTooltip(ImGui gui, Summary contents) protected override void BuildContent(ImGui gui) { + foreach (Guid displayPage in screen.project.displayPages) + { + ProjectPage page = screen.project.FindPage(displayPage); + mainGrid.BuildRow(gui, page); + } } public override void CreateModelDropdown(ImGui gui, Type type, Project project) From 3662a8eac71ee0f2a9bc1bbcd350b1d3768ffc24 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Thu, 30 Dec 2021 01:26:10 +0100 Subject: [PATCH 04/16] Align goods underneath eachother and mark linked goods when there is not enough production --- YAFC/Workspace/SummaryView.cs | 69 +++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index c185f9e0..fd151ce2 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using YAFC.Model; using YAFC.UI; @@ -47,31 +48,40 @@ public override void BuildElement(ImGui gui, ProjectPage page) return; } + var table = page.content as ProductionTable; + using var grid = gui.EnterInlineGrid(3f, 1f); - foreach (ProductionLink link in (page.content as ProductionTable).links) + foreach (KeyValuePair entry in view.allGoods) { - if (link.amount != 0f) + grid.Next(); + ProductionLink link = table.links.Find(x => x.goods.name == entry.Key); + if (link != null) { - grid.Next(); - DrawProvideProduct(gui, link, page); + if (link.amount != 0f) + { + DrawProvideProduct(gui, link, page, entry.Value); + } + } + else + { + if (Array.Exists(table.flow, x => x.goods.name == entry.Key)) + { + ProductionTableFlow flow = Array.Find(table.flow, x => x.goods.name == entry.Key); + if (flow.amount < -1e-5f) + { + DrawRequestProduct(gui, flow); + } + } } - - } - - foreach (ProductionTableFlow flow in (page.content as ProductionTable).flow) - { - if (flow.amount >= -1e-5f) - break; - grid.Next(); - DrawRequestProduct(gui, flow); } } - private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page) + + private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page, float requiredOutput) { gui.allocator = RectAllocator.Stretch; gui.spacing = 0f; - GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, SchemeColor.Primary); + GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, requiredOutput > element.amount ? SchemeColor.Error : SchemeColor.Primary); if (evt == GoodsWithAmountEvent.TextEditing && newAmount != 0) { element.RecordUndo().amount = newAmount; @@ -102,6 +112,8 @@ private void RebuildInvoked(bool visualOnly = false) private readonly DataGrid mainGrid; + private readonly Dictionary allGoods = new Dictionary(); + public SummaryView(MainScreen screen) { @@ -120,6 +132,33 @@ protected override void BuildPageTooltip(ImGui gui, Summary contents) protected override void BuildContent(ImGui gui) { + // TODO Can we detect if things changed? + allGoods.Clear(); + foreach (Guid displayPage in screen.project.displayPages) + { + ProjectPage page = screen.project.FindPage(displayPage); + ProductionTable content = page?.content as ProductionTable; + if (content == null) + { + continue; + } + + foreach (ProductionLink link in content.links) + { + if (link.amount != 0f && !allGoods.ContainsKey(link.goods.name)) + allGoods[link.goods.name] = 0; + } + + foreach (ProductionTableFlow flow in content.flow) + { + if (flow.amount < -1e-5f) + { + float value = allGoods.GetValueOrDefault(flow.goods.name); + value -= flow.amount; + allGoods[flow.goods.name] = value; + } + } + } foreach (Guid displayPage in screen.project.displayPages) { From 24a48d88da0b588bf943dcadc005f0c28bc804b5 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Thu, 30 Dec 2021 23:01:41 +0100 Subject: [PATCH 05/16] Hide columns that have no problems (produced/provided == consumed/requested) --- YAFC/Workspace/SummaryView.cs | 55 +++++++++++++++++++++++++---------- YAFCmodel/Data/DataUtils.cs | 8 ++--- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index fd151ce2..c2385584 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -49,17 +49,23 @@ public override void BuildElement(ImGui gui, ProjectPage page) } var table = page.content as ProductionTable; - using var grid = gui.EnterInlineGrid(3f, 1f); - foreach (KeyValuePair entry in view.allGoods) + foreach (KeyValuePair entry in view.allGoods) { + float amountAvailable = entry.Value.totalProvided > 0 ? entry.Value.totalProvided : entry.Value.extraProduced; + float amountNeeded = entry.Value.totalProvided < 0 ? -entry.Value.totalProvided : entry.Value.totalNeeded; + if (DataUtils.FormatAmount(amountAvailable, UnitOfMeasure.None) == DataUtils.FormatAmount(amountNeeded, UnitOfMeasure.None)) + { + continue; + } + grid.Next(); ProductionLink link = table.links.Find(x => x.goods.name == entry.Key); if (link != null) { if (link.amount != 0f) { - DrawProvideProduct(gui, link, page, entry.Value); + DrawProvideProduct(gui, link, page, entry.Value.extraProduced, amountAvailable >= entry.Value.totalNeeded); } } else @@ -67,21 +73,22 @@ public override void BuildElement(ImGui gui, ProjectPage page) if (Array.Exists(table.flow, x => x.goods.name == entry.Key)) { ProductionTableFlow flow = Array.Find(table.flow, x => x.goods.name == entry.Key); - if (flow.amount < -1e-5f) + if (Math.Abs(flow.amount) > 1e-5f) { - DrawRequestProduct(gui, flow); + + DrawRequestProduct(gui, flow, entry.Value.extraProduced >= entry.Value.totalNeeded); } } } } } - - private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page, float requiredOutput) + private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page, float extraProduced, bool enoughOutput) { gui.allocator = RectAllocator.Stretch; gui.spacing = 0f; - GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, requiredOutput > element.amount ? SchemeColor.Error : SchemeColor.Primary); + + GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, (element.amount > 0 && enoughOutput) || (element.amount < 0 && DataUtils.FormatAmount(extraProduced, UnitOfMeasure.None) == DataUtils.FormatAmount(-element.amount, UnitOfMeasure.None)) ? SchemeColor.Primary : SchemeColor.Error); if (evt == GoodsWithAmountEvent.TextEditing && newAmount != 0) { element.RecordUndo().amount = newAmount; @@ -93,12 +100,11 @@ private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage p page.SetActive(false); } } - - static private void DrawRequestProduct(ImGui gui, ProductionTableFlow flow) + static private void DrawRequestProduct(ImGui gui, ProductionTableFlow flow, bool enoughProduced) { gui.allocator = RectAllocator.Stretch; gui.spacing = 0f; - gui.BuildFactorioObjectWithAmount(flow.goods, -flow.amount, flow.goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, SchemeColor.None); + gui.BuildFactorioObjectWithAmount(flow.goods, -flow.amount, flow.goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, flow.amount > 1e-5f ? enoughProduced ? SchemeColor.Green : SchemeColor.Error : SchemeColor.None); } private void RebuildInvoked(bool visualOnly = false) @@ -108,11 +114,18 @@ private void RebuildInvoked(bool visualOnly = false) } } + struct GoodDetails + { + public float totalProvided; + public float totalNeeded; + public float extraProduced; + } + private readonly MainScreen screen; private readonly DataGrid mainGrid; - private readonly Dictionary allGoods = new Dictionary(); + private readonly Dictionary allGoods = new Dictionary(); public SummaryView(MainScreen screen) @@ -145,16 +158,26 @@ protected override void BuildContent(ImGui gui) foreach (ProductionLink link in content.links) { - if (link.amount != 0f && !allGoods.ContainsKey(link.goods.name)) - allGoods[link.goods.name] = 0; + if (link.amount != 0f) + { + GoodDetails value = allGoods.GetValueOrDefault(link.goods.name); + value.totalProvided += link.amount; + allGoods[link.goods.name] = value; + } } foreach (ProductionTableFlow flow in content.flow) { if (flow.amount < -1e-5f) { - float value = allGoods.GetValueOrDefault(flow.goods.name); - value -= flow.amount; + GoodDetails value = allGoods.GetValueOrDefault(flow.goods.name); + value.totalNeeded -= flow.amount; + allGoods[flow.goods.name] = value; + } + else if (flow.amount > 1e-5f) + { + GoodDetails value = allGoods.GetValueOrDefault(flow.goods.name); + value.extraProduced += flow.amount; allGoods[flow.goods.name] = value; } } diff --git a/YAFCmodel/Data/DataUtils.cs b/YAFCmodel/Data/DataUtils.cs index 016b3de0..4f16aada 100644 --- a/YAFCmodel/Data/DataUtils.cs +++ b/YAFCmodel/Data/DataUtils.cs @@ -427,11 +427,11 @@ public static string FormatTime(float time) public static string FormatAmount(float amount, UnitOfMeasure unit, string prefix = null, string suffix = null, bool precise = false) { - var (multplier, unitSuffix) = Project.current == null ? (1f, null) : Project.current.ResolveUnitOfMeasure(unit); - return FormatAmountRaw(amount, multplier, unitSuffix, prefix, suffix, precise ? PreciseFormat : FormatSpec); + var (multiplier, unitSuffix) = Project.current == null ? (1f, null) : Project.current.ResolveUnitOfMeasure(unit); + return FormatAmountRaw(amount, multiplier, unitSuffix, prefix, suffix, precise ? PreciseFormat : FormatSpec); } - public static string FormatAmountRaw(float amount, float unitMultipler, string unitSuffix, string prefix = null, string suffix = null, (char suffix, float multiplier, string format)[] formatSpec = null) + public static string FormatAmountRaw(float amount, float unitMultiplier, string unitSuffix, string prefix = null, string suffix = null, (char suffix, float multiplier, string format)[] formatSpec = null) { if (float.IsNaN(amount) || float.IsInfinity(amount)) return "-"; @@ -447,7 +447,7 @@ public static string FormatAmountRaw(float amount, float unitMultipler, string u amount = -amount; } - amount *= unitMultipler; + amount *= unitMultiplier; var idx = MathUtils.Clamp(MathUtils.Floor(MathF.Log10(amount)) + 8, 0, formatSpec.Length-1); var val = formatSpec[idx]; amountBuilder.Append((amount * val.multiplier).ToString(val.format)); From a1b57ced778b6c914ca8b25f7e42fbeea5ae5724 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Fri, 31 Dec 2021 01:01:52 +0100 Subject: [PATCH 06/16] Improve to match UI better ('YAFC rounding' and float rounding) --- YAFC/Workspace/SummaryView.cs | 46 +++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index c2385584..a4e36bc4 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -52,9 +52,9 @@ public override void BuildElement(ImGui gui, ProjectPage page) using var grid = gui.EnterInlineGrid(3f, 1f); foreach (KeyValuePair entry in view.allGoods) { - float amountAvailable = entry.Value.totalProvided > 0 ? entry.Value.totalProvided : entry.Value.extraProduced; - float amountNeeded = entry.Value.totalProvided < 0 ? -entry.Value.totalProvided : entry.Value.totalNeeded; - if (DataUtils.FormatAmount(amountAvailable, UnitOfMeasure.None) == DataUtils.FormatAmount(amountNeeded, UnitOfMeasure.None)) + float amountAvailable = YAFCRounding((entry.Value.totalProvided > 0 ? entry.Value.totalProvided : 0) + entry.Value.extraProduced); + float amountNeeded = YAFCRounding((entry.Value.totalProvided < 0 ? -entry.Value.totalProvided : 0) + entry.Value.totalNeeded); + if (Math.Abs(amountAvailable - amountNeeded) < Epsilon || amountNeeded == 0) { continue; } @@ -65,7 +65,7 @@ public override void BuildElement(ImGui gui, ProjectPage page) { if (link.amount != 0f) { - DrawProvideProduct(gui, link, page, entry.Value.extraProduced, amountAvailable >= entry.Value.totalNeeded); + DrawProvideProduct(gui, link, page, entry.Value.extraProduced, amountAvailable >= amountNeeded); } } else @@ -73,10 +73,10 @@ public override void BuildElement(ImGui gui, ProjectPage page) if (Array.Exists(table.flow, x => x.goods.name == entry.Key)) { ProductionTableFlow flow = Array.Find(table.flow, x => x.goods.name == entry.Key); - if (Math.Abs(flow.amount) > 1e-5f) + if (Math.Abs(flow.amount) > Epsilon) { - DrawRequestProduct(gui, flow, entry.Value.extraProduced >= entry.Value.totalNeeded); + DrawRequestProduct(gui, flow, amountAvailable >= amountNeeded); } } } @@ -88,7 +88,7 @@ private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage p gui.allocator = RectAllocator.Stretch; gui.spacing = 0f; - GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, (element.amount > 0 && enoughOutput) || (element.amount < 0 && DataUtils.FormatAmount(extraProduced, UnitOfMeasure.None) == DataUtils.FormatAmount(-element.amount, UnitOfMeasure.None)) ? SchemeColor.Primary : SchemeColor.Error); + GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, (element.amount > 0 && enoughOutput) || (element.amount < 0 && extraProduced == -element.amount) ? SchemeColor.Primary : SchemeColor.Error); if (evt == GoodsWithAmountEvent.TextEditing && newAmount != 0) { element.RecordUndo().amount = newAmount; @@ -104,7 +104,7 @@ static private void DrawRequestProduct(ImGui gui, ProductionTableFlow flow, bool { gui.allocator = RectAllocator.Stretch; gui.spacing = 0f; - gui.BuildFactorioObjectWithAmount(flow.goods, -flow.amount, flow.goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, flow.amount > 1e-5f ? enoughProduced ? SchemeColor.Green : SchemeColor.Error : SchemeColor.None); + gui.BuildFactorioObjectWithAmount(flow.goods, -flow.amount, flow.goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, flow.amount > Epsilon ? enoughProduced ? SchemeColor.Green : SchemeColor.Error : SchemeColor.None); } private void RebuildInvoked(bool visualOnly = false) @@ -114,6 +114,8 @@ private void RebuildInvoked(bool visualOnly = false) } } + static readonly float Epsilon = 1e-5f; + struct GoodDetails { public float totalProvided; @@ -161,24 +163,28 @@ protected override void BuildContent(ImGui gui) if (link.amount != 0f) { GoodDetails value = allGoods.GetValueOrDefault(link.goods.name); - value.totalProvided += link.amount; + value.totalProvided += YAFCRounding(link.amount); ; allGoods[link.goods.name] = value; } } foreach (ProductionTableFlow flow in content.flow) { - if (flow.amount < -1e-5f) + if (flow.amount < -Epsilon) { GoodDetails value = allGoods.GetValueOrDefault(flow.goods.name); - value.totalNeeded -= flow.amount; + value.totalNeeded -= YAFCRounding(flow.amount); ; allGoods[flow.goods.name] = value; } - else if (flow.amount > 1e-5f) + else if (flow.amount > Epsilon) { - GoodDetails value = allGoods.GetValueOrDefault(flow.goods.name); - value.extraProduced += flow.amount; - allGoods[flow.goods.name] = value; + if (!content.links.Exists(x => x.goods == flow.goods)) + { + // Only count extras if not linked + GoodDetails value = allGoods.GetValueOrDefault(flow.goods.name); + value.extraProduced += YAFCRounding(flow.amount); + allGoods[flow.goods.name] = value; + } } } } @@ -190,6 +196,16 @@ protected override void BuildContent(ImGui gui) } } + // Convert/truncate value as shown in UI to prevent slight mismatches + static private float YAFCRounding(float value) + { +#pragma warning disable CA1806 // We don't care about the returned value as result is updated independently whether the function return true or not + DataUtils.TryParseAmount(DataUtils.FormatAmount(value, UnitOfMeasure.Second), out float result, UnitOfMeasure.Second); +#pragma warning restore CA1806 + + return result; + } + public override void CreateModelDropdown(ImGui gui, Type type, Project project) { } From 1df569d8933ff4e67c0d4d3c7597f2e3cc374289 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Sun, 2 Jan 2022 14:22:07 +0100 Subject: [PATCH 07/16] Also calculate width when header is not rendered --- YAFC/Widgets/DataGrid.cs | 29 ++++++++++++++++++++--------- YAFC/Workspace/SummaryView.cs | 1 - 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/YAFC/Widgets/DataGrid.cs b/YAFC/Widgets/DataGrid.cs index 2752ac2d..f31b58c4 100644 --- a/YAFC/Widgets/DataGrid.cs +++ b/YAFC/Widgets/DataGrid.cs @@ -40,18 +40,18 @@ public override void BuildHeader(ImGui gui) if (hasMenu) { var rect = gui.statePosition; - var menuRect = new Rect(rect.Right-1.7f, rect.Y, 1.5f, 1.5f); + var menuRect = new Rect(rect.Right - 1.7f, rect.Y, 1.5f, 1.5f); if (gui.isBuilding) gui.DrawIcon(menuRect, Icon.DropDown, SchemeColor.BackgroundText); if (gui.BuildButton(menuRect, SchemeColor.None, SchemeColor.Grey)) gui.ShowDropDown(menuRect, BuildMenu, new Padding(1f)); } } - - public virtual void BuildMenu(ImGui gui) {} + + public virtual void BuildMenu(ImGui gui) { } } - public class DataGrid where TData:class + public class DataGrid where TData : class { public readonly List> columns; private readonly Padding innerPadding = new Padding(0.2f); @@ -66,7 +66,7 @@ public DataGrid(params DataColumn[] columns) this.columns = new List>(columns); spacing = innerPadding.left + innerPadding.right; } - + private void BuildHeaderResizer(ImGui gui, DataColumn column, Rect rect) { @@ -89,7 +89,7 @@ private void BuildHeaderResizer(ImGui gui, DataColumn column, Rect rect) gui.Rebuild(); break; case ImGuiAction.MouseDown: - gui.ConsumeMouseDown(rect, cursor:RenderingUtils.cursorHorizontalResize); + gui.ConsumeMouseDown(rect, cursor: RenderingUtils.cursorHorizontalResize); break; case ImGuiAction.MouseUp: if (gui.ConsumeMouseUp(rect, false)) @@ -102,6 +102,16 @@ private void BuildHeaderResizer(ImGui gui, DataColumn column, Rect rect) } } + private void CalculateWidth(ImGui gui) + { + var x = 0f; + foreach (var column in columns) + { + x += column.width + spacing; + } + width = MathF.Max(x + 0.2f - spacing, gui.width - 1f); + } + public void BuildHeader(ImGui gui) { var spacing = innerPadding.left + innerPadding.right; @@ -127,7 +137,7 @@ public void BuildHeader(ImGui gui) } } } - width = MathF.Max(x + 0.2f - spacing, gui.width - 1f); + CalculateWidth(gui); var separator = gui.AllocateRect(x, 0.1f); if (gui.isBuilding) @@ -162,10 +172,11 @@ public Rect BuildRow(ImGui gui, TData element, float startX = 0f) buildGroup.Complete(); } + CalculateWidth(gui); var rect = gui.lastRect; var bottom = gui.lastRect.Bottom; if (gui.isBuilding) - gui.DrawRectangle(new Rect(startX, bottom - 0.1f, width-startX, 0.1f), SchemeColor.Grey); + gui.DrawRectangle(new Rect(startX, bottom - 0.1f, width - startX, 0.1f), SchemeColor.Grey); return rect; } @@ -178,7 +189,7 @@ public void BeginBuildingContent(ImGui gui) public Rect EndBuildingContent(ImGui gui) { var bottom = gui.statePosition.Bottom; - return new Rect(buildingStart.X, buildingStart.Y, width, bottom-buildingStart.Y); + return new Rect(buildingStart.X, buildingStart.Y, width, bottom - buildingStart.Y); } public bool BuildContent(ImGui gui, IReadOnlyList data, out (TData from, TData to) reorder, out Rect rect, Func filter = null) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index a4e36bc4..4c616ad6 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -75,7 +75,6 @@ public override void BuildElement(ImGui gui, ProjectPage page) ProductionTableFlow flow = Array.Find(table.flow, x => x.goods.name == entry.Key); if (Math.Abs(flow.amount) > Epsilon) { - DrawRequestProduct(gui, flow, amountAvailable >= amountNeeded); } } From 2588e3ca24c11b518249e0c6a5883d1f956dc084 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Sun, 2 Jan 2022 14:46:56 +0100 Subject: [PATCH 08/16] (minor) fixes * Don't use 'weird' output of EncapsulateRect as 'lastRect', but the actual rect of the (old) state * Improve ScrollArea class names to reflect horizontal scrolling as well, including a constructor parameter to enable horizontal scrolling * Renamed some variables * Typos --- YAFC/Widgets/ObjectTooltip.cs | 2 +- YAFC/Windows/DependencyExplorer.cs | 34 ++++----- YAFC/Windows/ErrorListPanel.cs | 8 +-- YAFC/Windows/NeverEnoughItemsPanel.cs | 50 ++++++------- YAFC/Windows/WelcomeScreen.cs | 70 +++++++++---------- .../ProductionLinkSummaryScreen.cs | 12 ++-- YAFCui/ImGui/ImGui.cs | 40 +++++------ YAFCui/ImGui/ImGuiBuilding.cs | 23 +++--- YAFCui/ImGui/ImGuiLayout.cs | 54 +++++++------- YAFCui/ImGui/ScrollArea.cs | 28 ++++---- 10 files changed, 163 insertions(+), 158 deletions(-) diff --git a/YAFC/Widgets/ObjectTooltip.cs b/YAFC/Widgets/ObjectTooltip.cs index f1303c22..0f379d38 100644 --- a/YAFC/Widgets/ObjectTooltip.cs +++ b/YAFC/Widgets/ObjectTooltip.cs @@ -432,7 +432,7 @@ private void BuildRecipe(RecipeOrTechnology recipe, ImGui gui) gui.BuildText(DataUtils.FormatAmount(ingredient.amount, UnitOfMeasure.None)); } - gui.allocator = RectAllocator.RemainigRow; + gui.allocator = RectAllocator.RemainingRow; gui.BuildFactorioObjectButtonWithText(technology); } } diff --git a/YAFC/Windows/DependencyExplorer.cs b/YAFC/Windows/DependencyExplorer.cs index 7b38505c..571c7ae3 100644 --- a/YAFC/Windows/DependencyExplorer.cs +++ b/YAFC/Windows/DependencyExplorer.cs @@ -10,11 +10,11 @@ namespace YAFC public class DependencyExplorer : PseudoScreen { private static readonly DependencyExplorer Instance = new DependencyExplorer(); - - private readonly VerticalScrollCustom dependencies; - private readonly VerticalScrollCustom dependants; + + private readonly ScrollArea dependencies; + private readonly ScrollArea dependants; private static readonly Padding listPad = new Padding(0.5f); - + private readonly List history = new List(); private FactorioObject current; @@ -31,11 +31,11 @@ public class DependencyExplorer : PseudoScreen {DependencyList.Flags.SourceEntity, ("Source", "This recipe requires another entity")}, {DependencyList.Flags.Hidden, ("", "This technology is hidden")}, }; - + public DependencyExplorer() : base(60f) { - dependencies = new VerticalScrollCustom(30f, DrawDependencies); - dependants = new VerticalScrollCustom(30f, DrawDependants); + dependencies = new ScrollArea(30f, DrawDependencies); + dependants = new ScrollArea(30f, DrawDependants); } public static void Show(FactorioObject target) @@ -52,9 +52,9 @@ private void DrawFactorioObject(ImGui gui, FactorioId id) { gui.BuildFactorioObjectIcon(fobj); var text = fobj.locName + " (" + fobj.type + ")"; - gui.RemainingRow(0.5f).BuildText(text, null, true, color:fobj.IsAccessible() ? SchemeColor.BackgroundText : SchemeColor.BackgroundTextFaint); + gui.RemainingRow(0.5f).BuildText(text, null, true, color: fobj.IsAccessible() ? SchemeColor.BackgroundText : SchemeColor.BackgroundTextFaint); } - if (gui.BuildFactorioObjectButton(gui.lastRect, fobj, extendHeader:true)) + if (gui.BuildFactorioObjectButton(gui.lastRect, fobj, extendHeader: true)) Change(fobj); } @@ -64,12 +64,12 @@ private void DrawDependencies(ImGui gui) foreach (var data in Dependencies.dependencyList[current]) { if (!dependencyListTexts.TryGetValue(data.flags, out var dependencyType)) - dependencyType = (data.flags.ToString(), "Missing "+data.flags); + dependencyType = (data.flags.ToString(), "Missing " + data.flags); if (data.elements.Length > 0) { gui.AllocateSpacing(0.5f); if (data.elements.Length == 1) - gui.BuildText("Require this "+dependencyType.name+":"); + gui.BuildText("Require this " + dependencyType.name + ":"); else if (data.flags.HasFlags(DependencyList.Flags.RequireEverything)) gui.BuildText("Require ALL of these " + dependencyType.name + "s:"); else gui.BuildText("Require ANY of these " + dependencyType.name + "s:"); @@ -83,7 +83,7 @@ private void DrawDependencies(ImGui gui) if (Database.rootAccessible.Contains(current)) text += ", but it is inherently accessible"; else text += ", and it is inaccessible"; - gui.BuildText(text, wrap:true); + gui.BuildText(text, wrap: true); } } } @@ -113,7 +113,7 @@ public override void Build(ImGui gui) gui.BuildText("Currently inspecting:", Font.subheader); if (gui.BuildFactorioObjectButtonWithText(current)) SelectObjectPanel.Select(Database.objects.all, "Select something", Change); - gui.BuildText("(Click to change)", color:SchemeColor.BackgroundTextFaint); + gui.BuildText("(Click to change)", color: SchemeColor.BackgroundTextFaint); } using (gui.EnterRow()) { @@ -123,7 +123,7 @@ public override void Build(ImGui gui) if (current.IsAutomatable()) gui.BuildText("Status: Automatable"); else gui.BuildText("Status: Accessible, Not automatable"); - + if (settings.Flags(current).HasFlags(ProjectPerItemFlags.MarkedAccessible)) { gui.BuildText("Manually marked as accessible."); @@ -165,7 +165,7 @@ public override void Build(ImGui gui) dependants.Build(gui); } } - + public void Change(FactorioObject target) { if (target == null) @@ -174,7 +174,7 @@ public void Change(FactorioObject target) Close(); return; } - + history.Add(current); if (history.Count > 100) history.RemoveRange(0, 20); @@ -190,7 +190,7 @@ public override bool KeyDown(SDL.SDL_Keysym key) { var last = history[history.Count - 1]; Change(last); - history.RemoveRange(history.Count-2, 2); + history.RemoveRange(history.Count - 2, 2); return true; } return base.KeyDown(key); diff --git a/YAFC/Windows/ErrorListPanel.cs b/YAFC/Windows/ErrorListPanel.cs index 68f16216..f7f48f82 100644 --- a/YAFC/Windows/ErrorListPanel.cs +++ b/YAFC/Windows/ErrorListPanel.cs @@ -7,19 +7,19 @@ public class ErrorListPanel : PseudoScreen { private static readonly ErrorListPanel Instance = new ErrorListPanel(); private ErrorCollector collector; - private readonly VerticalScrollCustom verticalList; + private readonly ScrollArea verticalList; private (string error, ErrorSeverity severity)[] errors; public ErrorListPanel() : base(60f) { - verticalList = new VerticalScrollCustom(30f, BuildErrorList, default, true); + verticalList = new ScrollArea(30f, BuildErrorList, default, true); } private void BuildErrorList(ImGui gui) { foreach (var error in errors) { - gui.BuildText(error.error, wrap:true, color:error.severity >= ErrorSeverity.MajorDataLoss ? SchemeColor.Error : SchemeColor.BackgroundText); + gui.BuildText(error.error, wrap: true, color: error.severity >= ErrorSeverity.MajorDataLoss ? SchemeColor.Error : SchemeColor.BackgroundText); } } @@ -37,7 +37,7 @@ public override void Build(ImGui gui) BuildHeader(gui, "Loading completed with errors"); else BuildHeader(gui, "Analysis warnings"); verticalList.Build(gui); - + } } } \ No newline at end of file diff --git a/YAFC/Windows/NeverEnoughItemsPanel.cs b/YAFC/Windows/NeverEnoughItemsPanel.cs index 5ae0f998..43731e69 100644 --- a/YAFC/Windows/NeverEnoughItemsPanel.cs +++ b/YAFC/Windows/NeverEnoughItemsPanel.cs @@ -6,7 +6,7 @@ namespace YAFC { public class NeverEnoughItemsPanel : PseudoScreen, IComparer { - private static readonly NeverEnoughItemsPanel Instance = new NeverEnoughItemsPanel(); + private static readonly NeverEnoughItemsPanel Instance = new NeverEnoughItemsPanel(); private Goods current; private Goods changing; private float currentFlow; @@ -14,9 +14,9 @@ public class NeverEnoughItemsPanel : PseudoScreen, IComparer recent = new List(); private bool atCurrentMilestones; - private readonly VerticalScrollCustom productionList; - private readonly VerticalScrollCustom usageList; - + private readonly ScrollArea productionList; + private readonly ScrollArea usageList; + private enum EntryStatus { NotAccessible, @@ -25,7 +25,7 @@ private enum EntryStatus Normal, Useful } - + private readonly struct RecipeEntry { public readonly Recipe recipe; @@ -59,11 +59,11 @@ public RecipeEntry(Recipe recipe, bool isProduction, Goods currentItem, bool atC private readonly List productions = new List(); private readonly List usages = new List(); - + public NeverEnoughItemsPanel() : base(76f) { - productionList = new VerticalScrollCustom(40f, BuildItemProduction, new Padding(0.5f)); - usageList = new VerticalScrollCustom(40f, BuildItemUsages, new Padding(0.5f)); + productionList = new ScrollArea(40f, BuildItemProduction, new Padding(0.5f)); + usageList = new ScrollArea(40f, BuildItemUsages, new Padding(0.5f)); } private void SetItem(Goods current) @@ -79,7 +79,7 @@ private void SetItem(Goods current) currentFlow = current.ApproximateFlow(atCurrentMilestones); productions.Clear(); foreach (var recipe in current.production) - productions.Add(new RecipeEntry(recipe, true, current, atCurrentMilestones)); + productions.Add(new RecipeEntry(recipe, true, current, atCurrentMilestones)); productions.Sort(this); usages.Clear(); foreach (var usage in current.usages) @@ -157,7 +157,8 @@ private void DrawRecipeEntry(ImGui gui, RecipeEntry entry, bool production) { bgColor = SchemeColor.None; textcolor = SchemeColor.BackgroundTextFaint; - } else if (entry.flow > 0f) + } + else if (entry.flow > 0f) { bgColor = SchemeColor.Secondary; textcolor = SchemeColor.SecondaryText; @@ -172,12 +173,12 @@ private void DrawRecipeEntry(ImGui gui, RecipeEntry entry, bool production) using (gui.EnterRow()) { gui.BuildIcon(Icon.Time); - gui.BuildText(DataUtils.FormatAmount(entry.recipe.time, UnitOfMeasure.Second), align:RectAlignment.Middle); + gui.BuildText(DataUtils.FormatAmount(entry.recipe.time, UnitOfMeasure.Second), align: RectAlignment.Middle); } var bh = CostAnalysis.Instance.GetBuildingHours(recipe, entry.recipeFlow); if (bh > 20) { - gui.BuildText(DataUtils.FormatAmount(bh, UnitOfMeasure.None, suffix:"bh"), align:RectAlignment.Middle); + gui.BuildText(DataUtils.FormatAmount(bh, UnitOfMeasure.None, suffix: "bh"), align: RectAlignment.Middle); gui.BuildButton(gui.lastRect, SchemeColor.None, SchemeColor.Grey).WithTooltip(gui, "Building-hours.\nAmount of building-hours required for all researches assuming crafting speed of 1"); } } @@ -192,7 +193,7 @@ private void DrawRecipeEntry(ImGui gui, RecipeEntry entry, bool production) if (gui.BuildButton(iconRect, SchemeColor.None, SchemeColor.BackgroundAlt)) Project.current.preferences.ToggleFavourite(entry.recipe); gui.allocator = textalloc; - gui.BuildText(recipe.locName, wrap:true); + gui.BuildText(recipe.locName, wrap: true); } if (recipe.ingredients.Length + recipe.products.Length <= 8) { @@ -204,7 +205,7 @@ private void DrawRecipeEntry(ImGui gui, RecipeEntry entry, bool production) if (recipe.products.Length < 3 && recipe.ingredients.Length < 5) gui.AllocateSpacing((3 - entry.recipe.products.Length) * 3f); else if (recipe.products.Length < 3) - gui.allocator = RectAllocator.RemainigRow; + gui.allocator = RectAllocator.RemainingRow; gui.BuildIcon(Icon.ArrowRight, 3f); } } @@ -230,7 +231,8 @@ private void DrawRecipeEntry(ImGui gui, RecipeEntry entry, bool production) var percentFlow = MathUtils.Clamp(entry.flow / currentFlow, 0f, 1f); rect.Width *= percentFlow; gui.DrawRectangle(rect, SchemeColor.Primary); - } else if (waste <= 0f) + } + else if (waste <= 0f) bgColor = SchemeColor.Secondary; else { @@ -247,7 +249,7 @@ private void DrawEntryFooter(ImGui gui, bool production) { using (gui.EnterGroup(new Padding(0.5f), RectAllocator.LeftAlign)) { - gui.BuildText(current.fuelValue > 0f ? "Fuel value "+DataUtils.FormatAmount(current.fuelValue, UnitOfMeasure.Megajoule)+" can be used for:" : "Can be used to fuel:"); + gui.BuildText(current.fuelValue > 0f ? "Fuel value " + DataUtils.FormatAmount(current.fuelValue, UnitOfMeasure.Megajoule) + " can be used for:" : "Can be used to fuel:"); using (var grid = gui.EnterInlineGrid(3f)) { foreach (var fuelUsage in current.fuelFor) @@ -285,12 +287,12 @@ private void DrawEntryList(ImGui gui, List entries, bool production footerDrawn = true; gui.BuildText(entry.entryStatus == EntryStatus.Special ? "Show special recipes (barreling / voiding)" : entry.entryStatus == EntryStatus.NotAccessibleWithCurrentMilestones ? "There are more recipes, but they are locked based on current milestones" : - "There are more recipes but they are inaccessible", wrap:true); + "There are more recipes but they are inaccessible", wrap: true); if (gui.BuildButton("Show more recipes")) ChangeShowStatus(status); break; } - + if (status < prevEntryStatus) { prevEntryStatus = status; @@ -307,7 +309,7 @@ private void DrawEntryList(ImGui gui, List entries, bool production var latest = Milestones.Instance.GetHighest(entry.recipe, false); if (latest != prevLatestMilestone) { - gui.BuildFactorioObjectButtonWithText(latest, size:3f, display:MilestoneDisplay.None); + gui.BuildFactorioObjectButtonWithText(latest, size: 3f, display: MilestoneDisplay.None); prevLatestMilestone = latest; } } @@ -343,18 +345,18 @@ public override void Build(ImGui gui) using (gui.EnterGroup(new Padding(0.5f), RectAllocator.LeftRow)) { gui.spacing = 0.2f; - gui.BuildFactorioObjectIcon(current, size:3f); + gui.BuildFactorioObjectIcon(current, size: 3f); gui.BuildText(current.locName, Font.subheader); gui.allocator = RectAllocator.RightAlign; gui.BuildText(CostAnalysis.GetDisplayCost(current)); var amount = CostAnalysis.Instance.GetItemAmount(current); if (amount != null) - gui.BuildText(amount, wrap:true); + gui.BuildText(amount, wrap: true); } if (gui.BuildFactorioObjectButton(gui.lastRect, current, SchemeColor.Grey)) SelectObjectPanel.Select(Database.goods.all, "Select item", SetItem); - + using (var split = gui.EnterHorizontalSplit(2)) { split.Next(); @@ -369,13 +371,13 @@ public override void Build(ImGui gui) { if (gui.BuildLink("What do colored bars mean?")) { - MessageBox.Show("How to read colored bars", + MessageBox.Show("How to read colored bars", "Blue bar means estimated production or consumption of the thing you selected. Blue bar at 50% means that that recipe produces(consumes) 50% of the product.\n\n" + "Orange bar means estimated recipe efficiency. If it is not full, the recipe looks inefficient to YAFC.\n\n" + "It is possible for a recipe to be efficient but not useful - for example a recipe that produces something that is not useful.\n\n" + "YAFC only estimates things that are required for science recipes. So buildings, belts, weapons, fuel - are not shown in estimations.", "Ok"); } - if (gui.BuildCheckBox("Current milestones info", atCurrentMilestones, out atCurrentMilestones, allocator:RectAllocator.RightRow)) + if (gui.BuildCheckBox("Current milestones info", atCurrentMilestones, out atCurrentMilestones, allocator: RectAllocator.RightRow)) { var item = current; current = null; diff --git a/YAFC/Windows/WelcomeScreen.cs b/YAFC/Windows/WelcomeScreen.cs index c0cbfb8d..028b2b97 100644 --- a/YAFC/Windows/WelcomeScreen.cs +++ b/YAFC/Windows/WelcomeScreen.cs @@ -18,9 +18,9 @@ public class WelcomeScreen : WindowUtility, IProgress<(string, string)> private bool expensive; private string createText; private bool canCreate; - private readonly VerticalScrollCustom errorScroll; - private readonly VerticalScrollCustom recentProjectScroll; - private readonly VerticalScrollCustom languageScroll; + private readonly ScrollArea errorScroll; + private readonly ScrollArea recentProjectScroll; + private readonly ScrollArea languageScroll; private string errorMod; private string errorMessage; private string tip; @@ -49,7 +49,7 @@ public class WelcomeScreen : WindowUtility, IProgress<(string, string)> {"tr", "Turkish"}, {"uk", "Ukrainian"}, }; - + private static readonly Dictionary languagesRequireFontOverride = new Dictionary() { {"ja", "Japanese"}, @@ -69,10 +69,10 @@ public WelcomeScreen() : base(ImGuiUtils.DefaultScreenPadding) RenderingUtils.SetColorScheme(Preferences.Instance.darkMode); var lastProject = Preferences.Instance.recentProjects.FirstOrDefault(); SetProject(lastProject); - errorScroll = new VerticalScrollCustom(20f, BuildError, collapsible:true); - recentProjectScroll = new VerticalScrollCustom(20f, BuildRecentProjectList, collapsible:true); - languageScroll = new VerticalScrollCustom(20f, LanguageSelection, collapsible: true); - Create("Welcome to YAFC v"+YafcLib.version.ToString(3), 45, null); + errorScroll = new ScrollArea(20f, BuildError, collapsible: true); + recentProjectScroll = new ScrollArea(20f, BuildRecentProjectList, collapsible: true); + languageScroll = new ScrollArea(20f, LanguageSelection, collapsible: true); + Create("Welcome to YAFC v" + YafcLib.version.ToString(3), 45, null); IconCollection.ClearCustomIcons(); if (tips == null) tips = File.ReadAllLines("Data/Tips.txt"); @@ -81,22 +81,22 @@ public WelcomeScreen() : base(ImGuiUtils.DefaultScreenPadding) private void BuildError(ImGui gui) { if (errorMod != null) - gui.BuildText("Error While loading mod "+errorMod, Font.text, align:RectAlignment.Middle, color:SchemeColor.Error); + gui.BuildText("Error While loading mod " + errorMod, Font.text, align: RectAlignment.Middle, color: SchemeColor.Error); gui.allocator = RectAllocator.Stretch; - gui.BuildText(errorMessage, Font.text, color:SchemeColor.ErrorText, wrap:true); + gui.BuildText(errorMessage, Font.text, color: SchemeColor.ErrorText, wrap: true); gui.DrawRectangle(gui.lastRect, SchemeColor.Error); } protected override void BuildContents(ImGui gui) { gui.spacing = 1.5f; - gui.BuildText("Yet Another Factorio Calculator", Font.header, align:RectAlignment.Middle); + gui.BuildText("Yet Another Factorio Calculator", Font.header, align: RectAlignment.Middle); if (loading) { - gui.BuildText(currentLoad1, align:RectAlignment.Middle); - gui.BuildText(currentLoad2, align:RectAlignment.Middle); + gui.BuildText(currentLoad1, align: RectAlignment.Middle); + gui.BuildText(currentLoad2, align: RectAlignment.Middle); gui.AllocateSpacing(15f); - gui.BuildText(tip, wrap:true, align:RectAlignment.Middle); + gui.BuildText(tip, wrap: true, align: RectAlignment.Middle); gui.SetNextRebuild(Ui.time + 30); } else if (errorMessage != null) @@ -113,8 +113,8 @@ protected override void BuildContents(ImGui gui) errorMessage = null; Rebuild(); } - } - else + } + else { BuildPathSelect(gui, ref path, "Project file location", "You can leave it empty for a new project", EditType.Workspace); BuildPathSelect(gui, ref dataPath, "Factorio Data location*\nIt should contain folders 'base' and 'core'", @@ -133,7 +133,7 @@ protected override void BuildContents(ImGui gui) gui.ShowDropDown(x => languageScroll.Build(x)); gui.BuildText("In-game objects language:"); } - + using (gui.EnterRow()) { if (Preferences.Instance.recentProjects.Length > 1) @@ -149,7 +149,7 @@ protected override void BuildContents(ImGui gui) RenderingUtils.SetColorScheme(Preferences.Instance.darkMode); Preferences.Instance.Save(); } - if (gui.RemainingRow().BuildButton(createText, active:canCreate)) + if (gui.RemainingRow().BuildButton(createText, active: canCreate)) LoadProject(); } } @@ -158,13 +158,13 @@ protected override void BuildContents(ImGui gui) private void ProjectErrorMoreInfo(ImGui gui) { gui.allocator = RectAllocator.LeftAlign; - gui.BuildText("Check that these mods load in Factorio", wrap:true); - gui.BuildText("YAFC only supports loading mods that were loaded in Factorio before. If you add or remove mods or change startup settings, you need to load those in Factorio and then close the game because Factorio writes some files only when exiting", wrap:true); - gui.BuildText("Check that Factorio loads mods from the same folder as YAFC", wrap:true); - gui.BuildText("If that doesn't help, try removing all the mods that are present but aren't loaded because they are disabled, don't have required dependencies, or (especially) have several versions", wrap:true); + gui.BuildText("Check that these mods load in Factorio", wrap: true); + gui.BuildText("YAFC only supports loading mods that were loaded in Factorio before. If you add or remove mods or change startup settings, you need to load those in Factorio and then close the game because Factorio writes some files only when exiting", wrap: true); + gui.BuildText("Check that Factorio loads mods from the same folder as YAFC", wrap: true); + gui.BuildText("If that doesn't help, try removing all the mods that are present but aren't loaded because they are disabled, don't have required dependencies, or (especially) have several versions", wrap: true); if (gui.BuildLink("If that doesn't help either, create a github issue")) Ui.VisitLink(AboutScreen.Github); - gui.BuildText("For these types of errors simple mod list will not be enough. You need to attach a 'New game' savegame for syncing mods, mod versions and mod settings.", wrap:true); + gui.BuildText("For these types of errors simple mod list will not be enough. You need to attach a 'New game' savegame for syncing mods, mod versions and mod settings.", wrap: true); } private void DoLanguageList(ImGui gui, Dictionary list, bool enabled) @@ -186,18 +186,18 @@ private void LanguageSelection(ImGui gui) { gui.spacing = 0f; gui.allocator = RectAllocator.LeftAlign; - gui.BuildText("Mods may not support your language, using English as a fallback.", wrap:true); + gui.BuildText("Mods may not support your language, using English as a fallback.", wrap: true); gui.AllocateSpacing(0.5f); - + DoLanguageList(gui, languageMapping, true); if (!Program.hasOverriddenFont) { gui.AllocateSpacing(0.5f); - gui.BuildText("To select languages with non-european glyphs you need to override used font first. Download or locate a font that has your language glyphs.", wrap:true); + gui.BuildText("To select languages with non-european glyphs you need to override used font first. Download or locate a font that has your language glyphs.", wrap: true); gui.AllocateSpacing(0.5f); } DoLanguageList(gui, languagesRequireFontOverride, Program.hasOverriddenFont); - + gui.AllocateSpacing(0.5f); if (gui.BuildButton("Select font to override")) SelectFont(); @@ -211,7 +211,7 @@ private void LanguageSelection(ImGui gui) Preferences.Instance.Save(); } } - gui.BuildText("Selecting font to override require YAFC restart to take effect", wrap:true); + gui.BuildText("Selecting font to override require YAFC restart to take effect", wrap: true); } private async void SelectFont() @@ -230,7 +230,7 @@ private async void SelectFont() public void Report((string, string) value) => (currentLoad1, currentLoad2) = value; private bool FactorioValid(string factorio) => !string.IsNullOrEmpty(factorio) && Directory.Exists(Path.Combine(factorio, "core")); private bool ModsValid(string mods) => string.IsNullOrEmpty(mods) || File.Exists(Path.Combine(mods, "mod-list.json")); - + private void ValidateSelection() { var factorioValid = FactorioValid(dataPath); @@ -238,7 +238,7 @@ private void ValidateSelection() var projectExists = File.Exists(path); if (projectExists) - createText = "Load '" + Path.GetFileNameWithoutExtension(path)+"'"; + createText = "Load '" + Path.GetFileNameWithoutExtension(path) + "'"; else if (path != "") { var directory = Path.GetDirectoryName(path); @@ -248,7 +248,7 @@ private void ValidateSelection() canCreate = false; return; } - createText = "Create '" + Path.GetFileNameWithoutExtension(path)+"'"; + createText = "Create '" + Path.GetFileNameWithoutExtension(path) + "'"; } else createText = "Create new project"; canCreate = factorioValid && modsValid; @@ -256,7 +256,7 @@ private void ValidateSelection() private void BuildPathSelect(ImGui gui, ref string path, string description, string placeholder, EditType editType) { - gui.BuildText(description, wrap:true); + gui.BuildText(description, wrap: true); gui.spacing = 0.5f; using (gui.EnterGroup(default, RectAllocator.RightRow)) { @@ -267,7 +267,7 @@ private void BuildPathSelect(ImGui gui, ref string path, string description, str } gui.spacing = 1.5f; } - + private void SetProject(RecentProject project) { expensive = project.expensive; @@ -284,7 +284,7 @@ private void SetProject(RecentProject project) rootGui.ClearFocus(); rootGui.Rebuild(); } - + private async void LoadProject() { try @@ -352,7 +352,7 @@ private async void ShowFileSelect(string description, string path, EditType type ValidateSelection(); } } - + private void BuildRecentProjectsDropdown(ImGui gui) { recentProjectScroll.Build(gui); diff --git a/YAFC/Workspace/ProductionTable/ProductionLinkSummaryScreen.cs b/YAFC/Workspace/ProductionTable/ProductionLinkSummaryScreen.cs index 33289eb6..911b502a 100644 --- a/YAFC/Workspace/ProductionTable/ProductionLinkSummaryScreen.cs +++ b/YAFC/Workspace/ProductionTable/ProductionLinkSummaryScreen.cs @@ -12,22 +12,22 @@ public class ProductionLinkSummaryScreen : PseudoScreen, IComparer<(RecipeRow ro private readonly List<(RecipeRow row, float flow)> input = new List<(RecipeRow, float)>(); private readonly List<(RecipeRow row, float flow)> output = new List<(RecipeRow, float)>(); private float totalInput, totalOutput; - private readonly VerticalScrollCustom scrollArea; + private readonly ScrollArea scrollArea; private ProductionLinkSummaryScreen() { - scrollArea = new VerticalScrollCustom(30, BuildScrollArea); + scrollArea = new ScrollArea(30, BuildScrollArea); } private void BuildScrollArea(ImGui gui) { - gui.BuildText("Production: "+DataUtils.FormatAmount(totalInput, link.goods.flowUnitOfMeasure), Font.subheader); + gui.BuildText("Production: " + DataUtils.FormatAmount(totalInput, link.goods.flowUnitOfMeasure), Font.subheader); BuildFlow(gui, input, totalInput); gui.spacing = 0.5f; - gui.BuildText("Consumption: "+DataUtils.FormatAmount(totalOutput, link.goods.flowUnitOfMeasure), Font.subheader); + gui.BuildText("Consumption: " + DataUtils.FormatAmount(totalOutput, link.goods.flowUnitOfMeasure), Font.subheader); BuildFlow(gui, output, totalOutput); if (link.flags.HasFlags(ProductionLink.Flags.LinkNotMatched) && totalInput != totalOutput) - gui.BuildText((totalInput > totalOutput ? "Overproduction: " : "Overconsumption: ") + DataUtils.FormatAmount(MathF.Abs(totalInput-totalOutput), link.goods.flowUnitOfMeasure), Font.subheader, color:SchemeColor.Error); + gui.BuildText((totalInput > totalOutput ? "Overproduction: " : "Overconsumption: ") + DataUtils.FormatAmount(MathF.Abs(totalInput - totalOutput), link.goods.flowUnitOfMeasure), Font.subheader, color: SchemeColor.Error); } public override void Build(ImGui gui) @@ -71,7 +71,7 @@ private void CalculateFlow(ProductionLink link) { input.Add((recipe, localFlow)); totalInput += localFlow; - } + } else if (localFlow < 0) { output.Add((recipe, -localFlow)); diff --git a/YAFCui/ImGui/ImGui.cs b/YAFCui/ImGui/ImGui.cs index ae05e4dc..83575816 100644 --- a/YAFCui/ImGui/ImGui.cs +++ b/YAFCui/ImGui/ImGui.cs @@ -47,26 +47,26 @@ public enum RectAllocator Center, LeftRow, RightRow, - RemainigRow, + RemainingRow, FixedRect, HalfRow } public delegate void GuiBuilder(ImGui gui); - + public sealed partial class ImGui : IDisposable, IPanel { - public ImGui(GuiBuilder gui, Padding padding, RectAllocator defaultAllocator = RectAllocator.Stretch, bool clip = false) + public ImGui(GuiBuilder guiBuilder, Padding padding, RectAllocator defaultAllocator = RectAllocator.Stretch, bool clip = false) { - this.gui = gui; - if (gui == null) + this.guiBuilder = guiBuilder; + if (guiBuilder == null) action = ImGuiAction.Build; this.defaultAllocator = defaultAllocator; this.clip = clip; initialPadding = padding; } - - public readonly GuiBuilder gui; + + public readonly GuiBuilder guiBuilder; public Window window { get; private set; } public ImGui parent { get; private set; } IPanel IPanel.Parent => parent; @@ -77,7 +77,7 @@ public ImGui(GuiBuilder gui, Padding padding, RectAllocator defaultAllocator = R private bool disposed; public Vector2 contentSize { get; private set; } public ImGuiAction action { get; private set; } - + public bool isBuilding { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -92,7 +92,7 @@ public bool isBuilding private Vector2 _offset; private Rect screenRect; private Rect localClip; - + public Vector2 offset { get => _offset; @@ -132,12 +132,12 @@ public void SetNextRebuild(long nextRebuildTime) window.SetNextRepaint(nextRebuildTime); } } - + public void Repaint() { window?.Repaint(); } - + public Vector2 CalculateState(float width, float pixelsPerUnit) { if (IsRebuildRequired() || buildWidth != width || this.pixelsPerUnit != pixelsPerUnit) @@ -147,7 +147,7 @@ public Vector2 CalculateState(float width, float pixelsPerUnit) } return contentSize; } - + public void Present(DrawingSurface surface, Rect position, Rect screenClip, ImGui parent) { if (parent != null) @@ -170,7 +170,7 @@ internal void InternalPresent(DrawingSurface surface, Rect position, Rect screen if (clip) prevClip = surface.SetClip(ToSdlRect(screenClip)); localClip = new Rect(screenClip.Position - screenOffset, screenClip.Size / scale); - var currentColor = (SchemeColor) (-1); + var currentColor = (SchemeColor)(-1); borders.Clear(); for (var i = rects.Count - 1; i >= 0; i--) { @@ -190,7 +190,7 @@ internal void InternalPresent(DrawingSurface surface, Rect position, Rect screen } SDL.SDL_RenderFillRect(renderer, ref sdlRect); } - + foreach (var (pos, icon, color) in icons) { if (!pos.IntersectsWith(localClip)) @@ -220,7 +220,7 @@ internal void InternalPresent(DrawingSurface surface, Rect position, Rect screen if (clip) surface.SetClip(prevClip); } - + public IPanel HitTest(Vector2 position) { position = position / scale - offset; @@ -234,7 +234,7 @@ public IPanel HitTest(Vector2 position) return this; } - public int UnitsToPixels(float units) => (int) MathF.Round(units * pixelsPerUnit); + public int UnitsToPixels(float units) => (int)MathF.Round(units * pixelsPerUnit); public float PixelsToUnits(int pixels) => pixels / pixelsPerUnit; public SDL.SDL_Rect ToSdlRect(Rect rect, Vector2 offset = default) { @@ -246,7 +246,7 @@ public SDL.SDL_Rect ToSdlRect(Rect rect, Vector2 offset = default) h = UnitsToPixels(rect.Height) }; } - + private static void CheckMainThread() { if (!Ui.IsMainThread()) @@ -272,13 +272,13 @@ public Vector2 FromWindowPosition(Vector2 windowPosition) return windowPosition; return (windowPosition - screenRect.Position) * (pixelsPerUnit / window.pixelsPerUnit); } - + private void ReleaseUnmanagedResources() { disposed = true; textCache.Dispose(); } - + public void Dispose() { ReleaseUnmanagedResources(); @@ -302,7 +302,7 @@ private void ExportDrawCommandsTo(List> sourceList, List> rects = new List>(); private readonly List> icons = new List>(); private readonly List> renderables = new List>(); @@ -75,10 +75,10 @@ private void ClearDrawCommandList() public void ManualDrawingClear() { - if (gui == null) + if (guiBuilder == null) ClearDrawCommandList(); } - + public readonly ImGuiCache.Cache textCache = new ImGuiCache.Cache(); public FontFile.FontSize GetFontSize(Font font = null) => (font ?? Font.text).GetFontSize(pixelsPerUnit); @@ -108,7 +108,7 @@ public Rect AllocateTextRect(out TextCache cache, string text, Font font = null, } else { - cache = textCache.GetCached((fontSize, text, wrap ? (uint) UnitsToPixels(MathF.Max(width, 5f)) : uint.MaxValue)); + cache = textCache.GetCached((fontSize, text, wrap ? (uint)UnitsToPixels(MathF.Max(width, 5f)) : uint.MaxValue)); rect = AllocateRect(cache.texRect.w / pixelsPerUnit, topOffset + cache.texRect.h / pixelsPerUnit, align); } @@ -141,7 +141,7 @@ public bool BuildTextInput(string text, out string newText, string placeholder, textInputHelper = new ImGuiTextInputHelper(this); return textInputHelper.BuildTextInput(text, out newText, placeholder, GetFontSize(), delayed, icon, padding, alignment, color); } - + public void BuildIcon(Icon icon, float size = 1.5f, SchemeColor color = SchemeColor.None) { if (color == SchemeColor.None) @@ -162,7 +162,7 @@ public void BuildIcon(Icon icon, float size = 1.5f, SchemeColor color = SchemeCo private bool DoGui(ImGuiAction action) { - if (gui == null) + if (guiBuilder == null) return false; this.action = action; ResetLayout(); @@ -170,7 +170,7 @@ private bool DoGui(ImGuiAction action) buildGroupsIndex = -1; using (EnterGroup(initialPadding, defaultAllocator, initialTextColor)) { - gui(this); + guiBuilder(this); } actionParameter = 0; if (action == ImGuiAction.Build) @@ -184,14 +184,14 @@ private bool DoGui(ImGuiAction action) private void BuildGui(float width) { - if (gui == null) + if (guiBuilder == null) return; buildWidth = width; nextRebuildTimer = long.MaxValue; rebuildRequested = false; ClearDrawCommandList(); DoGui(ImGuiAction.Build); - contentSize = new Vector2(buildingWidth, lastRect.Bottom); + contentSize = new Vector2(lastRect.Right, lastRect.Bottom); if (boxColor != SchemeColor.None) { var rect = new Rect(default, contentSize); @@ -252,7 +252,7 @@ public void MouseUp(int button) SDL.SDL_SetCursor(RenderingUtils.cursorHand); cursorSetByMouseDown = false; } - + actionParameter = button; DoGui(ImGuiAction.MouseUp); } @@ -354,7 +354,7 @@ public void SetFocus(Rect rect) mouseDownRect = rect; Rebuild(); } - + public void SetTextInputFocus(Rect rect, string text) { if (textInputHelper != null && InputSystem.Instance.currentKeyboardFocus != textInputHelper) @@ -366,7 +366,6 @@ public void SetTextInputFocus(Rect rect, string text) public void SaveToImage() { - } } } \ No newline at end of file diff --git a/YAFCui/ImGui/ImGuiLayout.cs b/YAFCui/ImGui/ImGuiLayout.cs index 7e21a329..ff483549 100644 --- a/YAFCui/ImGui/ImGuiLayout.cs +++ b/YAFCui/ImGui/ImGuiLayout.cs @@ -12,7 +12,7 @@ public partial class ImGui public Rect statePosition => new Rect(state.left, state.top, width, 0f); public ref RectAllocator allocator => ref state.allocator; public ref float spacing => ref state.spacing; - public Rect layoutRect => new Rect(state.left, state.top, state.bottom - state.top, state.right - state.left); + public Rect layoutRect => new Rect(state.left, state.top, state.right - state.left, state.bottom - state.top); private void ResetLayout() { @@ -38,7 +38,8 @@ public Rect AllocateRect(float width, float height, float spacing = float.Negati public Rect EncapsulateRect(Rect rect) { - return lastRect = state.EncapsulateRect(rect); + lastRect = state.EncapsulateRect(rect); + return lastRect; } public Rect AllocateRect(float width, float height, RectAlignment alignment, float spacing = float.NegativeInfinity) @@ -54,15 +55,15 @@ public static Rect AlignRect(Rect boundary, RectAlignment alignment, float width switch (alignment) { case RectAlignment.Middle: - return new Rect(boundary.X + (boundary.Width - width) * 0.5f, boundary.Y + (boundary.Height-height) * 0.5f, width, height); + return new Rect(boundary.X + (boundary.Width - width) * 0.5f, boundary.Y + (boundary.Height - height) * 0.5f, width, height); case RectAlignment.MiddleLeft: - return new Rect(boundary.X, boundary.Y + (boundary.Height-height) * 0.5f, width, height); + return new Rect(boundary.X, boundary.Y + (boundary.Height - height) * 0.5f, width, height); case RectAlignment.MiddleRight: - return new Rect(boundary.X, boundary.Y + (boundary.Height-height) * 0.5f, width, height); + return new Rect(boundary.X, boundary.Y + (boundary.Height - height) * 0.5f, width, height); case RectAlignment.UpperCenter: return new Rect(boundary.X + (boundary.Width - width) * 0.5f, boundary.Y, width, height); case RectAlignment.MiddleFullRow: - return new Rect(boundary.X, boundary.Y + (boundary.Height-height) * 0.5f, boundary.Width, height); + return new Rect(boundary.X, boundary.Y + (boundary.Height - height) * 0.5f, boundary.Width, height); default: return boundary; } @@ -71,7 +72,7 @@ public static Rect AlignRect(Rect boundary, RectAlignment alignment, float width public ImGui RemainingRow(float spacing = float.NegativeInfinity) { state.AllocateSpacing(spacing); - allocator = RectAllocator.RemainigRow; + allocator = RectAllocator.RemainingRow; return this; } @@ -102,7 +103,7 @@ public Context EnterFixedPositioning(float width, float height, Padding padding, state.textColor = textColor; return context; } - + private struct CopyableState { public RectAllocator allocator; @@ -121,23 +122,23 @@ public Rect AllocateRect(float width, float height, float spacing) switch (allocator) { case RectAllocator.Stretch: - return new Rect(left, top, right-left, height); + return new Rect(left, top, right - left, height); case RectAllocator.LeftAlign: return new Rect(left, top, width, height); case RectAllocator.RightAlign: - return new Rect(right-width, top, width, height); + return new Rect(right - width, top, width, height); case RectAllocator.Center: - return new Rect((right+left-width) * 0.5f, top, width, height); + return new Rect((right + left - width) * 0.5f, top, width, height); case RectAllocator.LeftRow: return new Rect(left, top, width, rowHeight); case RectAllocator.RightRow: - return new Rect(right-width, top, width, rowHeight); - case RectAllocator.RemainigRow: - return new Rect(left, top, right-left, rowHeight); + return new Rect(right - width, top, width, rowHeight); + case RectAllocator.RemainingRow: + return new Rect(left, top, right - left, rowHeight); case RectAllocator.FixedRect: - return new Rect(left, top, right-left, rowHeight); + return new Rect(left, top, right - left, rowHeight); case RectAllocator.HalfRow: - return new Rect(left, top, (right-left-spacing)/2f, rowHeight); + return new Rect(left, top, (right - left - spacing) / 2f, rowHeight); default: throw new ArgumentOutOfRangeException(); } @@ -166,7 +167,7 @@ public void AllocateSpacing(float amount = float.NegativeInfinity) break; } } - + public Rect EncapsulateRect(Rect rect) { contextRect = hasContent ? Rect.Union(contextRect, rect) : rect; @@ -179,28 +180,28 @@ public Rect EncapsulateRect(Rect rect) rect.Width = right - left; break; case RectAllocator.RightAlign: - top = bottom = MathF.Max(rect.Bottom, top);; + top = bottom = MathF.Max(rect.Bottom, top); ; rect.Right = right; break; case RectAllocator.LeftAlign: - top = bottom = MathF.Max(rect.Bottom, top);; + top = bottom = MathF.Max(rect.Bottom, top); ; rect.Left = left; break; case RectAllocator.Center: - top = bottom = MathF.Max(rect.Bottom, top);; + top = bottom = MathF.Max(rect.Bottom, top); ; break; case RectAllocator.LeftRow: left = rect.Right; - bottom = MathF.Max(rect.Bottom, bottom);; + bottom = MathF.Max(rect.Bottom, bottom); ; break; case RectAllocator.RightRow: right = rect.Left; - bottom = MathF.Max(rect.Bottom, bottom);; + bottom = MathF.Max(rect.Bottom, bottom); ; break; case RectAllocator.HalfRow: - allocator = RectAllocator.RemainigRow; + allocator = RectAllocator.RemainingRow; left = rect.Right + spacing; - bottom = MathF.Max(rect.Bottom, bottom);; + bottom = MathF.Max(rect.Bottom, bottom); ; break; } @@ -240,7 +241,10 @@ public void Dispose() rect.Width += (padding.left + padding.right); rect.Height += (padding.top + padding.bottom); if (hasContent) - gui.lastRect = gui.state.EncapsulateRect(rect); + { + gui.state.EncapsulateRect(rect); + gui.lastRect = rect; + } else gui.lastRect = default; } diff --git a/YAFCui/ImGui/ScrollArea.cs b/YAFCui/ImGui/ScrollArea.cs index 82ae7f91..f8659de0 100644 --- a/YAFCui/ImGui/ScrollArea.cs +++ b/YAFCui/ImGui/ScrollArea.cs @@ -77,20 +77,20 @@ public void Build(ImGui gui, float height) { if (horizontal && maxScroll.X > 0f) { - var fullScrollRect = new Rect(rect.X, rect.Bottom-ScrollbarSize, rect.Width, ScrollbarSize); + var fullScrollRect = new Rect(rect.X, rect.Bottom - ScrollbarSize, rect.Width, ScrollbarSize); var scrollRect = new Rect(rect.X + scrollStart.X, fullScrollRect.Y, scrollSize.X, ScrollbarSize); BuildScrollBar(gui, 0, in fullScrollRect, in scrollRect); } if (vertical && maxScroll.Y > 0f) { - var fullScrollRect = new Rect(rect.Right-ScrollbarSize, rect.Y, ScrollbarSize, rect.Height); + var fullScrollRect = new Rect(rect.Right - ScrollbarSize, rect.Y, ScrollbarSize, rect.Height); var scrollRect = new Rect(fullScrollRect.X, rect.Y + scrollStart.Y, ScrollbarSize, scrollSize.Y); BuildScrollBar(gui, 1, in fullScrollRect, in scrollRect); } } } - + private void BuildScrollBar(ImGui gui, int axis, in Rect fullScrollRect, in Rect scrollRect) { switch (gui.action) @@ -132,7 +132,7 @@ public float scroll get => _scroll.Y; set => scroll2d = new Vector2(_scroll.X, value); } - + public float scrollX { get => _scroll.X; @@ -175,17 +175,17 @@ public bool KeyDown(SDL.SDL_Keysym key) public bool TextInput(string input) => false; public bool KeyUp(SDL.SDL_Keysym key) => false; - public void FocusChanged(bool focused) {} + public void FocusChanged(bool focused) { } } - - public abstract class ScrollArea : Scrollable + + public abstract class ScrollAreaBase : Scrollable { - protected readonly ImGui contents; + protected ImGui contents; protected readonly float height; - public ScrollArea(float height, Padding padding, bool collapsible = false, bool vertical = true, bool horizontal = false) : base(vertical, horizontal, collapsible) + public ScrollAreaBase(float height, Padding padding, bool collapsible = false, bool vertical = true, bool horizontal = false) : base(vertical, horizontal, collapsible) { - contents = new ImGui(BuildContents, padding, clip:true); + contents = new ImGui(BuildContents, padding, clip: true); this.height = height; } @@ -197,7 +197,7 @@ protected override void PositionContent(ImGui gui, Rect viewport) public void Build(ImGui gui) => Build(gui, height); protected abstract void BuildContents(ImGui gui); - + public void RebuildContents() { contents.Rebuild(); @@ -209,11 +209,11 @@ protected override Vector2 MeasureContent(Rect rect, ImGui gui) } } - public class VerticalScrollCustom : ScrollArea + public class ScrollArea : ScrollAreaBase { private readonly GuiBuilder builder; - public VerticalScrollCustom(float height, GuiBuilder builder, Padding padding = default, bool collapsible = false) : base(height, padding, collapsible) + public ScrollArea(float height, GuiBuilder builder, Padding padding = default, bool collapsible = false, bool vertical = true, bool horizontal = false) : base(height, padding, collapsible, vertical, horizontal) { this.builder = builder; } @@ -222,7 +222,7 @@ public VerticalScrollCustom(float height, GuiBuilder builder, Padding padding = public void Rebuild() => contents.Rebuild(); } - public class VirtualScrollList : ScrollArea + public class VirtualScrollList : ScrollAreaBase { private readonly Vector2 elementSize; protected readonly int bufferRows; From b4794c3573340c853274f96a3f505062c6daca09 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Sun, 2 Jan 2022 14:50:38 +0100 Subject: [PATCH 09/16] Make Summary table scrollable (horizontal and vertical) and add proper header/title text Note that the horizontal scrollbar is not clickable (ctrl+scroll and keyboard scrolling is working) --- YAFC/Workspace/SummaryView.cs | 37 ++++++++++++++++++++++++++++------- YAFCmodel/Model/Summary.cs | 2 +- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index 4c616ad6..0ebd2f43 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -49,7 +49,7 @@ public override void BuildElement(ImGui gui, ProjectPage page) } var table = page.content as ProductionTable; - using var grid = gui.EnterInlineGrid(3f, 1f); + using var grid = gui.EnterInlineGrid(ElementWidth, 1f); foreach (KeyValuePair entry in view.allGoods) { float amountAvailable = YAFCRounding((entry.Value.totalProvided > 0 ? entry.Value.totalProvided : 0) + entry.Value.extraProduced); @@ -60,12 +60,13 @@ public override void BuildElement(ImGui gui, ProjectPage page) } grid.Next(); + bool enoughProduced = amountAvailable >= amountNeeded; ProductionLink link = table.links.Find(x => x.goods.name == entry.Key); if (link != null) { if (link.amount != 0f) { - DrawProvideProduct(gui, link, page, entry.Value.extraProduced, amountAvailable >= amountNeeded); + DrawProvideProduct(gui, link, page, entry.Value.extraProduced, enoughProduced); } } else @@ -75,19 +76,20 @@ public override void BuildElement(ImGui gui, ProjectPage page) ProductionTableFlow flow = Array.Find(table.flow, x => x.goods.name == entry.Key); if (Math.Abs(flow.amount) > Epsilon) { - DrawRequestProduct(gui, flow, amountAvailable >= amountNeeded); + + DrawRequestProduct(gui, flow, enoughProduced); } } } } } - private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page, float extraProduced, bool enoughOutput) + private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page, float extraProduced, bool enoughProduced) { gui.allocator = RectAllocator.Stretch; gui.spacing = 0f; - GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, (element.amount > 0 && enoughOutput) || (element.amount < 0 && extraProduced == -element.amount) ? SchemeColor.Primary : SchemeColor.Error); + GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, (element.amount > 0 && enoughProduced) || (element.amount < 0 && extraProduced == -element.amount) ? SchemeColor.Primary : SchemeColor.Error); if (evt == GoodsWithAmountEvent.TextEditing && newAmount != 0) { element.RecordUndo().amount = newAmount; @@ -109,12 +111,13 @@ static private void DrawRequestProduct(ImGui gui, ProductionTableFlow flow, bool private void RebuildInvoked(bool visualOnly = false) { view.Rebuild(visualOnly); + view.scrollArea.RebuildContents(); invokedPage.contentChanged -= RebuildInvoked; } } static readonly float Epsilon = 1e-5f; - + static readonly float ElementWidth = 3; struct GoodDetails { public float totalProvided; @@ -124,6 +127,8 @@ struct GoodDetails private readonly MainScreen screen; + private readonly ScrollArea scrollArea; + private readonly SummaryDataColumn goodsColumn; private readonly DataGrid mainGrid; private readonly Dictionary allGoods = new Dictionary(); @@ -132,11 +137,14 @@ struct GoodDetails public SummaryView(MainScreen screen) { this.screen = screen; + goodsColumn = new SummaryDataColumn(this); var columns = new TextDataColumn[] { new SummaryTabColumn(), - new SummaryDataColumn(this), + goodsColumn, }; + // TODO Make height relative to window height instead of fixed + scrollArea = new ScrollArea(30, BuildScrollArea, vertical: true, horizontal: true); mainGrid = new DataGrid(columns); } @@ -144,6 +152,15 @@ protected override void BuildPageTooltip(ImGui gui, Summary contents) { } + protected override void BuildHeader(ImGui gui) + { + base.BuildHeader(gui); + + gui.allocator = RectAllocator.Center; + gui.BuildText("Production Sheet Summary", Font.header, false, RectAlignment.Middle); + gui.allocator = RectAllocator.LeftAlign; + } + protected override void BuildContent(ImGui gui) { // TODO Can we detect if things changed? @@ -188,6 +205,12 @@ protected override void BuildContent(ImGui gui) } } + goodsColumn.width = allGoods.Count * ElementWidth; + scrollArea.Build(gui); + } + + private void BuildScrollArea(ImGui gui) + { foreach (Guid displayPage in screen.project.displayPages) { ProjectPage page = screen.project.FindPage(displayPage); diff --git a/YAFCmodel/Model/Summary.cs b/YAFCmodel/Model/Summary.cs index 7eea9c58..4f83add9 100644 --- a/YAFCmodel/Model/Summary.cs +++ b/YAFCmodel/Model/Summary.cs @@ -9,7 +9,7 @@ public Summary(ModelObject page) : base(page) { } public override async Task Solve(ProjectPage page) { - return "Summary"; + return null; } } } \ No newline at end of file From 561c0ea62719cb8a0a655a9dfee67b9ec4fe6869 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Sun, 2 Jan 2022 20:54:43 +0100 Subject: [PATCH 10/16] Use event system to only recalculate/prepare when needed --- YAFC/Windows/MainScreen.cs | 5 ++- YAFC/Workspace/SummaryView.cs | 74 ++++++++++++++++++++++------------- 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/YAFC/Windows/MainScreen.cs b/YAFC/Windows/MainScreen.cs index a1b7b751..6eab6cb3 100644 --- a/YAFC/Windows/MainScreen.cs +++ b/YAFC/Windows/MainScreen.cs @@ -32,6 +32,7 @@ public class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string) private ProjectPage _secondaryPage; public ProjectPage secondaryPage => _secondaryPage; private ProjectPageView secondaryPageView; + private readonly SummaryView summaryView; private bool analysisUpdatePending; private SearchQuery pageSearch; @@ -45,10 +46,11 @@ public class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string) public MainScreen(int display, Project project) : base(default) { + summaryView = new SummaryView(); RegisterPageView(new ProductionTableView()); RegisterPageView(new AutoPlannerView()); RegisterPageView(new ProductionSummaryView()); - RegisterPageView(new SummaryView(this)); + RegisterPageView(summaryView); searchGui = new ImGui(BuildSearch, new Padding(1f)) { boxShadow = RectangleBorder.Thin, boxColor = SchemeColor.Background }; Instance = this; tabBar = new MainScreenTabBar(this); @@ -95,6 +97,7 @@ private void SetProject(Project project) project.metaInfoChanged += ProjectOnMetaInfoChanged; project.settings.changed += ProjectSettingsChanged; InputSystem.Instance.SetDefaultKeyboardFocus(this); + summaryView.SetProject(project); } private void ProjectSettingsChanged(bool visualOnly) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index 0ebd2f43..a693db48 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -34,7 +34,6 @@ public override void BuildElement(ImGui gui, ProjectPage page) private class SummaryDataColumn : TextDataColumn { protected readonly SummaryView view; - private ProjectPage invokedPage; public SummaryDataColumn(SummaryView view) : base("Linked", float.MaxValue) { @@ -84,7 +83,7 @@ public override void BuildElement(ImGui gui, ProjectPage page) } } - private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page, float extraProduced, bool enoughProduced) + static private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page, float extraProduced, bool enoughProduced) { gui.allocator = RectAllocator.Stretch; gui.spacing = 0f; @@ -94,8 +93,6 @@ private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage p { element.RecordUndo().amount = newAmount; // Hack: Force recalculate the page (and make sure to catch the content change event caused by the recalculation) - invokedPage = page; - page.contentChanged += RebuildInvoked; page.SetActive(true); page.SetToRecalculate(); page.SetActive(false); @@ -107,13 +104,6 @@ static private void DrawRequestProduct(ImGui gui, ProductionTableFlow flow, bool gui.spacing = 0f; gui.BuildFactorioObjectWithAmount(flow.goods, -flow.amount, flow.goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, flow.amount > Epsilon ? enoughProduced ? SchemeColor.Green : SchemeColor.Error : SchemeColor.None); } - - private void RebuildInvoked(bool visualOnly = false) - { - view.Rebuild(visualOnly); - view.scrollArea.RebuildContents(); - invokedPage.contentChanged -= RebuildInvoked; - } } static readonly float Epsilon = 1e-5f; @@ -125,7 +115,7 @@ struct GoodDetails public float extraProduced; } - private readonly MainScreen screen; + private Project project; private readonly ScrollArea scrollArea; private readonly SummaryDataColumn goodsColumn; @@ -134,20 +124,39 @@ struct GoodDetails private readonly Dictionary allGoods = new Dictionary(); - public SummaryView(MainScreen screen) + public SummaryView() { - this.screen = screen; goodsColumn = new SummaryDataColumn(this); var columns = new TextDataColumn[] { new SummaryTabColumn(), goodsColumn, }; - // TODO Make height relative to window height instead of fixed + // TODO Make height relative to min(window,content) height instead of fixed scrollArea = new ScrollArea(30, BuildScrollArea, vertical: true, horizontal: true); mainGrid = new DataGrid(columns); } + public void SetProject(Project project) + { + if (this.project != null) + { + this.project.metaInfoChanged -= Recalculate; + foreach (ProjectPage page in project.pages) + { + page.contentChanged -= Recalculate; + } + } + + this.project = project; + + project.metaInfoChanged += Recalculate; + foreach (ProjectPage page in project.pages) + { + page.contentChanged += Recalculate; + } + } + protected override void BuildPageTooltip(ImGui gui, Summary contents) { } @@ -163,11 +172,29 @@ protected override void BuildHeader(ImGui gui) protected override void BuildContent(ImGui gui) { - // TODO Can we detect if things changed? + scrollArea.Build(gui); + } + + private void BuildScrollArea(ImGui gui) + { + foreach (Guid displayPage in project.displayPages) + { + ProjectPage page = project.FindPage(displayPage); + if (page?.contentType != typeof(ProductionTable)) + continue; + + mainGrid.BuildRow(gui, page); + } + } + + private void Recalculate() => Recalculate(false); + + private void Recalculate(bool visualOnly) + { allGoods.Clear(); - foreach (Guid displayPage in screen.project.displayPages) + foreach (Guid displayPage in project.displayPages) { - ProjectPage page = screen.project.FindPage(displayPage); + ProjectPage page = project.FindPage(displayPage); ProductionTable content = page?.content as ProductionTable; if (content == null) { @@ -206,16 +233,9 @@ protected override void BuildContent(ImGui gui) } goodsColumn.width = allGoods.Count * ElementWidth; - scrollArea.Build(gui); - } - private void BuildScrollArea(ImGui gui) - { - foreach (Guid displayPage in screen.project.displayPages) - { - ProjectPage page = screen.project.FindPage(displayPage); - mainGrid.BuildRow(gui, page); - } + Rebuild(visualOnly); + scrollArea.RebuildContents(); } // Convert/truncate value as shown in UI to prevent slight mismatches From adb7ce6ebf1cbb4391260c04496a53fd11d3f691 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Sun, 2 Jan 2022 22:41:10 +0100 Subject: [PATCH 11/16] Add checkbox to only show goods with 'issues' (and fix width calculation of table) --- YAFC/Workspace/SummaryView.cs | 25 ++++++++++++++++++++++--- YAFCmodel/Model/Summary.cs | 2 ++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index a693db48..1c241660 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -48,12 +48,12 @@ public override void BuildElement(ImGui gui, ProjectPage page) } var table = page.content as ProductionTable; - using var grid = gui.EnterInlineGrid(ElementWidth, 1f); + using var grid = gui.EnterInlineGrid(ElementWidth, ElementSpacing); foreach (KeyValuePair entry in view.allGoods) { float amountAvailable = YAFCRounding((entry.Value.totalProvided > 0 ? entry.Value.totalProvided : 0) + entry.Value.extraProduced); float amountNeeded = YAFCRounding((entry.Value.totalProvided < 0 ? -entry.Value.totalProvided : 0) + entry.Value.totalNeeded); - if (Math.Abs(amountAvailable - amountNeeded) < Epsilon || amountNeeded == 0) + if (view.model.showOnlyIssues && (Math.Abs(amountAvailable - amountNeeded) < Epsilon || amountNeeded == 0)) { continue; } @@ -108,6 +108,7 @@ static private void DrawRequestProduct(ImGui gui, ProductionTableFlow flow, bool static readonly float Epsilon = 1e-5f; static readonly float ElementWidth = 3; + static readonly float ElementSpacing = 1; struct GoodDetails { public float totalProvided; @@ -172,6 +173,12 @@ protected override void BuildHeader(ImGui gui) protected override void BuildContent(ImGui gui) { + if (gui.BuildCheckBox("Only show issues", model.showOnlyIssues, out bool newValue)) + { + model.showOnlyIssues = newValue; + Recalculate(); + } + scrollArea.Build(gui); } @@ -232,7 +239,19 @@ private void Recalculate(bool visualOnly) } } - goodsColumn.width = allGoods.Count * ElementWidth; + int count = 0; + foreach (KeyValuePair entry in allGoods) + { + float amountAvailable = YAFCRounding((entry.Value.totalProvided > 0 ? entry.Value.totalProvided : 0) + entry.Value.extraProduced); + float amountNeeded = YAFCRounding((entry.Value.totalProvided < 0 ? -entry.Value.totalProvided : 0) + entry.Value.totalNeeded); + if (model != null && model.showOnlyIssues && (Math.Abs(amountAvailable - amountNeeded) < Epsilon || amountNeeded == 0)) + { + continue; + } + count++; + } + + goodsColumn.width = count * (ElementWidth + ElementSpacing); Rebuild(visualOnly); scrollArea.RebuildContents(); diff --git a/YAFCmodel/Model/Summary.cs b/YAFCmodel/Model/Summary.cs index 4f83add9..1a936a48 100644 --- a/YAFCmodel/Model/Summary.cs +++ b/YAFCmodel/Model/Summary.cs @@ -5,6 +5,8 @@ namespace YAFC.Model public class Summary : ProjectPageContents { + public bool showOnlyIssues { get; set; } + public Summary(ModelObject page) : base(page) { } public override async Task Solve(ProjectPage page) From 57b88a7aa5f32f6c21b63fa7b10785ca8537f3b8 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Mon, 3 Jan 2022 21:47:28 +0100 Subject: [PATCH 12/16] Keep original lastRect to fix some drawing glitches (e.g. in ErrorRow) Adding a lastContentRect which contains the (correct/out-of-window) size, fixes the contentSize calculation in BuildGui(). I did not see any glich(es) anymore, and the ScrollArea size is set correctly now (showing scrollbars if needed) --- YAFCui/ImGui/ImGuiBuilding.cs | 2 +- YAFCui/ImGui/ImGuiLayout.cs | 5 +++-- YAFCui/ImGui/ScrollArea.cs | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/YAFCui/ImGui/ImGuiBuilding.cs b/YAFCui/ImGui/ImGuiBuilding.cs index cc918a60..412823e5 100644 --- a/YAFCui/ImGui/ImGuiBuilding.cs +++ b/YAFCui/ImGui/ImGuiBuilding.cs @@ -191,7 +191,7 @@ private void BuildGui(float width) rebuildRequested = false; ClearDrawCommandList(); DoGui(ImGuiAction.Build); - contentSize = new Vector2(lastRect.Right, lastRect.Bottom); + contentSize = new Vector2(lastContentRect.Width, lastContentRect.Height); if (boxColor != SchemeColor.None) { var rect = new Rect(default, contentSize); diff --git a/YAFCui/ImGui/ImGuiLayout.cs b/YAFCui/ImGui/ImGuiLayout.cs index ff483549..30b1e163 100644 --- a/YAFCui/ImGui/ImGuiLayout.cs +++ b/YAFCui/ImGui/ImGuiLayout.cs @@ -8,6 +8,7 @@ public partial class ImGui { private CopyableState state; public Rect lastRect { get; set; } + public Rect lastContentRect { get; set; } public float width => state.right - state.left; public Rect statePosition => new Rect(state.left, state.top, width, 0f); public ref RectAllocator allocator => ref state.allocator; @@ -242,8 +243,8 @@ public void Dispose() rect.Height += (padding.top + padding.bottom); if (hasContent) { - gui.state.EncapsulateRect(rect); - gui.lastRect = rect; + gui.lastRect = gui.state.EncapsulateRect(rect); + gui.lastContentRect = rect; } else gui.lastRect = default; } diff --git a/YAFCui/ImGui/ScrollArea.cs b/YAFCui/ImGui/ScrollArea.cs index f8659de0..9ed0759e 100644 --- a/YAFCui/ImGui/ScrollArea.cs +++ b/YAFCui/ImGui/ScrollArea.cs @@ -219,7 +219,7 @@ public ScrollArea(float height, GuiBuilder builder, Padding padding = default, b } protected override void BuildContents(ImGui gui) => builder(gui); - public void Rebuild() => contents.Rebuild(); + public void Rebuild() => RebuildContents(); } public class VirtualScrollList : ScrollAreaBase @@ -240,7 +240,7 @@ public float spacing set { _spacing = value; - contents.Rebuild(); + RebuildContents(); } } @@ -252,7 +252,7 @@ public IReadOnlyList data set { _data = value ?? Array.Empty(); - contents.Rebuild(); + RebuildContents(); } } @@ -275,7 +275,7 @@ public override Vector2 scroll2d base.scroll2d = value; var row = CalcFirstBlock(); if (row != firstVisibleBlock) - contents.Rebuild(); + RebuildContents(); } } From 82e191ab6d994df81d9c2c1f9644b59dd5d317ba Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Tue, 18 Jan 2022 22:03:00 +0100 Subject: [PATCH 13/16] Calculate required amount when click on a link icon --- YAFC/Workspace/SummaryView.cs | 40 +++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index 1c241660..b3381cd4 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -49,10 +49,10 @@ public override void BuildElement(ImGui gui, ProjectPage page) var table = page.content as ProductionTable; using var grid = gui.EnterInlineGrid(ElementWidth, ElementSpacing); - foreach (KeyValuePair entry in view.allGoods) + foreach (KeyValuePair goodInfo in view.allGoods) { - float amountAvailable = YAFCRounding((entry.Value.totalProvided > 0 ? entry.Value.totalProvided : 0) + entry.Value.extraProduced); - float amountNeeded = YAFCRounding((entry.Value.totalProvided < 0 ? -entry.Value.totalProvided : 0) + entry.Value.totalNeeded); + float amountAvailable = YAFCRounding((goodInfo.Value.totalProvided > 0 ? goodInfo.Value.totalProvided : 0) + goodInfo.Value.extraProduced); + float amountNeeded = YAFCRounding((goodInfo.Value.totalProvided < 0 ? -goodInfo.Value.totalProvided : 0) + goodInfo.Value.totalNeeded); if (view.model.showOnlyIssues && (Math.Abs(amountAvailable - amountNeeded) < Epsilon || amountNeeded == 0)) { continue; @@ -60,19 +60,19 @@ public override void BuildElement(ImGui gui, ProjectPage page) grid.Next(); bool enoughProduced = amountAvailable >= amountNeeded; - ProductionLink link = table.links.Find(x => x.goods.name == entry.Key); + ProductionLink link = table.links.Find(x => x.goods.name == goodInfo.Key); if (link != null) { if (link.amount != 0f) { - DrawProvideProduct(gui, link, page, entry.Value.extraProduced, enoughProduced); + DrawProvideProduct(gui, link, page, goodInfo.Value, enoughProduced); } } else { - if (Array.Exists(table.flow, x => x.goods.name == entry.Key)) + if (Array.Exists(table.flow, x => x.goods.name == goodInfo.Key)) { - ProductionTableFlow flow = Array.Find(table.flow, x => x.goods.name == entry.Key); + ProductionTableFlow flow = Array.Find(table.flow, x => x.goods.name == goodInfo.Key); if (Math.Abs(flow.amount) > Epsilon) { @@ -83,19 +83,19 @@ public override void BuildElement(ImGui gui, ProjectPage page) } } - static private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page, float extraProduced, bool enoughProduced) + static private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page, GoodDetails goodInfo, bool enoughProduced) { gui.allocator = RectAllocator.Stretch; gui.spacing = 0f; - GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, (element.amount > 0 && enoughProduced) || (element.amount < 0 && extraProduced == -element.amount) ? SchemeColor.Primary : SchemeColor.Error); + GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, (element.amount > 0 && enoughProduced) || (element.amount < 0 && goodInfo.extraProduced == -element.amount) ? SchemeColor.Primary : SchemeColor.Error); if (evt == GoodsWithAmountEvent.TextEditing && newAmount != 0) { - element.RecordUndo().amount = newAmount; - // Hack: Force recalculate the page (and make sure to catch the content change event caused by the recalculation) - page.SetActive(true); - page.SetToRecalculate(); - page.SetActive(false); + SetProviderAmount(element, page, newAmount); + } + else if (evt == GoodsWithAmountEvent.ButtonClick) + { + SetProviderAmount(element, page, YAFCRounding(goodInfo.sum)); } } static private void DrawRequestProduct(ImGui gui, ProductionTableFlow flow, bool enoughProduced) @@ -104,6 +104,15 @@ static private void DrawRequestProduct(ImGui gui, ProductionTableFlow flow, bool gui.spacing = 0f; gui.BuildFactorioObjectWithAmount(flow.goods, -flow.amount, flow.goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, flow.amount > Epsilon ? enoughProduced ? SchemeColor.Green : SchemeColor.Error : SchemeColor.None); } + + static private void SetProviderAmount(ProductionLink element, ProjectPage page, float newAmount) + { + element.RecordUndo().amount = newAmount; + // Hack: Force recalculate the page (and make sure to catch the content change event caused by the recalculation) + page.SetActive(true); + page.SetToRecalculate(); + page.SetActive(false); + } } static readonly float Epsilon = 1e-5f; @@ -114,6 +123,7 @@ struct GoodDetails public float totalProvided; public float totalNeeded; public float extraProduced; + public float sum; } private Project project; @@ -224,6 +234,7 @@ private void Recalculate(bool visualOnly) { GoodDetails value = allGoods.GetValueOrDefault(flow.goods.name); value.totalNeeded -= YAFCRounding(flow.amount); ; + value.sum -= YAFCRounding(flow.amount); ; allGoods[flow.goods.name] = value; } else if (flow.amount > Epsilon) @@ -233,6 +244,7 @@ private void Recalculate(bool visualOnly) // Only count extras if not linked GoodDetails value = allGoods.GetValueOrDefault(flow.goods.name); value.extraProduced += YAFCRounding(flow.amount); + value.sum -= YAFCRounding(flow.amount); allGoods[flow.goods.name] = value; } } From 7bb0da7d34dd6ce2eed5f09efa9e8c834370b3a2 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Wed, 19 Jan 2022 00:45:43 +0100 Subject: [PATCH 14/16] Support the search box (ctrl+F) --- YAFC/Workspace/SummaryView.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index b3381cd4..5f488912 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -51,6 +51,11 @@ public override void BuildElement(ImGui gui, ProjectPage page) using var grid = gui.EnterInlineGrid(ElementWidth, ElementSpacing); foreach (KeyValuePair goodInfo in view.allGoods) { + if (!view.searchQuery.Match(goodInfo.Key)) + { + continue; + } + float amountAvailable = YAFCRounding((goodInfo.Value.totalProvided > 0 ? goodInfo.Value.totalProvided : 0) + goodInfo.Value.extraProduced); float amountNeeded = YAFCRounding((goodInfo.Value.totalProvided < 0 ? -goodInfo.Value.totalProvided : 0) + goodInfo.Value.totalNeeded); if (view.model.showOnlyIssues && (Math.Abs(amountAvailable - amountNeeded) < Epsilon || amountNeeded == 0)) @@ -127,6 +132,7 @@ struct GoodDetails } private Project project; + private SearchQuery searchQuery; private readonly ScrollArea scrollArea; private readonly SummaryDataColumn goodsColumn; @@ -279,6 +285,13 @@ static private float YAFCRounding(float value) return result; } + public override void SetSearchQuery(SearchQuery query) + { + searchQuery = query; + bodyContent.Rebuild(); + scrollArea.Rebuild(); + } + public override void CreateModelDropdown(ImGui gui, Type type, Project project) { } From fce3816f8734dc704a8041b4cf7df31bbe43bab4 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Mon, 12 Dec 2022 20:08:46 +0100 Subject: [PATCH 15/16] Adjust Summary View to window height It is a bit hackish but it seems to work (after updating the UI to trigger some 'recalculation') Fixes: #2 --- YAFC/Workspace/SummaryView.cs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs index 5f488912..5c4c46fc 100644 --- a/YAFC/Workspace/SummaryView.cs +++ b/YAFC/Workspace/SummaryView.cs @@ -7,6 +7,21 @@ namespace YAFC { public class SummaryView : ProjectPageView { + private class SummaryScrollArea : ScrollArea + { + static float DefaultHeight = 10; + + public SummaryScrollArea(GuiBuilder builder) : base(DefaultHeight, builder, default, false, true, true) + { + } + + public new void Build(ImGui gui) + { + // Maximize scroll area to fit parent area (minus header and 'show issues' heights, and some (2) padding probably) + Build(gui, gui.valid ? gui.parent.contentSize.Y - HeaderFont.size - Font.text.size - ScrollbarSize - 2 : DefaultHeight); + } + } + private class SummaryTabColumn : TextDataColumn { public SummaryTabColumn() : base("Tab", 6f) @@ -131,10 +146,12 @@ struct GoodDetails public float sum; } + static Font HeaderFont = Font.header; + private Project project; private SearchQuery searchQuery; - private readonly ScrollArea scrollArea; + private readonly SummaryScrollArea scrollArea; private readonly SummaryDataColumn goodsColumn; private readonly DataGrid mainGrid; @@ -149,8 +166,7 @@ public SummaryView() new SummaryTabColumn(), goodsColumn, }; - // TODO Make height relative to min(window,content) height instead of fixed - scrollArea = new ScrollArea(30, BuildScrollArea, vertical: true, horizontal: true); + scrollArea = new SummaryScrollArea(BuildScrollArea); mainGrid = new DataGrid(columns); } @@ -183,7 +199,7 @@ protected override void BuildHeader(ImGui gui) base.BuildHeader(gui); gui.allocator = RectAllocator.Center; - gui.BuildText("Production Sheet Summary", Font.header, false, RectAlignment.Middle); + gui.BuildText("Production Sheet Summary", HeaderFont, false, RectAlignment.Middle); gui.allocator = RectAllocator.LeftAlign; } From b5d24b04ecaa05110d128b047dea07fea64eb916 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Mon, 23 Jan 2023 17:22:48 +0100 Subject: [PATCH 16/16] Fix error when SummaryView is set as secondary screen --- YAFC/Windows/MainScreen.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/YAFC/Windows/MainScreen.cs b/YAFC/Windows/MainScreen.cs index 6eab6cb3..124de2be 100644 --- a/YAFC/Windows/MainScreen.cs +++ b/YAFC/Windows/MainScreen.cs @@ -51,6 +51,9 @@ public MainScreen(int display, Project project) : base(default) RegisterPageView(new AutoPlannerView()); RegisterPageView(new ProductionSummaryView()); RegisterPageView(summaryView); + // HACK: SummaryView does not like it to be cloned to the cloned as a secondary page, probably due to its contents (type) not set properly? + // Manually set SummaryView as a secondary page type with the correct key (type) + secondaryPageViews[typeof(Summary)] = summaryView; searchGui = new ImGui(BuildSearch, new Padding(1f)) { boxShadow = RectangleBorder.Thin, boxColor = SchemeColor.Background }; Instance = this; tabBar = new MainScreenTabBar(this);