diff --git a/CHANGELOG.md b/CHANGELOG.md index 8761ca2f9..6892cec2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add ability to show the raw collision tile values when editing collisions in world view - Add ability to ctrl/cmd + click frames in Sprite Editor to toggle multi select or shift + click to select range - Add right click context menu to frames in Sprite Editor allowing copy/paste/clone/delete to be performed on all selected frames +- Add ability to search and add scripts by name when adding events instead of needing to add a "Call Script" event and selecting the script manually from the dropdown each time [@pau-tomas](https://github.com/pau-tomas) +- Add ability to quickly create "Comment" events by typing the comment text in the Add Event search field and choosing "Comment" menu item [@pau-tomas](https://github.com/pau-tomas) ### Changed diff --git a/src/components/script/AddScriptEventMenu.tsx b/src/components/script/AddScriptEventMenu.tsx index 2734564b1..264eabb7b 100644 --- a/src/components/script/AddScriptEventMenu.tsx +++ b/src/components/script/AddScriptEventMenu.tsx @@ -20,9 +20,11 @@ import { ScriptEventNormalized, ScriptEventFieldSchema, ScriptEventParentType, + CustomEventNormalized, } from "shared/lib/entities/entitiesTypes"; import entitiesActions from "store/features/entities/entitiesActions"; import { + customEventSelectors, emoteSelectors, musicSelectors, sceneSelectors, @@ -32,7 +34,7 @@ import { import { useDebounce } from "ui/hooks/use-debounce"; import { ScriptEditorContext } from "./ScriptEditorContext"; import { defaultVariableForContext } from "shared/lib/scripts/context"; -import { EVENT_TEXT } from "consts"; +import { EVENT_CALL_CUSTOM_EVENT, EVENT_COMMENT, EVENT_TEXT } from "consts"; import { selectScriptEventDefsWithPresets } from "store/features/scriptEventDefs/scriptEventDefsState"; import type { ScriptEventDef } from "lib/project/loadScriptEventHandlers"; import { useAppDispatch, useAppSelector } from "store/hooks"; @@ -42,6 +44,7 @@ import { HighlightWords } from "ui/util/HighlightWords"; import { IMEUnstyledInput } from "ui/form/IMEInput"; import { StyledButton } from "ui/buttons/style"; import { StyledMenu, StyledMenuItem } from "ui/menu/style"; +import { ScriptEventDefs } from "shared/lib/scripts/scriptDefHelpers"; interface AddScriptEventMenuProps { parentType: ScriptEventParentType; @@ -58,6 +61,8 @@ type MenuElement = HTMLDivElement & { interface EventOption { label: string; + displayLabel?: string; // non searchable label, used only to display in the menu + description?: string; // override tooltip value: string; group?: string; groupLabel?: string; @@ -70,6 +75,7 @@ interface EventOption { interface EventOptGroup { label: string; + displayLabel?: string; // non searchable label, used only to display in the menu groupLabel?: string; options: EventOption[]; } @@ -244,6 +250,41 @@ const eventToOption = }; }; +const customEventToOption = + (scriptEventDefs: ScriptEventDefs) => + (event: CustomEventNormalized): EventOption => { + return { + label: event.name, + displayLabel: `${l10n(EVENT_CALL_CUSTOM_EVENT)} "${event.name}"`, + description: event.description.trim(), + value: `call_script_${event.id}`, + isFavorite: false, + event: scriptEventDefs[EVENT_CALL_CUSTOM_EVENT] as ScriptEventDef, + defaultArgs: { + customEventId: event.id, + }, + } as EventOption; + }; + +const titleForOption = ( + option: EventOption | EventOptGroup +): string | undefined => { + // If option description is provided with a non-empty + // string then use that as the title for menu item + if ( + "description" in option && + option.description && + option.description.length > 0 + ) { + return option.description; + } + // Otherwise use event description if available + if ("event" in option) { + return option.event.description; + } + return undefined; +}; + const SelectMenu = styled.div` width: 300px; @@ -520,6 +561,9 @@ const AddScriptEventMenu = ({ const scriptEventDefs = useAppSelector((state) => selectScriptEventDefsWithPresets(state) ); + const customEventsLookup = useAppSelector((state) => + customEventSelectors.selectAll(state) + ); useEffect(() => { if (selectedCategoryIndex === -1) { @@ -531,7 +575,13 @@ const AddScriptEventMenu = ({ const eventList = ( Object.values(scriptEventDefs).filter(identity) as ScriptEventDef[] ).filter(notDeprecated); - fuseRef.current = new Fuse(eventList.map(eventToOption(favoriteEvents)), { + + const allEvents = ([] as EventOption[]).concat( + eventList.map(eventToOption(favoriteEvents)), + customEventsLookup.map(customEventToOption(scriptEventDefs)) + ); + + fuseRef.current = new Fuse(allEvents, { includeScore: true, includeMatches: true, ignoreLocation: true, @@ -613,7 +663,7 @@ const AddScriptEventMenu = ({ setOptions(allOptions); firstLoad.current = true; } - }, [favoriteEvents, favoritesCache, scriptEventDefs]); + }, [customEventsLookup, favoriteEvents, favoritesCache, scriptEventDefs]); const updateOptions = useCallback(() => { if (searchTerm && fuseRef.current) { @@ -634,7 +684,7 @@ const AddScriptEventMenu = ({ ? searchOptions : [ { - value: "", + value: "fallback_option_0", label: `${l10n(EVENT_TEXT)} "${searchTerm}"`, event: scriptEventDefs[EVENT_TEXT] as ScriptEventDef, defaultArgs: { @@ -642,6 +692,15 @@ const AddScriptEventMenu = ({ }, isFavorite: false, }, + { + value: "fallback_option_1", + label: `${l10n(EVENT_COMMENT)} "${searchTerm}"`, + event: scriptEventDefs[EVENT_COMMENT] as ScriptEventDef, + defaultArgs: { + text: [searchTerm], + }, + isFavorite: false, + }, ] ); setSelectedIndex(0); @@ -910,17 +969,15 @@ const AddScriptEventMenu = ({ } onMouseOver={() => setSelectedIndex(optionIndex)} onClick={() => onSelectOption(optionIndex)} - title={ - "event" in option ? option.event.description : undefined - } + title={titleForOption(option)} > {searchTerm.length > 0 && highlightWords.length > 0 ? ( ) : ( - option.label + option.displayLabel ?? option.label )} {"options" in option ? ( @@ -974,9 +1031,9 @@ const AddScriptEventMenu = ({ selected={selectedIndex === childOptionIndex} onMouseOver={() => setSelectedIndex(childOptionIndex)} onClick={() => onSelectOption(childOptionIndex)} - title={childOption.event.description} + title={titleForOption(childOption)} > - {childOption.label} + {childOption.displayLabel ?? childOption.label}