Skip to content

Custom slider widget for the whole shell (thickness, scroll handling, animated jumps) #931

Description

@romanstingler

Placeholder:
just a quick AI summary from the comment mentioned below (Open for discussion)

Split out from a side discussion in #883 (comment): the default iced slider is fairly crude for our use case, and we keep working around it at the call sites. This issue is to discuss replacing it with a custom slider widget used consistently across the shell.

Current state

slider_control() in src/components/slider_control.rs builds every slider in the shell (audio sink/source, brightness, and the keyboard backlight added in #883). It composes:

  • iced's built-in slider for the track/handle,
  • a wrapping MouseArea just to get on_scroll,
  • utils::remote_value::Remote<u32> for optimistic value display while the async backend catches up,
  • an ad-hoc .style() override for the audio overdrive (>100%) rail color.

Two consequences:

  • Scroll handling is duplicated per caller. Each module writes its own on_scroll closure that re-implements step size, clamping and message wrapping (Audio::on_scroll, Brightness::on_scroll), and the same closures are re-used for the bar indicators via format_indicator().on_scroll(). The step/clamp policy lives in the modules rather than in one place.
  • Appearance is limited by what slider::Style exposes. Rail thickness, handle shape, and the overdrive segment are only reachable through style patches, and there is no way to render e.g. a filled portion past the "normal" mark differently from the rest.

What a custom widget could give us

  • Thicker rail / different handle. Several of us prefer a thick slider; right now that's a theme hack rather than a design decision. Worth agreeing on the look first (mockups welcome).
  • Scroll as a first-class input. Step size and clamping become widget-level concerns configured once, instead of a closure per module. Removes the MouseArea wrapper.
  • Animated transitions. Values that arrive from outside the widget (a keybinding changing volume, a Remote value settling back to the received value, an external brightnessctl call) currently snap. A widget owning its own animation state could ease those jumps in one place. We already have components/animated_size.rs and components/slide.rs as precedent for widget-local animation.
  • A place for the overdrive/threshold rendering the audio slider needs, instead of a style closure at the call site.

Open questions

  • Full custom Widget impl (own layout/draw/on_event), or a thinner wrapper that still delegates to iced's slider internals?
  • Does the animation belong in the widget, or in Remote<Value> where the requested/received distinction already lives?
  • Should the bar indicators (format_indicator) share the same scroll configuration, or stay separate?
  • What's actually configurable by users vs. fixed by the theme? (See theme.rs — thickness would presumably be theme-derived, not a per-module config key.)

Scope

Out of scope for #883 — that PR should land with the existing slider_control. This is a follow-up refactor + design change touching audio, brightness, and keyboard backlight together.

Ref: #883, #883 (comment)

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions