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
new file mode 100644
index 00000000..7e04eb01
--- /dev/null
+++ b/Yafc/Windows/MainScreen.PageListSearch.cs
@@ -0,0 +1,127 @@
+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];
+ private readonly bool[] previousCheckboxValues = 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
+ }
+
+ 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;
+
+ ///
+ /// 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.SetTextInputFocus(gui.lastContentRect, query.query);
+
+ 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]);
+ 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();
+ }
+ }
+ 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) {
+ if (gui.BuildCheckBox(text, isChecked, out isChecked)) {
+ updatePageList();
+ }
+ }
+
+ 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();
+ }
+ }
+ }
+
+ ///
+ /// 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 (searchNameMode != SearchNameMode.Internal && query.Match(localizedName)) ||
+ (searchNameMode != SearchNameMode.Localized && query.Match(internalName));
+ }
+ }
+ }
+}
diff --git a/Yafc/Windows/MainScreen.cs b/Yafc/Windows/MainScreen.cs
index cf607e06..13206fa9 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;
@@ -218,23 +218,15 @@ protected override void BuildContent(ImGui gui) {
ReRunAnalysis();
}
}
- BuildHeader(gui);
+ BuildTabBar(gui);
BuildPage(gui);
}
}
- private void UpdatePageList() {
- sortedAndFilteredPageList.Clear();
- foreach (var page in project.pages) {
- if (pageListSearch.Match(page.name)) {
- sortedAndFilteredPageList.Add(page);
- }
- }
- 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)) {
@@ -248,20 +240,29 @@ private void BuildHeader(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) {
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) {
@@ -319,15 +320,6 @@ private void CreatePageDropdown(ImGui gui) {
}
}
- private void MissingPagesDropdown(ImGui gui) {
- using (gui.EnterGroup(new Padding(1f))) {
- if (gui.BuildSearchBox(pageListSearch, out pageListSearch)) {
- UpdatePageList();
- }
- }
- allPages.Build(gui);
- }
-
public void BuildSubHeader(ImGui gui, string text) {
using (gui.EnterGroup(ObjectTooltip.contentPadding)) {
gui.BuildText(text, Font.subheader);
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.