From fa3d8becf98794be3ba19ca0aa7c9c243c97913b Mon Sep 17 00:00:00 2001 From: Dale McCoy <21223975+DaleStan@users.noreply.github.com> Date: Tue, 14 May 2024 00:03:43 -0400 Subject: [PATCH 1/8] Allow searching by page contents. In addition to desired and extra products, allow searching by ingredients and recipe. --- Yafc/Windows/MainScreen.PageListSearch.cs | 101 ++++++++++++++++++++++ Yafc/Windows/MainScreen.cs | 19 ++-- 2 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 Yafc/Windows/MainScreen.PageListSearch.cs diff --git a/Yafc/Windows/MainScreen.PageListSearch.cs b/Yafc/Windows/MainScreen.PageListSearch.cs new file mode 100644 index 00000000..351f7d2b --- /dev/null +++ b/Yafc/Windows/MainScreen.PageListSearch.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Yafc.Model; +using Yafc.UI; +using YAFC.Model; + +namespace Yafc; + +public partial class MainScreen { + /// + /// Encapsulates the text box and checkboxes used for searching in the page list dropdown. + /// + private class PageListSearch { + private SearchQuery query; + + // The state of the checkboxes. To add a new checkbox: Add a new value to PageSearchOption, + // draw the new checkbox in Build, and obey the new checkbox in Search. + private readonly bool[] checkboxValues = new bool[(int)PageSearchOption.MustBeLastValue]; + // Named constants for which bool means which type of search. + private enum PageSearchOption { + PageName, + DesiredProducts, + ExtraProducts, + Ingredients, + Recipes, + // Add values for any new search checkboxes above this point. + MustBeLastValue + } + + // Initialize to searching by page name only. + public PageListSearch() => checkboxValues[0] = true; + + /// + /// Draws the search header for the page list dropdown. + /// + /// The action to perform if the user updates any of the search parameters. + public void Build(ImGui gui, Action updatePageList) { + using (gui.EnterGroup(new Padding(1f))) { + if (gui.BuildSearchBox(query, out query)) { + updatePageList(); + } + + gui.BuildText("Search in:"); + using (gui.EnterRow()) { + buildCheckbox(gui, "Page name", ref checkboxValues[(int)PageSearchOption.PageName]); + buildCheckbox(gui, "Desired products", ref checkboxValues[(int)PageSearchOption.DesiredProducts]); + buildCheckbox(gui, "Recipes", ref checkboxValues[(int)PageSearchOption.Recipes]); + } + using (gui.EnterRow()) { + buildCheckbox(gui, "Ingredients", ref checkboxValues[(int)PageSearchOption.Ingredients]); + buildCheckbox(gui, "Extra products", ref checkboxValues[(int)PageSearchOption.ExtraProducts]); + } + } + + void buildCheckbox(ImGui gui, string text, ref bool isChecked) { + if (gui.BuildCheckBox(text, isChecked, out isChecked)) { + if (checkboxValues.Any(x => x)) { + updatePageList(); + } + else { + // Don't let the user uncheck the last checkbox. + isChecked = true; + } + } + } + } + + /// + /// Searches a list of s to find the ones that satisfy the current search criteria. + /// This is typically called by the updatePageList parameter to . + /// + /// The s to search. + /// The s that match the current search text and options. + public IEnumerable Search(IEnumerable pages) { + foreach (var page in pages) { + if (checkboxValues[(int)PageSearchOption.PageName] && query.Match(page.name)) { + yield return page; + } + else if (page.content is ProductionTable table) { + if (checkboxValues[(int)PageSearchOption.DesiredProducts] && table.links.Any(l => l.amount != 0 && isMatch(l.goods.name, l.goods.locName))) { + yield return page; + } + else if (checkboxValues[(int)PageSearchOption.Ingredients] && table.flow.Any(f => f.amount < 0 && isMatch(f.goods.name, f.goods.locName))) { + yield return page; + } + else if (checkboxValues[(int)PageSearchOption.ExtraProducts] && table.flow.Any(f => f.amount > 0 && isMatch(f.goods.name, f.goods.locName))) { + yield return page; + } + else if (checkboxValues[(int)PageSearchOption.Recipes] && table.GetAllRecipes().Any(r => isMatch(r.recipe.name, r.recipe.locName))) { + yield return page; + } + } + } + + bool isMatch(string internalName, string localizedName) { + return query.Match(localizedName) || query.Match(internalName); + } + } + } +} diff --git a/Yafc/Windows/MainScreen.cs b/Yafc/Windows/MainScreen.cs index cf607e06..16b8b5f3 100644 --- a/Yafc/Windows/MainScreen.cs +++ b/Yafc/Windows/MainScreen.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net.Http; using System.Numerics; using System.Text.Json; @@ -12,7 +13,7 @@ using YAFC.Model; namespace Yafc { - public class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string)> { + public partial class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string)> { ///Unique ID for the Summary page public static readonly Guid SummaryGuid = Guid.Parse("9bdea333-4be2-4be3-b708-b36a64672a40"); public static MainScreen Instance { get; private set; } @@ -35,8 +36,7 @@ public class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string) private bool analysisUpdatePending; private SearchQuery pageSearch; - private SearchQuery pageListSearch; - private readonly List sortedAndFilteredPageList = []; + private readonly PageListSearch pageListSearch = new(); private readonly ImGui searchGui; private Rect searchBoxRect; @@ -224,12 +224,7 @@ protected override void BuildContent(ImGui gui) { } private void UpdatePageList() { - sortedAndFilteredPageList.Clear(); - foreach (var page in project.pages) { - if (pageListSearch.Match(page.name)) { - sortedAndFilteredPageList.Add(page); - } - } + List sortedAndFilteredPageList = pageListSearch.Search(project.pages).ToList(); sortedAndFilteredPageList.Sort((a, b) => a.visible == b.visible ? string.Compare(a.name, b.name, StringComparison.InvariantCultureIgnoreCase) : a.visible ? -1 : 1); allPages.data = sortedAndFilteredPageList; } @@ -320,11 +315,7 @@ private void CreatePageDropdown(ImGui gui) { } private void MissingPagesDropdown(ImGui gui) { - using (gui.EnterGroup(new Padding(1f))) { - if (gui.BuildSearchBox(pageListSearch, out pageListSearch)) { - UpdatePageList(); - } - } + pageListSearch.Build(gui, UpdatePageList); allPages.Build(gui); } From 8ee67b3ad5c0810da7e5b57b204fe32bf8c44dc9 Mon Sep 17 00:00:00 2001 From: Dale McCoy <21223975+DaleStan@users.noreply.github.com> Date: Tue, 14 May 2024 21:25:58 -0400 Subject: [PATCH 2/8] Add an "All" checkbox for the search settings. --- Yafc/Windows/MainScreen.PageListSearch.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Yafc/Windows/MainScreen.PageListSearch.cs b/Yafc/Windows/MainScreen.PageListSearch.cs index 351f7d2b..17b2bace 100644 --- a/Yafc/Windows/MainScreen.PageListSearch.cs +++ b/Yafc/Windows/MainScreen.PageListSearch.cs @@ -50,6 +50,10 @@ public void Build(ImGui gui, Action updatePageList) { using (gui.EnterRow()) { buildCheckbox(gui, "Ingredients", ref checkboxValues[(int)PageSearchOption.Ingredients]); buildCheckbox(gui, "Extra products", ref checkboxValues[(int)PageSearchOption.ExtraProducts]); + if (gui.BuildCheckBox("All", checkboxValues.All(x => x), out bool checkAll) && checkAll) { + Array.Fill(checkboxValues, true); + updatePageList(); + } } } From 2fddbf76277ca2888128979333fb0a774ceb1a94 Mon Sep 17 00:00:00 2001 From: Dale McCoy <21223975+DaleStan@users.noreply.github.com> Date: Tue, 14 May 2024 21:38:57 -0400 Subject: [PATCH 3/8] When unchecking the search All box, uncheck the boxes it most recently checked. --- Yafc/Windows/MainScreen.PageListSearch.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Yafc/Windows/MainScreen.PageListSearch.cs b/Yafc/Windows/MainScreen.PageListSearch.cs index 17b2bace..47d8a617 100644 --- a/Yafc/Windows/MainScreen.PageListSearch.cs +++ b/Yafc/Windows/MainScreen.PageListSearch.cs @@ -17,6 +17,7 @@ private class PageListSearch { // The state of the checkboxes. To add a new checkbox: Add a new value to PageSearchOption, // draw the new checkbox in Build, and obey the new checkbox in Search. private readonly bool[] checkboxValues = new bool[(int)PageSearchOption.MustBeLastValue]; + private readonly bool[] previousCheckboxValues = new bool[(int)PageSearchOption.MustBeLastValue]; // Named constants for which bool means which type of search. private enum PageSearchOption { PageName, @@ -28,8 +29,8 @@ private enum PageSearchOption { MustBeLastValue } - // Initialize to searching by page name only. - public PageListSearch() => checkboxValues[0] = true; + // Initialize both the current and previous states to searching by page name only. + public PageListSearch() => checkboxValues[0] = previousCheckboxValues[0] = true; /// /// Draws the search header for the page list dropdown. @@ -50,8 +51,16 @@ public void Build(ImGui gui, Action updatePageList) { using (gui.EnterRow()) { buildCheckbox(gui, "Ingredients", ref checkboxValues[(int)PageSearchOption.Ingredients]); buildCheckbox(gui, "Extra products", ref checkboxValues[(int)PageSearchOption.ExtraProducts]); - if (gui.BuildCheckBox("All", checkboxValues.All(x => x), out bool checkAll) && checkAll) { - Array.Fill(checkboxValues, true); + if (gui.BuildCheckBox("All", checkboxValues.All(x => x), out bool checkAll)) { + if (checkAll) { + // Save the previous state, so we can restore it if necessary. + Array.Copy(checkboxValues, previousCheckboxValues, (int)PageSearchOption.MustBeLastValue); + Array.Fill(checkboxValues, true); + } + else { + // Restore the previous state. + Array.Copy(previousCheckboxValues, checkboxValues, (int)PageSearchOption.MustBeLastValue); + } updatePageList(); } } From 4f91476c3242148d2e37bd862b7faebfab31d0c1 Mon Sep 17 00:00:00 2001 From: Dale McCoy <21223975+DaleStan@users.noreply.github.com> Date: Tue, 14 May 2024 21:26:54 -0400 Subject: [PATCH 4/8] Add radio buttons to select between internal names, localized names, or both. --- Yafc/Windows/MainScreen.PageListSearch.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Yafc/Windows/MainScreen.PageListSearch.cs b/Yafc/Windows/MainScreen.PageListSearch.cs index 47d8a617..e950f074 100644 --- a/Yafc/Windows/MainScreen.PageListSearch.cs +++ b/Yafc/Windows/MainScreen.PageListSearch.cs @@ -29,6 +29,9 @@ private enum PageSearchOption { MustBeLastValue } + private enum SearchNameMode { Localized, Internal, Both } + private SearchNameMode searchNameMode = SearchNameMode.Both; + // Initialize both the current and previous states to searching by page name only. public PageListSearch() => checkboxValues[0] = previousCheckboxValues[0] = true; @@ -64,6 +67,11 @@ public void Build(ImGui gui, Action updatePageList) { updatePageList(); } } + using (gui.EnterRow()) { + buildRadioButton(gui, "Localized names", SearchNameMode.Localized); + buildRadioButton(gui, "Internal names", SearchNameMode.Internal); + buildRadioButton(gui, "Both", SearchNameMode.Both); + } } void buildCheckbox(ImGui gui, string text, ref bool isChecked) { @@ -77,6 +85,15 @@ void buildCheckbox(ImGui gui, string text, ref bool isChecked) { } } } + + void buildRadioButton(ImGui gui, string text, SearchNameMode thisValue) { + // All checkboxes except PageSearchOption.PageName search object names. + bool isObjectNameSearching = checkboxValues[1..].Any(x => x); + if (gui.BuildRadioButton(text, searchNameMode == thisValue, isObjectNameSearching ? SchemeColor.PrimaryText : SchemeColor.PrimaryTextFaint) && isObjectNameSearching) { + searchNameMode = thisValue; + updatePageList(); + } + } } /// @@ -107,7 +124,8 @@ public IEnumerable Search(IEnumerable pages) { } bool isMatch(string internalName, string localizedName) { - return query.Match(localizedName) || query.Match(internalName); + return (searchNameMode != SearchNameMode.Internal && query.Match(localizedName)) || + (searchNameMode != SearchNameMode.Localized && query.Match(internalName)); } } } From f76ee957d33d9557af36920d35b6ef233767ca11 Mon Sep 17 00:00:00 2001 From: Dale McCoy <21223975+DaleStan@users.noreply.github.com> Date: Thu, 16 May 2024 22:26:51 -0400 Subject: [PATCH 5/8] Add some comments and move the small helper methods into the method they help. --- Yafc/Windows/MainScreen.cs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Yafc/Windows/MainScreen.cs b/Yafc/Windows/MainScreen.cs index 16b8b5f3..b343a8ee 100644 --- a/Yafc/Windows/MainScreen.cs +++ b/Yafc/Windows/MainScreen.cs @@ -218,18 +218,15 @@ protected override void BuildContent(ImGui gui) { ReRunAnalysis(); } } - BuildHeader(gui); + BuildTabBar(gui); BuildPage(gui); } } - private void UpdatePageList() { - List sortedAndFilteredPageList = pageListSearch.Search(project.pages).ToList(); - sortedAndFilteredPageList.Sort((a, b) => a.visible == b.visible ? string.Compare(a.name, b.name, StringComparison.InvariantCultureIgnoreCase) : a.visible ? -1 : 1); - allPages.data = sortedAndFilteredPageList; - } - - private void BuildHeader(ImGui gui) { + /// + /// Draws the tab bar across the top of the window, including the pancake menu, add page button, and search pages dropdown. + /// + private void BuildTabBar(ImGui gui) { using (gui.EnterRow()) { gui.spacing = 0f; if (gui.BuildButton(Icon.Menu)) { @@ -249,14 +246,25 @@ private void BuildHeader(ImGui gui) { } if (gui.BuildButton(spaceForDropdown, SchemeColor.None, SchemeColor.Grey)) { - UpdatePageList(); - ShowDropDown(gui, spaceForDropdown, MissingPagesDropdown, new Padding(0f, 0f, 0f, 0.5f), 30f); + updatePageList(); + ShowDropDown(gui, spaceForDropdown, missingPagesDropdown, new Padding(0f, 0f, 0f, 0.5f), 30f); } } } if (gui.isBuilding) { gui.DrawRectangle(gui.lastRect, SchemeColor.PureBackground); } + + void updatePageList() { + List sortedAndFilteredPageList = pageListSearch.Search(project.pages).ToList(); + sortedAndFilteredPageList.Sort((a, b) => a.visible == b.visible ? string.Compare(a.name, b.name, StringComparison.InvariantCultureIgnoreCase) : a.visible ? -1 : 1); + allPages.data = sortedAndFilteredPageList; + } + + void missingPagesDropdown(ImGui gui) { + pageListSearch.Build(gui, updatePageList); + allPages.Build(gui); + } } private void BuildPage(ImGui gui) { @@ -314,11 +322,6 @@ private void CreatePageDropdown(ImGui gui) { } } - private void MissingPagesDropdown(ImGui gui) { - pageListSearch.Build(gui, UpdatePageList); - allPages.Build(gui); - } - public void BuildSubHeader(ImGui gui, string text) { using (gui.EnterGroup(ObjectTooltip.contentPadding)) { gui.BuildText(text, Font.subheader); From 17cb3c63033c0735fc041f88eda1a6e8420b954c Mon Sep 17 00:00:00 2001 From: Dale McCoy <21223975+DaleStan@users.noreply.github.com> Date: Thu, 16 May 2024 23:01:00 -0400 Subject: [PATCH 6/8] When opening the page list search, set the input focus to the text box. Always set the input focus to the end of the text, since that's where you want to type. --- Yafc.UI/ImGui/ImGuiTextInputHelper.cs | 2 +- Yafc/Windows/MainScreen.PageListSearch.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Yafc.UI/ImGui/ImGuiTextInputHelper.cs b/Yafc.UI/ImGui/ImGuiTextInputHelper.cs index 6222b009..4c76f5ea 100644 --- a/Yafc.UI/ImGui/ImGuiTextInputHelper.cs +++ b/Yafc.UI/ImGui/ImGuiTextInputHelper.cs @@ -33,7 +33,7 @@ public void SetFocus(Rect boundingRect, string setText) { } InputSystem.Instance.SetKeyboardFocus(this); rect = boundingRect; - caret = selectionAnchor = 0; + caret = selectionAnchor = setText.Length; } private void GetTextParameters(string textToBuild, Rect textRect, FontFile.FontSize fontSize, RectAlignment alignment, out TextCache cachedText, out float scale, out float textWidth, out Rect realTextRect) { diff --git a/Yafc/Windows/MainScreen.PageListSearch.cs b/Yafc/Windows/MainScreen.PageListSearch.cs index e950f074..6694fb9a 100644 --- a/Yafc/Windows/MainScreen.PageListSearch.cs +++ b/Yafc/Windows/MainScreen.PageListSearch.cs @@ -44,6 +44,7 @@ public void Build(ImGui gui, Action updatePageList) { if (gui.BuildSearchBox(query, out query)) { updatePageList(); } + gui.SetTextInputFocus(gui.lastContentRect, query.query); gui.BuildText("Search in:"); using (gui.EnterRow()) { From 0899d5f059ab8b02f7d533bd1251b7cc9d0f1890 Mon Sep 17 00:00:00 2001 From: Dale McCoy <21223975+DaleStan@users.noreply.github.com> Date: Fri, 17 May 2024 17:52:18 -0400 Subject: [PATCH 7/8] Make the search always accessible, and allow no checked boxes, which always returns zero results. --- Yafc/Windows/MainScreen.PageListSearch.cs | 8 +------- Yafc/Windows/MainScreen.cs | 14 ++++++-------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/Yafc/Windows/MainScreen.PageListSearch.cs b/Yafc/Windows/MainScreen.PageListSearch.cs index 6694fb9a..7e04eb01 100644 --- a/Yafc/Windows/MainScreen.PageListSearch.cs +++ b/Yafc/Windows/MainScreen.PageListSearch.cs @@ -77,13 +77,7 @@ public void Build(ImGui gui, Action updatePageList) { void buildCheckbox(ImGui gui, string text, ref bool isChecked) { if (gui.BuildCheckBox(text, isChecked, out isChecked)) { - if (checkboxValues.Any(x => x)) { - updatePageList(); - } - else { - // Don't let the user uncheck the last checkbox. - isChecked = true; - } + updatePageList(); } } diff --git a/Yafc/Windows/MainScreen.cs b/Yafc/Windows/MainScreen.cs index b343a8ee..13206fa9 100644 --- a/Yafc/Windows/MainScreen.cs +++ b/Yafc/Windows/MainScreen.cs @@ -240,15 +240,13 @@ private void BuildTabBar(ImGui gui) { gui.allocator = RectAllocator.RightRow; var spaceForDropdown = gui.AllocateRect(2.1f, 2.1f); tabBar.Build(gui); - if (project.hiddenPages > 0 || tabBar.maxScroll > 0f) { - if (gui.isBuilding) { - gui.DrawIcon(spaceForDropdown.Expand(-0.3f), Icon.DropDown, SchemeColor.BackgroundText); - } + if (gui.isBuilding) { + gui.DrawIcon(spaceForDropdown.Expand(-0.3f), Icon.DropDown, SchemeColor.BackgroundText); + } - if (gui.BuildButton(spaceForDropdown, SchemeColor.None, SchemeColor.Grey)) { - updatePageList(); - ShowDropDown(gui, spaceForDropdown, missingPagesDropdown, new Padding(0f, 0f, 0f, 0.5f), 30f); - } + if (gui.BuildButton(spaceForDropdown, SchemeColor.None, SchemeColor.Grey)) { + updatePageList(); + ShowDropDown(gui, spaceForDropdown, missingPagesDropdown, new Padding(0f, 0f, 0f, 0.5f), 30f); } } if (gui.isBuilding) { From 2d0dc4b51e4b556bb5bdd058130916cd01914271 Mon Sep 17 00:00:00 2001 From: Dale McCoy <21223975+DaleStan@users.noreply.github.com> Date: Fri, 17 May 2024 18:21:11 -0400 Subject: [PATCH 8/8] Add an entry to the changelog. --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index 2b9cfaba..1c378c65 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,7 @@ Date: soon Features: - Add the option to specify a number of belts of production, and to specify per-second/minute/hour production regardless of the current display setting. + - When searching in the page list, allow searching in page contents as well as in page names. Changes: - Add a help message and proper handling for command line arguments - Removed default pollution cost from calculation. Added a setting to customize pollution cost.