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

Add Setting for Manual Game Time Input Mode #985

Merged
merged 1 commit into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion livesplit-core
Submodule livesplit-core updated 39 files
+6 −5 Cargo.toml
+6 −0 capi/src/time_span.rs
+9 −5 crates/livesplit-auto-splitting/Cargo.toml
+31 −21 crates/livesplit-auto-splitting/README.md
+30 −19 crates/livesplit-auto-splitting/src/lib.rs
+16 −9 crates/livesplit-auto-splitting/src/runtime/api/process.rs
+6 −6 crates/livesplit-auto-splitting/src/runtime/api/runtime.rs
+103 −7 crates/livesplit-auto-splitting/src/runtime/api/wasi.rs
+42 −26 crates/livesplit-auto-splitting/src/runtime/mod.rs
+19 −3 crates/livesplit-auto-splitting/src/timer.rs
+3 −2 crates/livesplit-auto-splitting/tests/sandboxing.rs
+3 −3 crates/livesplit-hotkey/Cargo.toml
+74 −47 crates/livesplit-hotkey/src/linux/x11_impl.rs
+3 −3 crates/livesplit-hotkey/src/windows/mod.rs
+44 −21 src/auto_splitting/mod.rs
+1 −1 src/component/separator.rs
+2 −2 src/hotkey_system.rs
+4 −4 src/layout/parser/font_resolving/gdi.rs
+4 −4 src/rendering/default_text_engine/mod.rs
+3 −1 src/rendering/entity.rs
+4 −8 src/rendering/svg.rs
+134 −0 src/run/parser/flitter.rs
+0 −78 src/run/parser/flitter/mod.rs
+0 −419 src/run/parser/flitter/s_expressions.rs
+7 −7 src/run/parser/splitterino.rs
+1 −1 src/settings/layout_background.rs
+111 −1 src/timing/formatter/complete.rs
+105 −1 src/timing/formatter/days.rs
+113 −12 src/timing/formatter/delta.rs
+140 −1 src/timing/formatter/regular.rs
+117 −6 src/timing/formatter/segment_time.rs
+174 −5 src/timing/formatter/timer.rs
+2 −0 src/timing/mod.rs
+132 −50 src/timing/time_span.rs
+8 −19 src/util/ascii_char.rs
+5 −2 tests/rendering.rs
+81 −0 tests/run_files/flitter.json
+1 −2 tests/run_files/mod.rs
+0 −8 tests/split_parsing.rs
6 changes: 5 additions & 1 deletion src/storage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { openDB, IDBPDatabase } from "idb";
import { Option, assert } from "../util/OptionUtil";
import { RunRef, Run, TimingMethod } from "../livesplit-core";
import { GeneralSettings } from "../ui/MainSettings";
import { GeneralSettings, MANUAL_GAME_TIME_SETTINGS_DEFAULT } from "../ui/MainSettings";
import { FRAME_RATE_AUTOMATIC } from "../util/FrameRate";

export type HotkeyConfigSettings = unknown;
Expand Down Expand Up @@ -249,6 +249,10 @@ export async function loadGeneralSettings(): Promise<GeneralSettings> {

const isTauri = window.__TAURI__ != null;

if (generalSettings.showManualGameTime === true) {
generalSettings.showManualGameTime = MANUAL_GAME_TIME_SETTINGS_DEFAULT;
}

return {
frameRate: generalSettings.frameRate ?? FRAME_RATE_AUTOMATIC,
showControlButtons: generalSettings.showControlButtons ?? !isTauri,
Expand Down
6 changes: 5 additions & 1 deletion src/ui/LSOCommandSink.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CommandError, CommandResult, CommandSink, CommandSinkRef, Event, ImageCacheRefMut, LayoutEditorRefMut, LayoutRefMut, LayoutStateRefMut, Run, RunRef, TimeSpan, TimeSpanRef, Timer, TimerPhase, TimingMethod, isEvent } from "../livesplit-core";
import { CommandError, CommandResult, CommandSink, CommandSinkRef, Event, ImageCacheRefMut, LayoutEditorRefMut, LayoutRefMut, LayoutStateRefMut, Run, RunRef, TimeRef, TimeSpan, TimeSpanRef, Timer, TimerPhase, TimingMethod, isEvent } from "../livesplit-core";
import { WebCommandSink } from "../livesplit-core/livesplit_core";
import { assert } from "../util/OptionUtil";
import { showDialog } from "./Dialog";
Expand Down Expand Up @@ -410,6 +410,10 @@ export class LSOCommandSink {
layoutEditor.updateLayoutState(layoutState, imageCache, this.timer);
}

public currentTime(): TimeRef {
return this.timer.currentTime();
}

public currentSplitIndex(): number {
return this.timer.currentSplitIndex();
}
Expand Down
125 changes: 84 additions & 41 deletions src/ui/MainSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,24 @@ import "../css/SettingsEditor.scss";
export interface GeneralSettings {
frameRate: FrameRateSetting,
showControlButtons: boolean,
showManualGameTime: boolean,
showManualGameTime: ManualGameTimeSettings | false,
saveOnReset: boolean,
speedrunComIntegration: boolean,
splitsIoIntegration: boolean,
serverUrl?: string,
alwaysOnTop?: boolean,
}

export interface ManualGameTimeSettings {
mode: string,
}

export const MANUAL_GAME_TIME_MODE_SEGMENT_TIMES = "Segment Times";
export const MANUAL_GAME_TIME_MODE_SPLIT_TIMES = "Split Times";
export const MANUAL_GAME_TIME_SETTINGS_DEFAULT: ManualGameTimeSettings = {
mode: MANUAL_GAME_TIME_MODE_SEGMENT_TIMES,
};

export interface Props {
generalSettings: GeneralSettings,
hotkeyConfig: HotkeyConfig,
Expand Down Expand Up @@ -62,6 +72,63 @@ export class MainSettings extends React.Component<Props, State> {
}

private renderView() {
const generalFields = [
{
text: "Frame Rate",
tooltip: "Determines the frame rate at which to display the timer. \"Battery Aware\" tries determining the type of device and charging status to select a good frame rate. \"Match Screen\" makes the timer match the screen's refresh rate.",
value: {
CustomCombobox: {
value: this.state.generalSettings.frameRate === FRAME_RATE_MATCH_SCREEN ? FRAME_RATE_MATCH_SCREEN : this.state.generalSettings.frameRate === FRAME_RATE_BATTERY_AWARE ? FRAME_RATE_BATTERY_AWARE : this.state.generalSettings.frameRate.toString() + " FPS",
list: [FRAME_RATE_BATTERY_AWARE, "30 FPS", "60 FPS", "120 FPS", FRAME_RATE_MATCH_SCREEN],
mandatory: true,
}
},
},
{
text: "Save On Reset",
tooltip: "Determines whether to automatically save the splits when resetting the timer.",
value: {
Bool: this.state.generalSettings.saveOnReset,
},
},
{
text: "Show Control Buttons",
tooltip: "Determines whether to show buttons beneath the timer that allow controlling it. When disabled, you have to use the hotkeys instead.",
value: { Bool: this.state.generalSettings.showControlButtons },
},
{
text: "Show Manual Game Time Input",
tooltip: "Shows a text box beneath the timer that allows you to manually input the game time. You start the timer and do splits by pressing the Enter key in the text box. Make sure to compare against \"Game Time\".",
value: { Bool: this.state.generalSettings.showManualGameTime !== false },
},
];

let manualGameTimeModeIndex = 0;
if (this.state.generalSettings.showManualGameTime) {
manualGameTimeModeIndex = generalFields.length;
generalFields.push({
text: "Manual Game Time Mode",
tooltip: "Determines whether to input the manual game time as segment times or split times.",
value: {
CustomCombobox: {
value: this.state.generalSettings.showManualGameTime.mode,
list: [MANUAL_GAME_TIME_MODE_SEGMENT_TIMES, MANUAL_GAME_TIME_MODE_SPLIT_TIMES],
mandatory: false,
}
},
});
}

let alwaysOnTopIndex = 0;
if (window.__TAURI__ != null) {
alwaysOnTopIndex = generalFields.length;
generalFields.push({
text: "Always On Top",
tooltip: "Keeps the window always on top of other windows.",
value: { Bool: this.state.generalSettings.alwaysOnTop! },
});
}

return (
<div className="settings-editor">
<h2>Hotkeys</h2>
Expand All @@ -84,41 +151,7 @@ export class MainSettings extends React.Component<Props, State> {
context="settings-editor-general"
factory={new JsonSettingValueFactory()}
state={{
fields: [
{
text: "Frame Rate",
tooltip: "Determines the frame rate at which to display the timer. \"Battery Aware\" tries determining the type of device and charging status to select a good frame rate. \"Match Screen\" makes the timer match the screen's refresh rate.",
value: {
CustomCombobox: {
value: this.state.generalSettings.frameRate === FRAME_RATE_MATCH_SCREEN ? FRAME_RATE_MATCH_SCREEN : this.state.generalSettings.frameRate === FRAME_RATE_BATTERY_AWARE ? FRAME_RATE_BATTERY_AWARE : this.state.generalSettings.frameRate.toString() + " FPS",
list: [FRAME_RATE_BATTERY_AWARE, "30 FPS", "60 FPS", "120 FPS", FRAME_RATE_MATCH_SCREEN],
mandatory: true,
}
},
},
{
text: "Show Control Buttons",
tooltip: "Determines whether to show buttons beneath the timer that allow controlling it. When disabled, you have to use the hotkeys instead.",
value: { Bool: this.state.generalSettings.showControlButtons },
},
{
text: "Show Manual Game Time Input",
tooltip: "Shows a text box beneath the timer that allows you to manually input the game time. You start the timer and do splits by pressing the Enter key in the text box. Make sure to compare against \"Game Time\".",
value: { Bool: this.state.generalSettings.showManualGameTime },
},
{
text: "Save On Reset",
tooltip: "Determines whether to automatically save the splits when resetting the timer.",
value: {
Bool: this.state.generalSettings.saveOnReset,
},
},
...((window.__TAURI__ != null) ? [{
text: "Always On Top",
tooltip: "Keeps the window always on top of other windows.",
value: { Bool: this.state.generalSettings.alwaysOnTop! },
}] : []),
],
fields: generalFields,
}}
editorUrlCache={this.props.urlCache}
allComparisons={this.props.allComparisons}
Expand All @@ -143,7 +176,7 @@ export class MainSettings extends React.Component<Props, State> {
this.setState({
generalSettings: {
...this.state.generalSettings,
showControlButtons: value.Bool,
saveOnReset: value.Bool,
},
});
}
Expand All @@ -153,7 +186,7 @@ export class MainSettings extends React.Component<Props, State> {
this.setState({
generalSettings: {
...this.state.generalSettings,
showManualGameTime: value.Bool,
showControlButtons: value.Bool,
},
});
}
Expand All @@ -163,19 +196,29 @@ export class MainSettings extends React.Component<Props, State> {
this.setState({
generalSettings: {
...this.state.generalSettings,
saveOnReset: value.Bool,
showManualGameTime: value.Bool ?
MANUAL_GAME_TIME_SETTINGS_DEFAULT : false,
},
});
}
break;
case 4:
if ("Bool" in value) {
default:
if (index === alwaysOnTopIndex && "Bool" in value) {
this.setState({
generalSettings: {
...this.state.generalSettings,
alwaysOnTop: value.Bool,
},
});
} else if (index === manualGameTimeModeIndex && "String" in value) {
this.setState({
generalSettings: {
...this.state.generalSettings,
showManualGameTime: {
mode: value.String,
},
},
});
}
break;
}
Expand Down
12 changes: 10 additions & 2 deletions src/ui/TimerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import DragUpload from "./DragUpload";
import Layout from "../layout/Layout";
import { UrlCache } from "../util/UrlCache";
import { WebRenderer } from "../livesplit-core/livesplit_core";
import { GeneralSettings } from "./MainSettings";
import { GeneralSettings, MANUAL_GAME_TIME_MODE_SEGMENT_TIMES } from "./MainSettings";
import { LiveSplitServer } from "../api/LiveSplitServer";
import { LSOCommandSink } from "./LSOCommandSink";

Expand Down Expand Up @@ -73,6 +73,8 @@ export class TimerView extends React.Component<Props, State> {
}

private renderView() {
const showManualGameTime = this.props.generalSettings.showManualGameTime;

return <DragUpload
importLayout={(file) => this.props.callbacks.importLayoutFromFile(file)}
importSplits={(file) => this.props.callbacks.importSplitsFromFile(file)}
Expand Down Expand Up @@ -167,7 +169,7 @@ export class TimerView extends React.Component<Props, State> {
</div>
}
{
this.props.generalSettings.showManualGameTime && <div className="buttons" style={{ width: this.props.layoutWidth }}>
showManualGameTime && <div className="buttons" style={{ width: this.props.layoutWidth }}>
<input
type="text"
className="manual-game-time"
Expand All @@ -191,6 +193,12 @@ export class TimerView extends React.Component<Props, State> {
} else {
using gameTime = TimeSpan.parse(this.state.manualGameTime);
if (gameTime !== null) {
if (showManualGameTime.mode === MANUAL_GAME_TIME_MODE_SEGMENT_TIMES) {
const currentGameTime = timer.currentTime().gameTime();
if (currentGameTime !== null) {
gameTime.addAssign(currentGameTime);
}
}
timer.setGameTimeInner(gameTime);
timer.split();
this.setState({ manualGameTime: "" });
Expand Down