Skip to content

Options with unmet dependencies are completely removed from the config screen if out of view #840

Description

@lunaynx

Original reporter & ticket id

Polyfrost/PolySprint#17

Category

Config UI

Bug description

If you have an option that is supposed to be greyed out because of a dependency, and but it is completely out of view, it will instead get removed entirely, and won't be re-added until the dependency is enabled.

Steps to reproduce

Part 1:

  1. Launch the game with PolySprint
  2. Ensure Fly Boost is off
  3. Close the config
  4. Scroll down to the bottom
  5. Observe the lack of Fly Boost Amount slider

Part 2:

  1. Turn on Fly Boost
  2. Scroll down to the bottom
  3. Turn off Fly Boost
  4. Scroll up until Fly Boost Amount is fully out of view
  5. Scroll down to the bottom
  6. Observe the lack of Fly Boost Amount slider

Anything else?

Claude's analysis

Affected: 1.0.6 (config screen). addDependency(option, condition) / @DependsOn (i.e. hides=falseDisplay.DISABLED).

Summary: An option gated by a grey-out dependency is only shown (greyed) while it is the live target of an onDisplayChange callback. On any fresh composition — opening the screen with the condition false, or a LazyColumn recycling the row after it scrolls out of view — it reverts to being fully hidden instead of greyed.

Repro: Declare a boolean option A and a slider B with addDependency("B", "A") (default hides=false). With A=false: (1) open config → B is absent, not greyed; (2) toggle A on then off → B shows greyed; (3) scroll B out of view and back → B is absent again.

Root cause: Property.canDisplay() (Property.java:78-80) returns display == SHOWN, collapsing DISABLED and HIDDEN. ConfigScreen.kt#rememberDisplay (:603-614) seeds its state from that boolean (if (canDisplay()) SHOWN else HIDDEN), so a DISABLED property initializes as HIDDEN on every composition; rememberVisibleOptions (:505-512) / SettingRow (:460-464) then drop it. DISABLED is only ever surfaced via the transient onDisplayChange callback, which is cleared on onDispose and re-seeded incorrectly on recomposition.

Suggested fix: Expose the real state, e.g. public final Display getDisplay() { return display; } on Property, and seed rememberDisplay from it: mutableStateOf(prop.getDisplay()) instead of the canDisplay() ternary (keep the onDisplayChange subscription for live updates). That makes DISABLED render as persistently greyed across opens and recycling. (Secondary note: onDisplayChange stores a single overwritable callback — fine for one subscriber, but worth documenting.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions