Unified UI (Interface)


#1

After reading a lot of previous ideas about UI for Rust, the my current conclusions are:

  • The UI should be decoupled from the application (with the option to link to it).

  • Should work for asmjs/wasm :left_right_arrow: JS, classic terminal program :left_right_arrow: Qt/Curses, application running on a server :left_right_arrow: JS UI

  • Numeric identifier for Widget instances: Item Id

  • Widgets are created by calling the appropriate method on a common struct: UIChannel

  • Widget creation and manipulation is non-blocking

  • Data between program and UI is passed over a message channel only. This should reduce FFI bugs and enables security separation between application and UI.
    Open questions:

  • possible to clone widgets? (edit: The handle to the widget)

Item Id

(probably) u32 which computed in a deterministic way (+= 1)
This id is wrapped in various structs for the type system.

UI Channel

This is really a struct which implements a common trait and performs the translation to the underlying channel (JSON, CBOR, some channel to the UI thread written in C++, …)

  • create_xyz(&mut self, parent, args…) -> Xyz
    create an instance of widget type Xyz
  • move<T: Widget, W: Widget>(&self, new_parent: T, target: W)
    move a widget in the tree (fails if it would create a cycle)
  • delete<W: Widget>(&mut self, w: W)
  • listen<W: Widget + E, E: Event, F: Fn(E)>(&self, w: W, f: F)
    add a callback on a widget for a given event, ensuring the widget implements it.

Widget Tree

Every widget instance has a parent (another widget or the root window). The resulting tree is determines the default structure for the UI.

Events

The created widgets may be observed by registering the appropriate event at the channel.

Different high-level abstractions are possible:

  • the classic event listener yielding (target_widget, event_type)
  • closures as callback: channel.register(target_widget, event_type, callback) insert magic to ensure correct type here

feel free to improve this!


#2

In what scenario whould this be of use?

Some more aspects to think about:

  • Moving widgets to other parents (like detaching a toolbar)
  • generic layouters
  • event propagation (forwarding from child to parent or vice versa; capturing of scroll events for controls not having the focus - yeah Windows; I’m talking about you! :imp:)
  • capturing of global events (skinning, change of dpi, font-change, zooming, loss of hardware acceleration, maybe Hotkeys)
  • Vulkan/OpenGL-integration
  • Full-screen-support
  • Look and feel: Native or same on every platform?

Just my 2 cents :slight_smile:


#3

“The cloning of widgets” is really the cloning of widget handles on the Rust side. So more than one struct in Rust may have a “reference” to it.

  • Advantage to cloning: easier to write code
  • Disadvantage: Handles can’t be reused (the Rust side doesn’t know how many are alive)

My idea was to have a very abstract interface on the Rust side which then drives one of many implementations in C++ or what ever fits best.

The implementation is free to implement this. I consider the tree structure send by the Rust side to be the default structure, but the user should have the possibility to change it.

Global Events…
Requesting the DPI should be possible, as it is required to render text on the Rust side.