Skip to content

kud1ing/guiver

Repository files navigation

guiver

guiver tries to make GUI programming with Rust simple, but perhaps without perfect efficiency.

guiver can be used in immediate mode, which is inspired by egui. In this case it is just a thin wrapper around druid-shell for event handling/windowing and Piet for rendering.

There are retained mode widgets that can be used with the help of an optional widget manager. The widgets are decoupled from the application data via message passing. This is inspired by Tk commands and egui. The widget size constraint system and some layout widgets are influenced by Flutter.

The example code may appear a bit verbose. On the upside you get simple setup and simple control flow.

Bildschirm­foto 2022-12-16 um 09 19 34

Bildschirm­foto 2022-12-16 um 09 21 12

Bildschirm­foto 2022-12-16 um 09 26 50

Bildschirm­foto 2022-12-16 um 09 22 23

Bildschirm­foto 2022-12-16 um 09 25 50

Bildschirm­foto 2022-12-16 um 09 27 34

Bildschirm­foto 2022-12-20 um 08 08 19

Bildschirm­foto 2022-12-20 um 08 13 10

Bildschirm­foto 2022-12-20 um 08 16 51

Bildschirm­foto 2022-12-20 um 08 21 59

Bildschirm­foto 2022-12-20 um 08 19 20

Bildschirmfoto 2022-07-05 um 19 43 55

Status

  • Operating systems:
    • macOS: tested intensively
    • Windows: works in general, probably has rough edges
    • other: testers are welcome
  • Widget manager:
    • handles widget lifetimes
    • handles a main widget
    • handles widget focussing – including tab order
    • handles copy/paste
  • Widgets:
    • Button
    • Hyperlink
    • Placeholder
    • Text
    • TextInput
  • Layout widgets:
    • Center
    • Column
    • Expanded
    • Grid
    • Padding
    • Row
    • SizedBox

Developer guide

Widgets are decoupled from the developers code via the WidgetManager. It owns the widgets and manages their lifetimes. Widgets are created via new_*() methods and are modified and composed via Commands. Commands allow transactional modification, where a re-layout happens once at the end.

Widgets implement the Widget trait. The methods can be used by the WidgetManager and by other widgets (e.g. the TextInput widget contains a Text widget). A widget reacts to user Events and possibly creates WidgetEvents accordingly. The developer code can handle those widget events.

Backlog

  • guiver-wasm:

    • add WidgetManager
  • guiver-piet: bundle the Commands per WidgetId: add Command::ToWidget<Vec<WidgetCommand>>

    • add WidgetCommand
  • guiver-piet: unify widgets:

    • add fn core(&self) -> &WidgetCore to PietWidget or Widget?
    • add a method to return an iterator over th child widgets
    • default implement PietWidget::paint() using core() and the child widgets iterator yielding method
  • guiver-piet: add support to PietWidget for the concept of layers/z-order for dropdown boxes, tooltips etc.

  • add selectors:

    • add Command::SetClass(Option<C>)
    • add WidgetSelector:
      • All
      • WithId(WidgetId)
      • ChildrenOf(WidgetId)
      • ParentOf(WidgetId)
      • use it in
        • Command::Destroy
        • Command::RemoveChild
          • remove Command::RemoveChildren
        • Command::RemoveEventObservation
        • Command::AddEventObservation
        • Command::SetDebugRendering
        • Command::SetFill
        • Command::SetFont
        • Command::SetHorizontalAlignment
        • Command::SetIsDisabled
        • Command::SetIsHidden
        • Command::SetStroke
        • Command::SetValue
        • Command::SetVerticalAlignment
  • add Widget::class() -> Option<C>

    • implement in Core
    • add WidgetSelector::WithClass(C)
  • sketch a WASM backend?

  • sketch a cacao backend?

  • sketch a egui backend?

  • guiver-piet: text:

    • test_selected_text_replaced():
      • fix
      • add a test case using umlauts
    • test_selected_text(): add a test case using umlauts
    • test_text_inserted(): implement
      • add a test case using umlauts
    • TextInput::set_selected_value(): implement
    • determine the graphical positions:
    • paint(): paint the cursor
    • paint(): paint the text selection
    • TextInput::handle_event(): adjust the text cursor on arrow left/right
    • TextInput::handle_event(): adjust the text selection on Shift + arrow left/right
    • TextInput::update_caret_character_index(): implement
    • TextInput: Meta+X should cut the selected text
    • TextInput: if a text is too large to fit in, the size of the text input should not increase but truncate
    • TextInput::handle_event(): select the whole text on double click
  • guiver-piet: add support for scrolling

    • how?
  • test::widgets_layout(): add remaining layout widgets

  • test::widgets(): add child widgets to the layout widgets

  • move WidgetManager::focused_widget to WidgetFocusOrder?

  • publish 0.1.1, once the updated druid-shell >0.7.0 is released

  • make Text selectable:

    • via double click
      • how?
    • via click + drag
  • add WidgetCore::is_disabled?

    • makes sense for non-layout widgets:
      • Button
      • Hyperlink
      • Text
      • TextInput
    • does it make sense for layout widgets?
      • should they pass the command down to its child widgets?
  • Widget: remove flex_factor()?

    • Pro:
      • for Grid it needs to be held externally (GridColumnProperties, GridRowProperties)
    • Cons:
      • does it make usage of Column, Row less pleasant?
  • should all container widgets clip the child widget's painting?

    • Pro:
      • restricts misbehaving widgets paint
    • Con:
      • performance impact if clipping is unnecessary?
  • add integration tests:

    • widgets():
      • add Column
      • add Padding
      • add Row
    • add for Column:
      • test_apply_size_constraints()
      • test_handle_command()
      • test_handle_event()
    • add for Row:
      • test_apply_size_constraints()
      • test_handle_command()
      • test_handle_event()
    • add for Center:
      • test_apply_size_constraints()
      • test_handle_command()
      • test_handle_event()
    • add for Padding:
      • test_apply_size_constraints()
      • test_handle_command()
      • test_handle_event()
  • add unit tests:

    • add for Button:
      • test_apply_size_constraints()
      • test_handle_command()
      • test_handle_event()
    • add for Placeholder:
      • test_handle_command()
      • test_handle_event()
    • add for Text:
      • test_handle_command()
      • test_handle_event()
    • add for TextInput:
      • test_apply_size_constraints()
      • test_handle_command()
      • test_handle_event()
  • add a Table widget

  • example layout_expanded_row_column.rs: make the row not grab all height

  • all layout widgets need to clip too big child widgets

  • add layout widget Stacked + Positioned

  • Hyperlink: make it possible to remember/pass "was visited" status across widget lifetimes

  • support text selection:

    • Shift + cursor movement
    • Meta+A
  • support Drag and drop

  • implement 7GUIs

    • 7GUIs "Flight Booker"
      • implement DropdownBox (needs 2 layers)
    • 7GUIs "Timer"
    • 7GUIs "CRUD"
    • 7GUIs "Circle Drawer"
    • 7GUIs "Cells"
  • optimize: do not paint on every event. Make the widgets request the repaint in a region/Rect

  • add Redmond 31 widgets?

  • add Python bindings?

  • provide wrappers to native widgets?

  • provide a WebAssembly demo

  • consider decoupling from druid-shell/piet

See also "So you want to write a GUI framework"

License

Licensed under either of

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions.