Skip to content

Commit

Permalink
Merge pull request #701 from nhruo123/feat/mod-options-search
Browse files Browse the repository at this point in the history
Add search to mod options
  • Loading branch information
maddie480 authored Mar 6, 2024
2 parents ab4ea54 + b55905b commit 3bf9e25
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 60 deletions.
2 changes: 2 additions & 0 deletions Celeste.Mod.mm/Content/Dialog/English.txt
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@
MODOPTIONS_COREMODULE_NOTLOADED_ASMLOADERROR= mod assembly failed to load
MODOPTIONS_COREMODULE_YAMLERRORS= Some everest.yaml files could not be loaded.

MODOPTIONS_COREMODULE_SEARCHBOX_PLACEHOLDER= Press 'Tab' or 'Enter' to scroll to the next match

MODOPTIONS_VANILLATRISTATE_NEVER= OFF
MODOPTIONS_VANILLATRISTATE_EVEREST= EVEREST
MODOPTIONS_VANILLATRISTATE_ALWAYS= ALWAYS
Expand Down
2 changes: 2 additions & 0 deletions Celeste.Mod.mm/Content/Dialog/French.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@
MODOPTIONS_COREMODULE_NOTLOADED_ASMLOADERROR= échec de chargement de la DLL
MODOPTIONS_COREMODULE_YAMLERRORS= Certains fichiers everest.yaml n'ont pas pu être chargés.

MODOPTIONS_COREMODULE_SEARCHBOX_PLACEHOLDER= Appuyez sur Tab ou Entrée pour lancer la recherche

