Skip to content

Add Native Subsplit Support#934

Open
CryZe wants to merge 22 commits into
LiveSplit:masterfrom
CryZe:native-subsplits
Open

Add Native Subsplit Support#934
CryZe wants to merge 22 commits into
LiveSplit:masterfrom
CryZe:native-subsplits

Conversation

@CryZe

@CryZe CryZe commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

This introduces native subsplit support to livesplit-core by representing subsplits as first-class segment groups over the existing flat Run segment list. The flat list remains the canonical structure for timing, comparisons, history, auto splitting, and editor indices, while SegmentGroup / SegmentGroups provide a validated alternate view for display, editing, and serialization. Groups are one-level contiguous ranges, with the final Segment in the range acting as the major split, and group names and icons can either be explicit or inherit from that major split.

The LiveSplit XML format now stores segment groups directly after the segment list in <SegmentGroups>. Explicit group metadata is preferred whenever present, while older files without native segment groups are upgraded from the legacy dash-based subsplit convention. The legacy importer strips one leading dash from subsplit names, groups contiguous subsplits with the following major split, and supports the {Group Name} prefix syntax for giving a group a display name separate from the major split name. New-format files no longer interpret leading dashes as subsplit syntax.

The Splits component now supports these groups directly instead of requiring a separate subsplits component. SubsplitDisplayMode can show the flat list, show all groups expanded, or show a hierarchical CurrentGroupExpanded view where only the current or manually scrolled-to group is expanded. Closed groups summarize the major split and show the appropriate columns for the whole group, while expanded groups render a header and indented segment rows. When the timer is NotRunning, the hierarchical cursor starts before the first split, and when the run has Ended, it starts after the final split, so groups are collapsed by default in those states but can still be opened by manually scrolling into them.

The RunEditor now exposes segment groups as editable structure rather than name syntax. It can create, remove, rename, and preserve groups through segment movement, insertion, deletion, and selection changes. Group headers are represented in the editor state, group names can be left automatic via empty strings, and group icons can be set explicitly or inherited from the major split icon. The C API and generated bindings expose the new editor operations and state so frontends can edit groups directly while keeping flat segment indices stable.

CryZe added 14 commits July 4, 2026 11:32
This adds native segment groups on top of the flat segment list so runs can describe one-level subsplits without encoding them in segment names. The LiveSplit parser upgrades legacy dash-prefixed subsplits and the saver writes SegmentGroups, while OpenSplit child segments are flattened into groups where possible.

This also teaches the splits component, renderer, run editor, and C API about group metadata. Rendering tests now cover both upgraded legacy subsplits and native segment groups, including the classic icon and text indentation behavior.
This resets all split row metadata when reusing split states and assigns blank rows their visible row section index, so alternating backgrounds do not keep stale group state after subsplit groups collapse.
This makes filler rows continue alternating from the last visible visual section instead of from the number of displayed rows, matching hierarchical split backgrounds when groups are expanded.
This replaces the separate subsplit/header/section-end booleans with a single split role and removes non-rendering group and segment metadata from split rows.
This narrows the split row render state from semantic row roles to the single rendering decision the subsplits renderer needs: whether the row is indented.
This promotes the subsplit display mode setting from a numeric value to a typed setting value, adds binding support for constructing it by name, and makes current-group-expanded the default.
This adds localized labels and descriptions for the subsplit display mode setting across the available languages.
This makes current-group-expanded subsplits scroll through flat segment indices, opening the scrolled-to group while keeping the active group highlighted when it is collapsed. It also exposes the scrolled-to split state for renderers to draw a cursor.
This makes the run editor treat segment group membership as stable metadata while moving rows across group boundaries. Moving a segment out of a group now first makes it ungrouped, and a later move can add it to the adjacent group without dropping existing group metadata.

This also removes the unsupported major-split and clear-groups editor operations, exposes backend-controlled availability for creating and removing groups, and adds editor tests for grouped movement and mixed selection states.
This makes the run editor move an exactly selected segment group as a single visual section instead of applying flat row movement to each segment. Whole groups can now move past neighboring groups or ungrouped segments while keeping both group ranges and selection intact.

This also adds regression tests for moving whole groups up and down across grouped and ungrouped neighbors.
This relaxes segment group validation so a group can contain a single segment, which lets the run editor create a group from one selected row and preserve groups that shrink down to one segment.

This also keeps whole-group movement distinct from moving an edge segment out, fixes hierarchical scrolling without a current split, and adds regression tests for single-segment groups and stopped-timer subsplit scrolling.
This makes the run editor C API treat an empty segment group name as the automatic group name instead of requiring nullable strings for group names.

This also removes the hard-coded run editor group name handling from the wasm binding generator so string parameters are generated from the regular type metadata again.
This adds coverage for renaming a selected segment group while keeping the whole group selected in the run editor state.
This adds explicit icons to segment groups, including LiveSplit XML parsing and saving, run editor state, C API support, wasm binding helpers, and splits component rendering that falls back to the major split icon when no group icon is set.

This also makes current-group-expanded scrolling use virtual cursor positions before the first split while not running and after the final split once ended, so groups are collapsed by default in those states but still open when manually scrolled into.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class segment grouping (“native subsplits”) to livesplit-core, spanning run structure (SegmentGroups), parsing/saving (LiveSplit XML + OpenSplit), Splits component presentation (new SubsplitDisplayMode), editor/C-API/bindings, and associated tests/localizations.

Changes:

  • Introduces SegmentGroup / SegmentGroups on Run, including validation/repair and iteration as grouped/ungrouped views.
  • Extends parsing/saving: LiveSplit XML gains <SegmentGroups>, legacy dash-based subsplits are upgraded (pre-1.8.1), and OpenSplit “children” are converted into segment groups.
  • Updates the Splits component + renderer to support hierarchical group display modes, scrolling behavior, and new state fields exposed through the C API/bindings.

Reviewed changes

Copilot reviewed 41 out of 41 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/split_parsing.rs Adds tests for native <SegmentGroups>, legacy subsplit upgrade behavior, and OpenSplit child-to-group conversion.
tests/rendering.rs Updates snapshot hashes and adds a rendering test for native subsplit layouts.
tests/layout_parsing.rs Verifies subsplit-related layout parsing now maps to CurrentGroupExpanded.
src/settings/value.rs Adds typed Value::SubsplitDisplayMode conversions for settings plumbing.
src/settings/gradient.rs Adds Gradient::average_color() utility used for selection border coloring.
src/run/tests/segment_groups.rs Adds unit tests for segment group repair, iteration, and icon inheritance behavior.
src/run/tests/mod.rs Registers new segment_groups test module.
src/run/segment_groups.rs Implements SegmentGroup, SegmentGroups, iterator, and view types for grouped runs.
src/run/saver/livesplit.rs Bumps LiveSplit save version to 1.8.1 and serializes <SegmentGroups>.
src/run/parser/opensplit.rs Flattens OpenSplit segments while emitting top-level groups from one-level children.
src/run/parser/livesplit.rs Parses <SegmentGroups> and upgrades legacy dash subsplits for versions < 1.8.1 when no native groups exist.
src/run/mod.rs Stores segment_groups on Run and exposes accessors + iterator.
src/run/editor/tests/mod.rs Adds extensive tests for creating/removing/renaming groups, icon inheritance, and group behavior under segment moves/inserts/deletes.
src/run/editor/state.rs Extends editor state with per-row SegmentGroupState and new buttons for group operations.
src/run/editor/mod.rs Implements group create/remove/rename/icon operations and updates group metadata during edits and moves.
src/rendering/mod.rs Refactors selection outline rendering and adds custom-color outline helper.
src/rendering/component/splits.rs Updates split row rendering for hierarchical rows, indentation, alternating backgrounds by section, and scrolled-to border rendering.
src/localization/mod.rs Adds new localization keys for “Subsplit Display Mode”.
src/localization/english.rs Adds English strings for the new Splits setting.
src/localization/spanish.rs Adds Spanish strings for the new Splits setting.
src/localization/russian.rs Adds Russian strings for the new Splits setting.
src/localization/portuguese.rs Adds Portuguese strings for the new Splits setting.
src/localization/brazilian_portuguese.rs Adds Brazilian Portuguese strings for the new Splits setting.
src/localization/polish.rs Adds Polish strings for the new Splits setting.
src/localization/korean.rs Adds Korean strings for the new Splits setting.
src/localization/japanese.rs Adds Japanese strings for the new Splits setting.
src/localization/italian.rs Adds Italian strings for the new Splits setting.
src/localization/german.rs Adds German strings for the new Splits setting.
src/localization/french.rs Adds French strings for the new Splits setting.
src/localization/dutch.rs Adds Dutch strings for the new Splits setting.
src/localization/chinese_simplified.rs Adds Simplified Chinese strings for the new Splits setting.
src/localization/chinese_traditional.rs Adds Traditional Chinese strings for the new Splits setting.
src/layout/parser/splits.rs Maps legacy subsplit-related layout flags into the new subsplit_display_mode setting.
src/component/splits/tests/mod.rs Adds tests for new display modes, scrolling semantics, and typed settings value behavior.
src/component/splits/mod.rs Adds SubsplitDisplayMode, augments SplitState for hierarchy metadata, and switches Splits state generation to grouped display logic.
src/component/splits/column.rs Adjusts column calculations to support group header summarization via column_start_index.
capi/src/splits_component_state.rs Exposes is_scrolled_to_split via the C API.
capi/src/setting_value.rs Adds C API constructor for SubsplitDisplayMode setting values.
capi/src/run_editor.rs Adds C API for creating/removing/renaming groups and setting/removing group icons.
capi/bind_gen/src/wasm_bindgen.rs Adds WASM bindgen helper for setting segment group icons from Uint8Array.
capi/bind_gen/src/typescript.ts Extends TS types for SubsplitDisplayMode and editor segment group state + buttons.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/rendering/component/splits.rs Outdated
Comment thread capi/src/run_editor.rs Outdated
@CryZe CryZe force-pushed the native-subsplits branch from c505d42 to f645cf5 Compare July 6, 2026 20:22
CryZe added 2 commits July 6, 2026 22:34
This updates the software and SVG rendering expectations for layouts whose blank split rows now use the corrected alternating background pattern.
@CryZe CryZe force-pushed the native-subsplits branch from f645cf5 to 0e0884c Compare July 6, 2026 20:35
* Describes whether the currently selected segments are exactly one
* segment group that can be removed.
*/
can_remove_segment_group: boolean,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about removing multiple groups?

Comment on lines +1053 to +1055
icon: ImageId,
/** Whether the group icon is explicitly set instead of inherited. */
has_explicit_icon: boolean,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels redundant, we don't do this for the segment icons either.

Comment thread capi/bind_gen/src/typescript.ts Outdated
Comment on lines +1040 to +1043
/** Whether this segment is a subsplit inside the group. */
is_subsplit: boolean,
/** Whether this segment is the major split ending the group. */
is_major_split: boolean,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need is_major_split? I feel like this entire way of communicating the groups is weird in how they are attached to individual splits.

CryZe added 5 commits July 8, 2026 18:47
This adds range-based analysis helpers for segment times, comparison segment times, and segment deltas so closed subsplit groups can reuse the same analysis plumbing as normal split rows.

This also removes the duplicated calculation helpers from the splits column implementation and covers the range behavior with an analysis unit test.
This lets the Sum of Best Segments analysis calculate a specific segment range by seeding the existing prediction path at the range start.

Collapsed subsplit rows now use that range calculation for Best Segment coloring, so a grouped row can be colored as a best segment when the whole combined range beats the best known path for that range.
This imports the platform prelude in the analysis helper so the range-based Best Segments calculation can use the crate's no_std-compatible vec macro.
This adds smallvec as a direct no_std-compatible dependency and uses an inline prediction buffer for range-based Best Segments analysis.

Collapsed subsplit rows can now reuse the Sum of Best path calculation without heap allocating for typical split counts.
This exposes segment group state separately from flat segment rows in the run editor and removes the extra per-row group metadata that the frontend does not need for rendering or editing.

This also updates the C API and TypeScript binding generation for the restructured editor state and covers the group selection behavior in editor tests.
This makes split delta columns color collapsed subsplit group rows based on the full segment range the row represents, matching the already range-aware segment delta path.

This prevents a collapsed group from being colored as a best segment just because its ending segment is individually gold while the group as a whole is losing time.
@CryZe CryZe force-pushed the native-subsplits branch from 0ae7391 to b98eac3 Compare July 8, 2026 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants