Skip to content

QML UI batch: null guards, delegate staleness, timer/focus races (9 verified) #2083

Description

@Swastik36

Summary

Batch of 9 verified QML UI bugs (null guards, binding/delegate staleness, timer/focus races). All confirmed by re-reading code at commit 4325418e. No fixes applied.

Environment

  • Shell commit: 4325418e (caelestia-dots/shell, Sep 2026)
  • OS: CachyOS Linux, Hyprland (Wayland)
  • Quickshell-based shell at ~/.config/quickshell/caelestia

Bugs

1. StyledProgressBar OOB via operator precedence (high)

  • File: components/controls/StyledProgressBar.qml:164,166
  • Code: manager.activeIndicators[index + 1 % manager.activeIndicators.length], then next.startFraction unguarded.
  • % binds tighter than +, so this is index + (1 % len). On async shrink/stale delegate next is undefined → throw. Compare guarded pattern at lines 146/152.

2. windowinfo Preview unguarded IPC + divide-by-zero (high)

  • File: modules/windowinfo/Preview.qml:74,92-93
  • Code: root.client?.lastIpcObject.size[0] / root.client?.lastIpcObject.size[1] — ?. guards only client; if lastIpcObject is null it throws. size[1]==0 yields Infinity → corrupt constraintSize.width. Line 93 client.lastIpcObject.at[0] / mon.name assume non-null.
  • Trigger: inspected window closes or Hyprland IPC lags while windowinfo open.

3. windowinfo Details ?. stops at client (high)

  • File: modules/windowinfo/Details.qml:51,56,62 (same pattern :26,78,84,89,95,101,106,113)
  • Code: root.client?.lastIpcObject.at[0] ?? -1, ...size[0] ?? -1, root.client?.workspace.name ?? -1.
  • If client is non-null but lastIpcObject/workspace is null, .at/.size/.name throws before ?? runs.
  • Trigger: window close / workspace teardown while details visible.

4. BatteryMonitor hibernate timer never cancelled (high)

  • File: modules/BatteryMonitor.qml:31-34,78-83
  • hibernateTimer (5s single-shot → SessionManager.hibernate()) is start()ed at/below critical but stop() appears nowhere in repo. Early-return on !onBattery and onReady/onPercentage don't cancel it.
  • Trigger: battery dips to critical then recovers (charger plugged in within 5s) — machine still hibernates.

5. Menu parent binding null deref (med, latent)

  • File: components/controls/Menu.qml:36-40
  • Code: const win = QsWindow.window; ... (win as QsWindow).contentItem with no null check.
  • Trigger: Menu created in async Loader/drawer before attaching to a window. Current callers live inside ContentWindow so latent, but robustness bug.

6. FolderContents delegate staleness (med)

  • File: components/filedialog/FolderContents.qml:191-201,217
  • Icon source and name.text assigned only in Component.onCompleted, never rebound on modelData change. No reuseItems/onModelDataChanged binding.
  • Trigger: scrolling large folder, rename/mime change, navigating directories shows wrong icon/filename.

7. Wallpaper double-create + error-path leak (med)

  • File: modules/background/Wallpaper.qml:18-35,127-131
  • onSourceChanged creates synchronously at startup (initial source fires it) and onCompleted schedules a second createObject via Qt.callLater. Old-image Timer only destroys when root.current.status === Image.Ready, so broken path → blank/error image current, old one leaked. Rapid source changes pile up.

8. Launcher/session focus race (med)

  • File: modules/launcher/Content.qml:109-123, modules/session/Content.qml:30-39
  • forceActiveFocus() in Component.onCompleted fires before drawer window visible/active (async loader + animation), so type-ahead goes nowhere until manual click. onSessionChanged/onLauncherChanged handlers ping-pong focus with no visible/active guard. Panels.qml:88,99 instantiates both always.

9. Areapicker unguarded destructuring + sticky state (med)

  • File: modules/areapicker/Picker.qml:57-60,87,198-201
  • let {at:[cx,cy], size:[cw,ch]} = client.lastIpcObject after only if (!client) continue — throws if lastIpcObject/at/size null, aborting loop. JSON.parse(hyprctl cursorpos) uncaught. checkClientRects only sets onClient=true, never resets on miss → stale sx/sy/ex/ey.
  • Trigger: transient windows while picker open, hyprctl errors.

Expected / Actual

Expected: guards on IPC nulls, timer cancellation, bindings instead of onCompleted assignment, focus after visible.
Actual: throws/stale UI/spurious hibernate under the triggers above.

Additional Context

Found by systematic review, each re-verified by a second pass reading the exact lines. Happy to split into separate issues or open PRs per fix.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions