From 0b6a732f833f6f4f157bdbe99a31106798c15b5c Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 18 Sep 2024 16:56:21 -0400 Subject: [PATCH] Refactored code and fixed bugs --- src/ui/autocomplete-ui-handler.ts | 56 ++++++++----- src/ui/rename-form-ui-handler.ts | 134 +++++++++++++++++------------- 2 files changed, 112 insertions(+), 78 deletions(-) diff --git a/src/ui/autocomplete-ui-handler.ts b/src/ui/autocomplete-ui-handler.ts index 0da35ecba14..27657de8c29 100644 --- a/src/ui/autocomplete-ui-handler.ts +++ b/src/ui/autocomplete-ui-handler.ts @@ -1,21 +1,25 @@ import { Button } from "#enums/buttons"; import BattleScene from "../battle-scene"; -import AbstractOptionSelectUiHandler from "./abstact-option-select-ui-handler"; +import AbstractOptionSelectUiHandler, { OptionSelectConfig } from "./abstact-option-select-ui-handler"; import { Mode } from "./ui"; import InputText from "phaser3-rex-plugins/plugins/inputtext"; -// import * as Utils from "#app/utils"; + +export interface OptionSelectConfigAC extends OptionSelectConfig { + inputContainer: Phaser.GameObjects.Container; + modalContainer: Phaser.GameObjects.Container; + maxOptionsReverse?: number; + reverse?: true; +} export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler { modalContainer: Phaser.GameObjects.Container; inputContainer: Phaser.GameObjects.Container; handlerKeyDown: (inputObject: InputText, evt: KeyboardEvent) => void; + reverse?: true; - - constructor(scene: BattleScene, mode: Mode = Mode.OPTION_SELECT, ...args) { + constructor(scene: BattleScene, mode: Mode = Mode.AUTO_COMPLETE) { super(scene, mode); - this.config = { - options: [] - }; + this.handlerKeyDown = (inputObject, evt) => { // Don't move inputText cursor if (["arrowup"].some((key) => key === (evt.code || evt.key).toLowerCase())) { @@ -37,6 +41,7 @@ export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler inputObject.on("blur", recoveryFocus); } }; + } getWindowWidth(): integer { @@ -45,9 +50,12 @@ export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler show(args: any[]): boolean { if (args[0].modalContainer && args[0].inputContainer && args[0].inputContainer.list.some((el) => el instanceof InputText)) { - const { modalContainer, inputContainer } = args[0]; - args[0].options?.forEach((opt)=>{ - const originalHandler = opt.handler; + const { modalContainer, inputContainer, reverse } = args[0] as OptionSelectConfigAC; + const newArgs = JSON.parse(JSON.stringify(args)); + this.reverse = reverse; + + newArgs[0].options?.forEach((opt, index)=>{ + const originalHandler = args[0].options[index].handler; opt.handler = () => { if (originalHandler()) { ui.revertMode(); @@ -83,10 +91,21 @@ export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler input.on("keydown", originalsEvents[i]); } - const show = super.show(args); - this.setupOptions(); + if (this.reverse && newArgs[0].maxOptionsReverse) { + newArgs[0].maxOptions = newArgs[0].maxOptionsReverse; + } - return show; + if (this.reverse) { + newArgs[0].options.reverse(); + } + + if (super.show(newArgs)) { + if (this.reverse) { + this.processInput(Button.UP); + } + + return true; + } } return false; } @@ -94,17 +113,14 @@ export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler protected setupOptions() { super.setupOptions(); if (this.modalContainer) { + if (this.reverse) { + this.optionSelectContainer.setPositionRelative(this.modalContainer, this.optionSelectBg.width + this.inputContainer.x, this.inputContainer.y); + return; + } this.optionSelectContainer.setPositionRelative(this.modalContainer, this.optionSelectBg.width + this.inputContainer.x, this.optionSelectBg.height + this.inputContainer.y + (this.inputContainer.list.find((el) => el instanceof Phaser.GameObjects.NineSlice)?.height ?? 0)); } } - // processInput(button: Button): boolean { - // if (button !== Button.CANCEL) { - // return super.processInput(button); - // } - // return false; - // } - clear(): void { super.clear(); const input = this.inputContainer.list.find((el) => el instanceof InputText); diff --git a/src/ui/rename-form-ui-handler.ts b/src/ui/rename-form-ui-handler.ts index 8644a3247e9..aca86839caa 100644 --- a/src/ui/rename-form-ui-handler.ts +++ b/src/ui/rename-form-ui-handler.ts @@ -3,14 +3,14 @@ import { ModalConfig } from "./modal-ui-handler"; import i18next from "i18next"; import { PlayerPokemon } from "#app/field/pokemon.js"; import { Mode } from "./ui"; -import { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui-handler"; +import { OptionSelectItem } from "./abstact-option-select-ui-handler"; import { addWindow } from "./ui-theme"; import { addTextObject, getTextStyleOptions, TextStyle } from "./text"; import InputText from "phaser3-rex-plugins/plugins/inputtext"; import BattleScene from "#app/battle-scene.js"; -import AutoCompleteUiHandler from "./autocomplete-ui-handler"; +import AutoCompleteUiHandler, { OptionSelectConfigAC } from "./autocomplete-ui-handler"; -const emojiAvailable = ["♪", "★", "♥", "♣", "☻", "ª", "☼", "►", "♫", "←", "→"]; +const emojiAvailable = ["♪", "★", "♥", "♣", "☻", "ª", "☼", "►", "♫", "←", "→", "↩", "↪"]; export default class RenameFormUiHandler extends FormModalUiHandler { protected autocomplete: AutoCompleteUiHandler; @@ -90,89 +90,107 @@ export default class RenameFormUiHandler extends FormModalUiHandler { } }; + // emoji list config const maxEmojis = 6; - const emojiOptions = emojiAvailable.map((emoji, index): OptionSelectItem => { - return { - label: `${emoji} /${index + 1}`, - handler: ()=> { - const command = input.text.split("").filter((_, i) => i >= (input.text.split("").filter((_, i) => i < input.cursorPosition).lastIndexOf("/")) && i < input.cursorPosition).join(""); + const modalOptions: OptionSelectConfigAC = { + options: emojiAvailable.map((emoji, index): OptionSelectItem => { + return { + label: `${emoji} /${index + 1}`, + handler: ()=> { + // Retrieve the exact command, as it can be either "/", or "/n" + const command = input.text.split("").filter((_, i) => i >= (input.text.split("").filter((_, i) => i < input.cursorPosition).lastIndexOf("/")) && i < input.cursorPosition).join(""); - const texto = input.text; - const textBeforeCursor = texto.substring(0, input.cursorPosition); - const textAfterCursor = texto.substring(input.cursorPosition); + const texto = input.text; + const textBeforeCursor = texto.substring(0, input.cursorPosition); + const textAfterCursor = texto.substring(input.cursorPosition); - const exactlyCommand = textBeforeCursor.lastIndexOf(command); - if (exactlyCommand !== -1) { - const textReplace = textBeforeCursor.substring(0, exactlyCommand) + emoji + textAfterCursor; - input.setText(textReplace); - input.setCursorPosition(exactlyCommand + emoji.length); - return true; - } - return false; - }, - }; - }); - - interface OptionSelectConfigAC extends OptionSelectConfig { - inputContainer: Phaser.GameObjects.Container; - modalContainer: Phaser.GameObjects.Container; - } - - const modalOptions = { - options: emojiOptions, + const exactlyCommand = textBeforeCursor.lastIndexOf(command); + if (exactlyCommand !== -1) { + const textReplace = textBeforeCursor.substring(0, exactlyCommand) + emoji + textAfterCursor; + input.setText(textReplace); + input.setCursorPosition(exactlyCommand + emoji.length); + return true; + } + return false; + }, + }; + }), modalContainer: this.modalContainer, inputContainer: this.inputContainers[0], maxOptions: 5 }; input.on("textchange", (inputObject:InputText, evt:InputEvent) => { - if (input.text.split("").filter((char) => emojiAvailable.some((em) => em === char)).length < maxEmojis && input.text.split("").some((char, i) => char === "/" && i + 1 === input.cursorPosition) && input.text.length < input.maxLength) { + // If deleting and currently positioned at "/", display the list of emojis + if ( + !evt.data && + input.text.split("").filter((char) => emojiAvailable.some((em) => em === char)).length < maxEmojis && + input.text.split("").some((char, i) => char === "/" && i + 1 === input.cursorPosition) + ) { ui.setOverlayMode(Mode.AUTO_COMPLETE, modalOptions); } - if (input.text.split("").filter((char) => emojiAvailable.some((em) => em === char)).length > maxEmojis) { - const command = input.text.split("").filter((_, i) => evt.data && i >= (input.text.split("").filter((_, i) => i < input.cursorPosition).lastIndexOf(evt.data)) && i < input.cursorPosition).join(""); + const getDisallowedEmojis = (text) => { + return text.split("").filter((char) => { + const isEmoji = !char.match(/[\u0000-\u00ff]/); + const isAllowedEmoji = emojiAvailable.includes(char); + return isEmoji && !isAllowedEmoji; + }); + }; + // Remove disallowed emojis. + while (getDisallowedEmojis(input.text).length > 0) { + const disallowedEmojis = getDisallowedEmojis(input.text); + const lastDisallowedEmojiIndex = input.text.lastIndexOf(disallowedEmojis[0]); + + if (lastDisallowedEmojiIndex !== -1) { + const textBeforeCursor = input.text.substring(0, lastDisallowedEmojiIndex); + const textAfterCursor = input.text.substring(lastDisallowedEmojiIndex + 1); + const newText = textBeforeCursor + textAfterCursor; + + if (newText !== input.text) { + input.setText(newText); + input.setCursorPosition(lastDisallowedEmojiIndex); + } + } + } + + + // If the number of available emojis exceeds the maximum allowed number of emojis.. + //.. Delete any attempt to insert another one. + if (evt.data && input.text.split("").filter((char) => emojiAvailable.some((em) => em === char)).length > maxEmojis) { const texto = input.text; const textBeforeCursor = texto.substring(0, input.cursorPosition); const textAfterCursor = texto.substring(input.cursorPosition); - const exactlyCommand = textBeforeCursor.lastIndexOf(command); - if (exactlyCommand !== -1 && evt.data) { - const textReplace = textBeforeCursor.substring(0, exactlyCommand) + textAfterCursor; + const exactlyEmoji = textBeforeCursor.lastIndexOf(evt.data); + if (exactlyEmoji !== -1) { + const textReplace = textBeforeCursor.substring(0, exactlyEmoji) + textAfterCursor; if (textReplace !== input.text) { input.setText(textReplace); - input.setCursorPosition(exactlyCommand); + input.setCursorPosition(exactlyEmoji); } } } + + // If the number of available emojis has been reached, do not display the list of emojis if (evt.data && input.text.split("").filter((char) => emojiAvailable.some((em) => em === char)).length < maxEmojis) { - if (evt.data === "/") { - ui.setOverlayMode(Mode.AUTO_COMPLETE, modalOptions); + + // Retrieve the exact command, as it can be either "/", or "/n" + const command = input.text.split("").filter((_, i, arr) => arr.includes("/") && i >= (input.text.split("").filter((_, i) => i < input.cursorPosition).lastIndexOf("/")) && i < input.cursorPosition).join(""); + + if (modalOptions.options.some((opt) => opt.label.includes(command))) { + const filterOptions = { + ...modalOptions, + options: modalOptions.options.filter((opt) => opt.label.includes(command)) + }; + + this.scene.ui.setOverlayMode(Mode.AUTO_COMPLETE, filterOptions); } } }); - input.on("keydown", (inputObject:InputText, evt:KeyboardEvent)=>{ - const command = input.text.split("").filter((_, i) => i >= (input.text.split("").filter((_, i) => i < input.cursorPosition).lastIndexOf("/")) && i < input.cursorPosition).join(""); - if (!isNaN(parseInt(evt.key))) { - input.setData("filter", input.getData("filter") ? input.getData("filter").toString().concat(evt.key) : evt.key.toString()); - } else { - input.setData("filter"); - } - if (command.includes("/") && emojiOptions.some((_, i) => (i + 1).toString().includes(input.getData("filter")))) { - const filterOptions: OptionSelectConfigAC = { - options: emojiOptions.filter((_, i) => (i + 1).toString().includes(input.getData("filter"))), - modalContainer: this.modalContainer, - inputContainer: this.inputContainers[0], - maxOptions: 5 - }; - - this.scene.ui.setOverlayMode(Mode.AUTO_COMPLETE, filterOptions); - } - }); - return true; } return false;