From aeb016cb649a382c422d6d7f79e561713e5324be Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Sun, 27 Oct 2024 23:02:46 +0100 Subject: [PATCH] Choose Variables from Combo Boxes (#986) Instead of manually specifying the variables via text boxes, you can now choose them from combo boxes. This makes it easier to select the variables you want to use for the layout. Changelog: You can now choose variables to display on the layout from combo boxes instead of manually specifying them via text boxes. --- livesplit-core | 2 +- src/ui/LSOCommandSink.ts | 15 ++++++++++ src/ui/LayoutEditor.tsx | 25 ++++++++++++++++ src/ui/LiveSplit.tsx | 20 +++++++++++++ src/ui/MainSettings.tsx | 4 +++ src/ui/RunEditor.tsx | 2 ++ src/ui/Settings.tsx | 61 +++++++++++++++++++++++++++++++--------- 7 files changed, 115 insertions(+), 14 deletions(-) diff --git a/livesplit-core b/livesplit-core index 51775324..86bc01b3 160000 --- a/livesplit-core +++ b/livesplit-core @@ -1 +1 @@ -Subproject commit 517753242561fd51ab86fae3304c860e64b2c44d +Subproject commit 86bc01b32372e5b692dc9674af462f70da5b57f0 diff --git a/src/ui/LSOCommandSink.ts b/src/ui/LSOCommandSink.ts index 9cf7a3eb..dc44da1f 100644 --- a/src/ui/LSOCommandSink.ts +++ b/src/ui/LSOCommandSink.ts @@ -7,6 +7,7 @@ interface Callbacks { handleEvent(event: Event): void, runChanged(): void, runNotModifiedAnymore(): void, + encounteredCustomVariable(name: string): void, } @@ -361,6 +362,7 @@ export class LSOCommandSink { const result = Event.CustomVariableSet; this.callbacks.handleEvent(result); + this.callbacks.encounteredCustomVariable(name); return result; } @@ -440,6 +442,19 @@ export class LSOCommandSink { return comparisons; } + public getAllCustomVariables(): Set { + const customVariables = new Set(); + using customVariablesIter = this.getRun().metadata().customVariables(); + while (true) { + const element = customVariablesIter.next(); + if (element === null) { + break; + } + customVariables.add(element.name()); + } + return customVariables; + } + public currentTimingMethod(): TimingMethod { return this.timer.currentTimingMethod(); } diff --git a/src/ui/LayoutEditor.tsx b/src/ui/LayoutEditor.tsx index 07ea6286..84d49d8a 100644 --- a/src/ui/LayoutEditor.tsx +++ b/src/ui/LayoutEditor.tsx @@ -19,6 +19,7 @@ export interface Props { layoutHeight: number, generalSettings: GeneralSettings, allComparisons: string[], + allVariables: Set, isDesktop: boolean, commandSink: LSOCommandSink, renderer: WebRenderer, @@ -105,6 +106,7 @@ export class LayoutEditor extends React.Component { state={this.state.editor.component_settings} editorUrlCache={this.props.layoutEditorUrlCache} allComparisons={this.props.allComparisons} + allVariables={this.props.allVariables} setValue={(index, value) => { this.props.editor.setComponentSettingsValue(index, value); this.update(); @@ -117,6 +119,7 @@ export class LayoutEditor extends React.Component { state={this.state.editor.general_settings} editorUrlCache={this.props.layoutEditorUrlCache} allComparisons={this.props.allComparisons} + allVariables={this.props.allVariables} setValue={(index, value) => { this.props.editor.setGeneralSettingsValue( index, @@ -234,6 +237,21 @@ export class LayoutEditor extends React.Component { Shows the total amount of time that the current category has been played for. + { + this.props.allVariables.size > 0 && + } + { + this.props.allVariables.size > 0 && Array.from(this.props.allVariables).map((name) => { + return ( + this.addVariable(name)}> + {name} + + Creates a text component that shows the value of the custom variable "{name}". + + + ); + }) + } this.addComponent(LiveSplit.BlankSpaceComponent)}> Blank Space @@ -381,6 +399,13 @@ export class LayoutEditor extends React.Component { this.update(true); } + private addVariable(name: string) { + const textComponent = LiveSplit.TextComponent.new(); + textComponent.useVariable(name, true); + this.props.editor.addComponent(textComponent.intoGeneric()); + this.update(true); + } + private removeComponent() { this.props.editor.removeComponent(); this.update(); diff --git a/src/ui/LiveSplit.tsx b/src/ui/LiveSplit.tsx index e3d3dd86..ae55566e 100644 --- a/src/ui/LiveSplit.tsx +++ b/src/ui/LiveSplit.tsx @@ -101,6 +101,7 @@ export interface State { currentPhase: TimerPhase, currentSplitIndex: number, allComparisons: string[], + allVariables: Set, splitsModified: boolean, layoutModified: boolean, } @@ -219,6 +220,7 @@ export class LiveSplit extends React.Component { currentPhase: commandSink.currentPhase(), currentSplitIndex: commandSink.currentSplitIndex(), allComparisons: commandSink.getAllComparisons(), + allVariables: commandSink.getAllCustomVariables(), splitsModified: commandSink.hasBeenModified(), layoutModified: false, }; @@ -321,6 +323,7 @@ export class LiveSplit extends React.Component { callbacks={this} runEditorUrlCache={this.state.runEditorUrlCache} allComparisons={this.state.allComparisons} + allVariables={this.state.allVariables} generalSettings={this.state.generalSettings} />; } else if (this.state.menu.kind === MenuKind.LayoutEditor) { @@ -333,6 +336,7 @@ export class LiveSplit extends React.Component { layoutHeight={this.state.layoutHeight} generalSettings={this.state.generalSettings} allComparisons={this.state.allComparisons} + allVariables={this.state.allVariables} isDesktop={this.state.isDesktop} commandSink={this.state.commandSink} renderer={this.state.renderer} @@ -347,6 +351,7 @@ export class LiveSplit extends React.Component { commandSink={this.state.commandSink} serverConnection={this.state.serverConnection} allComparisons={this.state.allComparisons} + allVariables={this.state.allVariables} />; } else if (this.state.menu.kind === MenuKind.About) { view = ; @@ -899,12 +904,27 @@ export class LiveSplit extends React.Component { this.currentSplitChanged(); this.comparisonListChanged(); this.splitsModifiedChanged(); + + if (this.state != null) { + this.setState({ + allVariables: this.state.commandSink.getAllCustomVariables(), + }); + } } runNotModifiedAnymore(): void { this.splitsModifiedChanged(); } + encounteredCustomVariable(name: string): void { + if (this.state.allVariables.has(name)) { + return; + } + this.setState({ + allVariables: new Set([...this.state.allVariables, name]), + }); + } + private currentComparisonChanged(): void { if (this.state != null) { const currentComparison = this.state.commandSink.currentComparison(); diff --git a/src/ui/MainSettings.tsx b/src/ui/MainSettings.tsx index 5fad1abd..2bfaf0a7 100644 --- a/src/ui/MainSettings.tsx +++ b/src/ui/MainSettings.tsx @@ -40,6 +40,7 @@ export interface Props { serverConnection: Option, commandSink: LSOCommandSink, allComparisons: string[], + allVariables: Set, } export interface State { @@ -138,6 +139,7 @@ export class MainSettings extends React.Component { state={this.state.settings} editorUrlCache={this.props.urlCache} allComparisons={this.props.allComparisons} + allVariables={this.props.allVariables} setValue={(index, value) => { if (!this.props.hotkeyConfig.setValue(index, value)) { toast.error("The hotkey is already in use."); @@ -155,6 +157,7 @@ export class MainSettings extends React.Component { }} editorUrlCache={this.props.urlCache} allComparisons={this.props.allComparisons} + allVariables={this.props.allVariables} setValue={(index, value) => { switch (index) { case 0: @@ -259,6 +262,7 @@ export class MainSettings extends React.Component { }} editorUrlCache={this.props.urlCache} allComparisons={this.props.allComparisons} + allVariables={this.props.allVariables} setValue={(index, value) => { switch (index) { case 0: diff --git a/src/ui/RunEditor.tsx b/src/ui/RunEditor.tsx index 55fd02d9..0b642b94 100644 --- a/src/ui/RunEditor.tsx +++ b/src/ui/RunEditor.tsx @@ -34,6 +34,7 @@ export interface Props { callbacks: Callbacks, runEditorUrlCache: UrlCache, allComparisons: string[], + allVariables: Set, generalSettings: GeneralSettings, } export interface State { @@ -870,6 +871,7 @@ export class RunEditor extends React.Component { state={{ fields }} editorUrlCache={this.props.runEditorUrlCache} allComparisons={this.props.allComparisons} + allVariables={this.props.allVariables} setValue={(index, value) => { function unwrapString(value: ExtendedSettingsDescriptionValueJson): string { if ("String" in value) { diff --git a/src/ui/Settings.tsx b/src/ui/Settings.tsx index aaf53eb2..e0e4e07d 100644 --- a/src/ui/Settings.tsx +++ b/src/ui/Settings.tsx @@ -21,6 +21,7 @@ export interface Props { factory: SettingValueFactory, editorUrlCache: UrlCache, allComparisons: string[], + allVariables: Set, } export interface ExtendedSettingsDescriptionJson { @@ -246,19 +247,53 @@ export class SettingsComponent extends React.Component> { ); } else if ("String" in value) { - component = ( -
- { - this.props.setValue( - valueIndex, - factory.fromString(e.target.value), - ); - }} - /> -
- ); + // FIXME: This is a hack that we need for now until the way + // settings are represented is refactored. + if (typeof (field.text) === "string" && /^Variable/.test(field.text)) { + if (this.props.allVariables.size === 0) { + component =
+ + No variables available + + Custom variables can be defined in the Variables tab when + editing splits. Additional custom variables can be provided + automatically by auto splitters. + + +
; + } else { + component =
+ +
; + } + } else { + component = ( +
+ { + this.props.setValue( + valueIndex, + factory.fromString(e.target.value), + ); + }} + /> +
+ ); + } } else if ("OptionalString" in value) { // FIXME: This is a hack that we need for now until the way // settings are represented is refactored.