Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Page search for specific things on the page #133

Merged
merged 8 commits into from
May 18, 2024
2 changes: 1 addition & 1 deletion Yafc.UI/ImGui/ImGuiTextInputHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
127 changes: 127 additions & 0 deletions Yafc/Windows/MainScreen.PageListSearch.cs
Original file line number Diff line number Diff line change
@@ -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 {
/// <summary>
/// Encapsulates the text box and checkboxes used for searching in the page list dropdown.
/// </summary>
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;

/// <summary>
/// Draws the search header for the page list dropdown.
/// </summary>
/// <param name="updatePageList">The action to perform if the user updates any of the search parameters.</param>
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();
}
}
}

/// <summary>
/// Searches a list of <see cref="ProjectPage"/>s to find the ones that satisfy the current search criteria.
/// This is typically called by the <c>updatePageList</c> parameter to <see cref="Build"/>.
/// </summary>
/// <param name="pages">The <see cref="ProjectPage"/>s to search.</param>
/// <returns>The <see cref="ProjectPage"/>s that match the current search text and options.</returns>
public IEnumerable<ProjectPage> Search(IEnumerable<ProjectPage> 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));
}
}
}
}
58 changes: 25 additions & 33 deletions Yafc/Windows/MainScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)> {
///<summary>Unique ID for the Summary page</summary>
public static readonly Guid SummaryGuid = Guid.Parse("9bdea333-4be2-4be3-b708-b36a64672a40");
public static MainScreen Instance { get; private set; }
Expand All @@ -35,8 +36,7 @@ public class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string)

private bool analysisUpdatePending;
private SearchQuery pageSearch;
private SearchQuery pageListSearch;
private readonly List<ProjectPage> sortedAndFilteredPageList = [];
private readonly PageListSearch pageListSearch = new();
private readonly ImGui searchGui;
private Rect searchBoxRect;

Expand Down Expand Up @@ -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) {
/// <summary>
/// Draws the tab bar across the top of the window, including the pancake menu, add page button, and search pages dropdown.
/// </summary>
private void BuildTabBar(ImGui gui) {
using (gui.EnterRow()) {
gui.spacing = 0f;
if (gui.BuildButton(Icon.Menu)) {
Expand All @@ -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<ProjectPage> 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) {
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down