MODOPTIONS_VANILLATRISTATE_NEVER= DÉSACTIVÉ
MODOPTIONS_VANILLATRISTATE_EVEREST= EVEREST
MODOPTIONS_VANILLATRISTATE_ALWAYS= TOUJOURS
Expand Down
17 changes: 17 additions & 0 deletions Celeste.Mod.mm/Mod/Core/CoreModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,21 @@ public void CreatePauseMenuButtons(Level level, patch_TextMenu menu, bool minima
level.Paused = true;

TextMenu options = OuiModOptions.CreateMenu(true, LevelExt.PauseSnapshot);
Action startSearching = OuiModOptions.AddSearchBox(options);

options.OnUpdate = () => {
if (options.Focused) {
if (Input.QuickRestart.Pressed) {
startSearching();
}
}
};

options.OnESC = options.OnCancel = () => {
if (!options.Focused) {
return;
}

Audio.Play(SFX.ui_main_button_back);
options.CloseAndRun(Everest.SaveSettings(), () => {
level.Pause(returnIndex, minimal, false);
Expand All @@ -271,6 +284,10 @@ public void CreatePauseMenuButtons(Level level, patch_TextMenu menu, bool minima
};

options.OnPause = () => {
if (!options.Focused) {
return;
}

Audio.Play(SFX.ui_main_button_back);
options.CloseAndRun(Everest.SaveSettings(), () => {
level.Paused = false;
Expand Down
91 changes: 91 additions & 0 deletions Celeste.Mod.mm/Mod/UI/OuiModOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Celeste.Mod.Core;
using FMOD.Studio;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Monocle;
using System;
using System.Collections;
Expand All @@ -27,6 +28,8 @@ public interface ISubmenu { }

private int savedMenuIndex = -1;

private Action startSearching;

public OuiModOptions() {
Instance = this;
}
Expand Down Expand Up @@ -176,6 +179,7 @@ private void ReloadMenu() {
}

menu = CreateMenu(false, null);
startSearching = AddSearchBox(menu, Overworld);

if (selected >= 0) {
menu.Selection = selected;
Expand All @@ -185,6 +189,86 @@ private void ReloadMenu() {
Scene.Add(menu);
}

static public Action AddSearchBox(TextMenu menu, Overworld overworld = null) {
TextMenuExt.TextBox textBox = new(overworld) {
PlaceholderText = Dialog.Clean("MODOPTIONS_COREMODULE_SEARCHBOX_PLACEHOLDER")
};

TextMenuExt.Modal modal = new(textBox, absoluteX: null, absoluteY: 85);
menu.Add(modal);
menu.Add(new TextMenuExt.SearchToolTip());

Action<TextMenuExt.TextBox> searchNextMod(bool inReverse) => (TextMenuExt.TextBox textBox) => {
string searchTarget = textBox.Text.ToLower();
List<TextMenu.Item> menuItems = ((patch_TextMenu) menu).Items;

bool searchNextPredicate(TextMenu.Item item) {
string searchLabel = ((patch_TextMenu.patch_Item) item).SearchLabel();
return item.Visible && item.Selectable && !item.Disabled && searchLabel != null && searchLabel.ToLower().Contains(searchTarget);
}


if (TextMenuExt.TextBox.WrappingLinearSearch(menuItems, searchNextPredicate, menu.Selection + (inReverse ? -1 : 1), inReverse, out int targetSelectionIndex)) {
if (targetSelectionIndex >= menu.Selection) {
Audio.Play(SFX.ui_main_roll_down);
} else {
Audio.Play(SFX.ui_main_roll_up);
}

menu.Selection = targetSelectionIndex;
} else {
Audio.Play(SFX.ui_main_button_invalid);
}
};

void exitSearch(TextMenuExt.TextBox textBox) {
textBox.StopTyping();
modal.Visible = false;
textBox.ClearText();
}

textBox.OnTextInputCharActions['\t'] = searchNextMod(false);
textBox.OnTextInputCharActions['\n'] = (_) => { };
textBox.OnTextInputCharActions['\r'] = (textBox) => {
if (MInput.Keyboard.CurrentState.IsKeyDown(Keys.LeftShift)
|| MInput.Keyboard.CurrentState.IsKeyDown(Keys.RightShift)) {
searchNextMod(true)(textBox);
} else {
searchNextMod(false)(textBox);
}
};
textBox.OnTextInputCharActions['\b'] = (textBox) => {
if (textBox.DeleteCharacter()) {
Audio.Play(SFX.ui_main_rename_entry_backspace);
} else {
exitSearch(textBox);
Input.MenuCancel.ConsumePress();
}
};


textBox.AfterInputConsumed = () => {
if (textBox.Typing) {
if (Input.ESC.Pressed) {
exitSearch(textBox);
Input.ESC.ConsumePress();
} else if (Input.MenuDown.Pressed) {
searchNextMod(false)(textBox);
} else if (Input.MenuUp.Pressed) {
searchNextMod(true)(textBox);
}
}
};

return () => {
// we want to ensure we don't open the search box while we are in a sub-menu
if (menu.Focused) {
modal.Visible = true;
textBox.StartTyping();
}
};
}

public override IEnumerator Enter(Oui from) {
ReloadMenu();

Expand Down Expand Up @@ -233,6 +317,13 @@ public override void Update() {
Overworld.Goto<OuiMainMenu>();
}

if (Selected && Focused) {
if (Input.QuickRestart.Pressed) {
startSearching?.Invoke();
return;
}
}

base.Update();
}

Expand Down
52 changes: 3 additions & 49 deletions Celeste.Mod.mm/Mod/UI/OuiModToggler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -360,50 +360,21 @@ protected override void addOptionsToMenu(patch_TextMenu menu) {
modLoadingTask.Start();
}

private static bool WrappingLinearSearch<T>(List<T> items, Func<T, bool> predicate, int startIndex, bool inReverse, out int nextModIndex) {
int step = inReverse ? -1 : 1;

if (startIndex > items.Count) {
nextModIndex = 0;
return false;
}

for (int currentIndex = (startIndex + step) % items.Count; currentIndex != startIndex; currentIndex = (currentIndex + step) % items.Count) {
if (currentIndex < 0) {
currentIndex = items.Count - 1;
}

if (predicate(items[currentIndex])) {
nextModIndex = currentIndex;
return true;
}
}

nextModIndex = startIndex;
return predicate(items[nextModIndex]);
}

private void AddSearchBox(TextMenu menu) {
TextMenuExt.TextBox textBox = new(Overworld) {
PlaceholderText = Dialog.Clean("MODOPTIONS_MODTOGGLE_SEARCHBOX_PLACEHOLDER")
};

TextMenuExt.Modal modal = new(absoluteY: 85, textBox);
TextMenuExt.Modal modal = new(textBox, absoluteX: null, absoluteY: 85);
menu.Add(modal);
menu.Add(new TextMenuExt.SearchToolTip());

startSearching = () => {
modal.Visible = true;
textBox.StartTyping();

if (((patch_TextMenu) menu).Items[menu.Selection] is patch_TextMenu.patch_Option<bool> currentOption
&& modToggles.ContainsKey(currentOption.Label)) {
currentOption.UnselectedColor = currentOption.Container.HighlightColor;
}
};

Action<TextMenuExt.TextBox> searchNextMod(bool inReverse) => (TextMenuExt.TextBox textBox) => {
updateHighlightedMods();

string searchTarget = textBox.Text.ToLower();
List<TextMenu.Item> menuItems = ((patch_TextMenu) menu).Items;
int currentSelection = menu.Selection;
Expand All @@ -412,18 +383,14 @@ bool searchPredicate(TextMenu.Item item) => item is patch_TextMenu.patch_Option<
&& modToggles.ContainsKey(currentOption.Label)
&& currentOption.Label.ToLower().Contains(searchTarget);

if (WrappingLinearSearch(menuItems, searchPredicate, menu.Selection, inReverse, out int targetSelectionIndex)) {

if (TextMenuExt.TextBox.WrappingLinearSearch(menuItems, searchPredicate, menu.Selection + (inReverse ? -1 : 1), inReverse, out int targetSelectionIndex)) {
if (targetSelectionIndex >= menu.Selection) {
Audio.Play(SFX.ui_main_roll_down);
} else {
Audio.Play(SFX.ui_main_roll_up);
}

menu.Selection = targetSelectionIndex;
if (menuItems[targetSelectionIndex] is patch_TextMenu.patch_Option<bool> currentOption) {
currentOption.UnselectedColor = currentOption.Container.HighlightColor;
}
} else {
Audio.Play(SFX.ui_main_button_invalid);
}
Expand All @@ -433,7 +400,6 @@ void exitSearch(TextMenuExt.TextBox textBox) {
textBox.StopTyping();
modal.Visible = false;
textBox.ClearText();
updateHighlightedMods();
}

textBox.OnTextInputCharActions['\t'] = searchNextMod(false);
Expand Down Expand Up @@ -755,18 +721,6 @@ private bool modHasDependencies(string modFilename) {

public override void Render() {
base.Render();

if (modLoadingTask == null) {
MTexture searchIcon = GFX.Gui["menu/mapsearch"];

const float PREFERRED_ICON_X = 100f;
float spaceNearMenu = (Engine.Width - menu.Width) / 2;
float scaleFactor = Math.Min(spaceNearMenu / (PREFERRED_ICON_X + searchIcon.Width / 2), 1);

Vector2 searchIconLocation = new(PREFERRED_ICON_X * scaleFactor, 952f);
searchIcon.DrawCentered(searchIconLocation, Color.White, scaleFactor);
Input.GuiKey(Input.FirstKey(Input.QuickRestart)).Draw(searchIconLocation, Vector2.Zero, Color.White, scaleFactor);
}
}

public override IEnumerator Leave(Oui next) {
Expand Down
Loading

0 comments on commit 3bf9e25

Please sign in to comment.