Skip to content

Generic Widgets #25889

Description

@viridia

This is an alternative plan to the one sketched out in #25860

The idea is to make FeathersNumberInput, Slider, and Stepper generic over the numeric type. So an f32 slider would be Slider::<f32>. Only number types would be supported.

The advantages of this approach is that the monomorphized implementations could be simpler and more efficient, avoiding the need to dynamically cast between numeric types while editing and dragging. Most apps only concern themselves with a single numeric type, so all of the baggage for supporting f64 and i64 could be jettisoned for apps that only care about f32.

It would simplify the API, eliminating the need for things like the NumberInputRange type; we could just use the regular Rust RangeInclusive.

However, there are some serious challenges to overcome. The first is observer registration. There are three paths here:

  • Pre-register all of the numeric types in advance (i32, f32, i64, f64). Doable, but it means that everyone is paying the cost for number types they don't care about.
  • Manual registration. Also doable, but it breaks the "batteries included" philosophy we've adopted up to this point.
  • Automatic per-instance registration.

The third option is one that I have wanted to do for a long time, and which is already supported in BSN via the on() primitive. The basic notion is a component hook that registers the observers when the component is inserted - essentially a "required observers". This would also mean converting the design of the observers from universal observers to entity observers, which is a trade-off: it means many more individual observers (a set for each widget instance), but much more efficient dispatching, as we would no longer have 20 different global handlers looking at every mouse down/up/click event.

Going to per-entity observers would also simplify the design of the observers considerably, since we could remove the logic that filters out unrecognized entities - each observer now knows that it is only called for one entity and one entity only.

How exactly the component hook will be represented syntactically is TBD. Note that because component hooks are passed a DeferredWorld, the registration of the observers would have to happen at the next commands flush; this means that "initialization" observers (ones that trigger on the initial insertion of a component) would have to be rethought a bit.

A second, equally challenging issue is systems. Although Slider only uses observers (12 of them!), the FeathersNumberInput uses two systems; the reason for this is because of change detection on feathers theming. There's no way for a query filter to operate on a type-erased component, so we'd need to register these systems for each different number type.

Since there are no per-entity systems, this would have to be a single shared system that is registered lazily, meaning that the component hook would need to detect whether the system had already been registered.

Despite the complexity of all of this, I think it's worth considering, for reasons of overall efficiency, binary size, and developer experience.

@cart @alice-i-cecile @jbuehler23 @kfc35 @Tatsuya0330

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

    A-UIGraphical user interfaces, styles, layouts, and widgetsD-ComplexQuite challenging from either a design or technical perspective. Ask for help!S-Needs-BenchmarkingThis set of changes needs performance benchmarking to double-check that they helpS-Ready-For-ImplementationThis issue is ready for an implementation PR. Go for it!X-ContentiousThere are nontrivial implications that should be thought through

    Type

    No type

    Projects

    • Status
      Needs SME Triage

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions