From 9d719050e5009d6b084b931b509385442e3141d1 Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Mon, 12 Oct 2026 18:51:10 +0000 Subject: [PATCH] Add Detailed Timer Icon Modes Update livesplit-core to the merged master revision with detailed timer icon modes, automatic icon fitting, and missing-icon space reclamation. Icons can be hidden, span both rows, or occupy only the first row, while timer sizing and placement remain stable through starts and splits. Expose the typed icon display setting in the layout editor through the shared setting value factory and a dedicated selector. Localize the row modes in every supported language and keep fitting and placement in Core so the editor preview and running timer follow the same layout rules. --- livesplit-core | 2 +- src/localization/chinese-simplified.ts | 4 ++ src/localization/dutch.ts | 4 ++ src/localization/english.ts | 4 ++ src/localization/french.ts | 4 ++ src/localization/german.ts | 4 ++ src/localization/index.ts | 2 + src/localization/italian.ts | 4 ++ src/localization/japanese.ts | 4 ++ src/localization/korean.ts | 4 ++ src/localization/polish.ts | 4 ++ src/localization/portuguese-brazil.ts | 4 ++ src/localization/portuguese.ts | 4 ++ src/localization/russian.ts | 4 ++ src/localization/spanish.ts | 4 ++ .../components/Settings/IconDisplayMode.tsx | 50 +++++++++++++++++++ src/ui/components/Settings/index.tsx | 25 ++++++++++ 17 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/ui/components/Settings/IconDisplayMode.tsx diff --git a/livesplit-core b/livesplit-core index fb62dd42..eb9d284b 160000 --- a/livesplit-core +++ b/livesplit-core @@ -1 +1 @@ -Subproject commit fb62dd42505b16e813d2779468d81bbad7f01b5e +Subproject commit eb9d284b056e7d7455fb4889d05e68df11470a04 diff --git a/src/localization/chinese-simplified.ts b/src/localization/chinese-simplified.ts index 1c98c261..761929de 100644 --- a/src/localization/chinese-simplified.ts +++ b/src/localization/chinese-simplified.ts @@ -572,6 +572,10 @@ export function resolveChineseSimplified(text: Label): string { return "对比区间时间"; case Label.ColumnStartWithPossibleTimeSave: return "可节省时间"; + case Label.IconDisplayModeBothRows: + return "两行"; + case Label.IconDisplayModeFirstRow: + return "仅第一行"; case Label.SubsplitDisplayModeFlat: return "平铺"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/dutch.ts b/src/localization/dutch.ts index be63aee2..e890020b 100644 --- a/src/localization/dutch.ts +++ b/src/localization/dutch.ts @@ -572,6 +572,10 @@ export function resolveDutch(text: Label): string { return "Vergelijking segmenttijd"; case Label.ColumnStartWithPossibleTimeSave: return "Mogelijke tijdswinst"; + case Label.IconDisplayModeBothRows: + return "Beide rijen"; + case Label.IconDisplayModeFirstRow: + return "Alleen de eerste rij"; case Label.SubsplitDisplayModeFlat: return "Plat"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/english.ts b/src/localization/english.ts index 11480359..ca0b9c44 100644 --- a/src/localization/english.ts +++ b/src/localization/english.ts @@ -572,6 +572,10 @@ export function resolveEnglish(text: Label): string { return "Comparison Segment Time"; case Label.ColumnStartWithPossibleTimeSave: return "Possible Time Save"; + case Label.IconDisplayModeBothRows: + return "Both Rows"; + case Label.IconDisplayModeFirstRow: + return "First Row Only"; case Label.SubsplitDisplayModeFlat: return "Flat"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/french.ts b/src/localization/french.ts index 96840205..9e13cb62 100644 --- a/src/localization/french.ts +++ b/src/localization/french.ts @@ -572,6 +572,10 @@ export function resolveFrench(text: Label): string { return "Temps du segment de comparaison"; case Label.ColumnStartWithPossibleTimeSave: return "Gain de temps possible"; + case Label.IconDisplayModeBothRows: + return "Les deux lignes"; + case Label.IconDisplayModeFirstRow: + return "Première ligne uniquement"; case Label.SubsplitDisplayModeFlat: return "À plat"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/german.ts b/src/localization/german.ts index af3087e3..d8c6f88f 100644 --- a/src/localization/german.ts +++ b/src/localization/german.ts @@ -572,6 +572,10 @@ export function resolveGerman(text: Label): string { return "Vergleichs‑Segmentzeit"; case Label.ColumnStartWithPossibleTimeSave: return "Mögliche Zeitersparnis"; + case Label.IconDisplayModeBothRows: + return "Beide Zeilen"; + case Label.IconDisplayModeFirstRow: + return "Nur erste Zeile"; case Label.SubsplitDisplayModeFlat: return "Flach"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/index.ts b/src/localization/index.ts index 0a30026f..2dec3e5f 100644 --- a/src/localization/index.ts +++ b/src/localization/index.ts @@ -301,6 +301,8 @@ export enum Label { ColumnStartWithComparisonSegmentTime, ColumnStartWithPossibleTimeSave, SubsplitDisplayModeFlat, + IconDisplayModeBothRows, + IconDisplayModeFirstRow, SubsplitDisplayModeCurrentGroupExpanded, SubsplitDisplayModeAllGroupsExpanded, ColumnUpdateWithDontUpdate, diff --git a/src/localization/italian.ts b/src/localization/italian.ts index cf53a42b..57fcdc4a 100644 --- a/src/localization/italian.ts +++ b/src/localization/italian.ts @@ -572,6 +572,10 @@ export function resolveItalian(text: Label): string { return "Tempo segmento di confronto"; case Label.ColumnStartWithPossibleTimeSave: return "Tempo risparmiabile"; + case Label.IconDisplayModeBothRows: + return "Entrambe le righe"; + case Label.IconDisplayModeFirstRow: + return "Solo la prima riga"; case Label.SubsplitDisplayModeFlat: return "Elenco piatto"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/japanese.ts b/src/localization/japanese.ts index 29714520..c289dae1 100644 --- a/src/localization/japanese.ts +++ b/src/localization/japanese.ts @@ -572,6 +572,10 @@ export function resolveJapanese(text: Label): string { return "比較対象区間タイム"; case Label.ColumnStartWithPossibleTimeSave: return "更新余地"; + case Label.IconDisplayModeBothRows: + return "両方の行"; + case Label.IconDisplayModeFirstRow: + return "最初の行のみ"; case Label.SubsplitDisplayModeFlat: return "フラット"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/korean.ts b/src/localization/korean.ts index e0cef6ce..5c33ddf4 100644 --- a/src/localization/korean.ts +++ b/src/localization/korean.ts @@ -572,6 +572,10 @@ export function resolveKorean(text: Label): string { return "비교 세그먼트 시간"; case Label.ColumnStartWithPossibleTimeSave: return "절약 가능한 시간"; + case Label.IconDisplayModeBothRows: + return "두 행 모두"; + case Label.IconDisplayModeFirstRow: + return "첫 번째 행만"; case Label.SubsplitDisplayModeFlat: return "플랫"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/polish.ts b/src/localization/polish.ts index 5f33444b..3e3d031d 100644 --- a/src/localization/polish.ts +++ b/src/localization/polish.ts @@ -572,6 +572,10 @@ export function resolvePolish(text: Label): string { return "Czas segmentu porównania"; case Label.ColumnStartWithPossibleTimeSave: return "Możliwa oszczędność czasu"; + case Label.IconDisplayModeBothRows: + return "Oba wiersze"; + case Label.IconDisplayModeFirstRow: + return "Tylko pierwszy wiersz"; case Label.SubsplitDisplayModeFlat: return "Płasko"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/portuguese-brazil.ts b/src/localization/portuguese-brazil.ts index 474bd89b..4c39896d 100644 --- a/src/localization/portuguese-brazil.ts +++ b/src/localization/portuguese-brazil.ts @@ -572,6 +572,10 @@ export function resolveBrazilianPortuguese(text: Label): string { return "Tempo de segmento de comparação"; case Label.ColumnStartWithPossibleTimeSave: return "Tempo possível de economizar"; + case Label.IconDisplayModeBothRows: + return "Ambas as linhas"; + case Label.IconDisplayModeFirstRow: + return "Apenas a primeira linha"; case Label.SubsplitDisplayModeFlat: return "Plano"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/portuguese.ts b/src/localization/portuguese.ts index a78a5c7e..87775d4f 100644 --- a/src/localization/portuguese.ts +++ b/src/localization/portuguese.ts @@ -572,6 +572,10 @@ export function resolvePortuguese(text: Label): string { return "Tempo de segmento de comparação"; case Label.ColumnStartWithPossibleTimeSave: return "Tempo possível de economizar"; + case Label.IconDisplayModeBothRows: + return "Ambas as linhas"; + case Label.IconDisplayModeFirstRow: + return "Apenas a primeira linha"; case Label.SubsplitDisplayModeFlat: return "Plano"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/russian.ts b/src/localization/russian.ts index e4c6c92d..7f67f28a 100644 --- a/src/localization/russian.ts +++ b/src/localization/russian.ts @@ -572,6 +572,10 @@ export function resolveRussian(text: Label): string { return "Время сегмента сравнения"; case Label.ColumnStartWithPossibleTimeSave: return "Возможная экономия времени"; + case Label.IconDisplayModeBothRows: + return "Обе строки"; + case Label.IconDisplayModeFirstRow: + return "Только первая строка"; case Label.SubsplitDisplayModeFlat: return "Плоский список"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/localization/spanish.ts b/src/localization/spanish.ts index c96d5638..9441bef0 100644 --- a/src/localization/spanish.ts +++ b/src/localization/spanish.ts @@ -572,6 +572,10 @@ export function resolveSpanish(text: Label): string { return "Tiempo de segmento de comparación"; case Label.ColumnStartWithPossibleTimeSave: return "Tiempo posible de ahorrar"; + case Label.IconDisplayModeBothRows: + return "Ambas filas"; + case Label.IconDisplayModeFirstRow: + return "Solo la primera fila"; case Label.SubsplitDisplayModeFlat: return "Plano"; case Label.SubsplitDisplayModeCurrentGroupExpanded: diff --git a/src/ui/components/Settings/IconDisplayMode.tsx b/src/ui/components/Settings/IconDisplayMode.tsx new file mode 100644 index 00000000..870845bf --- /dev/null +++ b/src/ui/components/Settings/IconDisplayMode.tsx @@ -0,0 +1,50 @@ +import * as React from "react"; +import { expect } from "../../../util/OptionUtil"; +import { + type IconDisplayMode as IconDisplayModeType, + type Language, +} from "../../../livesplit-core"; +import { type SettingValueFactory } from "."; +import { Label, resolve } from "../../../localization"; + +import tableClasses from "../../../css/Table.module.css"; + +// Core owns icon fitting and missing-image reclamation. The editor only +// forwards the display mode, so previews and the running timer use the same +// automatic layout rules without any additional frontend sizing settings. +export function IconDisplayMode({ + value, + setValue, + factory, + lang, +}: { + value: IconDisplayModeType; + setValue: (value: T) => void; + factory: SettingValueFactory; + lang: Language | undefined; +}) { + return ( +
+ +
+ ); +} diff --git a/src/ui/components/Settings/index.tsx b/src/ui/components/Settings/index.tsx index 6138f648..300922d5 100644 --- a/src/ui/components/Settings/index.tsx +++ b/src/ui/components/Settings/index.tsx @@ -21,6 +21,7 @@ import { } from "./Column"; import { Alignment } from "./Alignment"; import { SubsplitDisplayMode } from "./SubsplitDisplayMode"; +import { IconDisplayMode } from "./IconDisplayMode"; import { OptionalTimingMethod } from "./TimingMethod"; import { DeltaGradient, Gradient, ListGradient } from "./Gradient"; import { Color, OptionalColor } from "./Color"; @@ -127,6 +128,7 @@ export interface SettingValueFactory { fromColumnUpdateTrigger(value: string): T | null; fromLayoutDirection(value: string): T | null; fromSubsplitDisplayMode(value: string): T | null; + fromIconDisplayMode(value: string): T | null; fromFont( name: string, style: string, @@ -250,6 +252,18 @@ export class JsonSettingValueFactory implements SettingValueFactory extends React.Component> { lang={this.props.lang} /> ); + } else if ("IconDisplayMode" in value) { + component = ( + + this.props.setValue(valueIndex, value) + } + factory={this.props.factory} + lang={this.props.lang} + /> + ); } else if ("SubsplitDisplayMode" in value) { component = (