Git Product home page Git Product logo

kas's Introduction

KAS GUI

Test Status Crates.io kas-text Docs Minimum rustc version

KAS is a pure-Rust GUI toolkit with stateful widgets:

  • Pure, portable Rust
  • Very fast and CPU efficient
  • Flexible event handling without data races
  • Theme abstraction layer
  • Winit + WGPU shell supporting embedded accelerated content
  • More portable shells: OpenGL, CPU-rendered, integration
  • Complex text
  • OS integration: menus, fonts, IME
  • Accessibility: screen reader, translation

Animated Scalable

Documentation

Examples

See the examples directory and kas-gui/7guis.

Design

Data or widget first?

KAS attempts to blend several GUI models:

  • Like many older GUIs, there is a persistent tree of widgets with state
  • Like Elm, event handling uses messages; unlike Elm, messages may be handled anywhere in the widget tree (proceeding from leaf to root until handled)
  • Widgets have a stable identity using a path over optionally explicit components
  • Like Model-View-Controller designs, data separation is possible; unlike Elm this is not baked into the core of the design

The results:

  • Natural support for multiple windows (there is no central data model)
  • Widget trees (without MVC) are static and pre-allocated, though efficient enough that maintaining (many) thousands of not-currently-visible widgets isn't a problem
  • Support for accessibility (only navigation aspects so far)
  • MVC supports virtual scrolling (including persistent IDs for unrealised widgets)
  • MVC supports shared (Rc or Arc) data
  • MVC and stateful widget designs feel like two different architectures forced into the same UI toolkit

Crates and features

kas is a meta-package over the core (kas-core), widget library (kas-widgets), etc. See here.

At this point in time, kas-wgpu is the only windowing/rendering implementation thus kas uses this crate by default, though it is optional.

Feature flags

The kas crate enables most important features by default, excepting those requiring nightly rustc. Other crates enable fewer features by default. See Cargo.toml.

Copyright and Licence

The COPYRIGHT file includes a list of contributors who claim copyright on this project. This list may be incomplete; new contributors may optionally add themselves to this list.

The KAS library is published under the terms of the Apache License, Version 2.0. You may obtain a copy of this licence from the LICENSE file or on the following webpage: https://www.apache.org/licenses/LICENSE-2.0

kas's People

Contributors

captchaking avatar dhardy avatar nyanpasu64 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kas's Issues

Text processing

This is not a trivial subject; it requires several steps:

  • optional translation step
  • decoding markup (from Markdown, simplified HTML, etc.)
  • font loading (handled by rusttype)
  • font association (currently we only support a single font)
  • conversion from unicode to glyphs (handled by rusttype)
  • line-breaking (handled by glyph_brush_layout)
  • real font shaping
  • alignment
  • justified alignment
  • caching and rendering (glyph_brush)
  • extraction of text metrics (use of line height for layouts and text highlighting)
  • text position → screen position translation (drawing edit bar)
  • screen position → text position translation (mouse selection/positioning)
  • clipboard interaction
  • rich-text clipboard support
  • efficient editing for large documents: kas-gui/kas-text#15

Currently we use glyph_brush (part of rusttype) to achieve a sub-set of the above.

disabled and error states

  • make it possible to disable any input widget
  • distinct graphics for disabled input widgets

Since this affects many widgets, possibly it should be part of WidgetCore / CoreData. Effects:

  • widgets should not be highlightable
  • keyboard navigation should skip the widget
  • active pointer/character grabs should be cancelled
  • Handler::action should not be called?

Additionally, at least char-input fields should support an error state, causing the background to be highlighted red:

  • allow EditBox to be able to be put into an error state

Goal: a general-purpose GUI toolkit

Sub-goals:

  • stable API
  • #9: widget traits
  • #2: full compliment of basic widgets
  • #3: layout generation
  • #4: input handling
  • #5: widget specification
  • #6: graphics
  • #7: internationalisation
  • #8: standard dialogs (including via desktop integration)
  • #12: theming & custom widgets
  • #13: text layout and editing
  • #23: font selection / management
  • #25: full compatibility with stable Rust
  • (#33, ...): portability
  • #59: colour management
  • (#196, #198, #199, #200,#201): user-customisation (theme preference, colour palette, font, ...)

Panic on an any example

image

Maybe i should install something additional?
Steps that i do:

  1. clone repository
  2. cd kas-wgpu
  3. cargo run --exmaple example name

Also when i compiled it, some crate require to install cmake, so, i think, you should mention it in some installation guide.

All it was on a linux peppermint 10. Also using last nightly linux gnu toolchain.

Make shaderc dependency optional

Hello!

We discussed a bit in the 3D widget of Iced I started to experiment with KAS. So far I struggled a bit to compile it and run it because of the shaderc dependencies. As the Rust crate is a wrapper around a C++ lib, some C++ tools make it somewhat difficult to built. So far I run into these case:

  • On Windows, when I try to build the KAS, shaderc wants ninja to build it. As I don't have it the build fail.
  • On Linux, I tried to cross compile it to Windows using mingw. shaderc use C++ 11 and for some reason the mingw tools I have on Linux doesn't seem to support it. I did not dive into it so it may be a incorrect use of me.

Anyway, I find that the use of shaderc make the compilation harder. I tried to compile KAS without shaderc and it was very easy. Basically, what I did was compile the existing shaders to SPIR-V using this online tool. I also edited the ShaderManager from

let vert_3122 = compile!(device, Vertex, "shaders_bin/scaled3122.vert");

to

let vert_3122 = device.create_shader_module(wgpu::include_spirv!("shaders_spir-v/scaled3122.vert"))

and it just worked.

So, my point is that since shaderc make the compilation less reliable it could be made an optional feature. That way, people who are OK with compiling their shaders before compile time can do it easily don't have to deal with shaderc. The other that want a shader compiler that can be used at runtime can enable the flag and use it as today.

What do you think?

Draw APIs

This is a partial successor to #16, mapping out how custom draw routines are supported. It should support both:

  • a medium-level API independent of the toolkit backend, providing things like "draw quad" (provided by kas::draw::Draw already, but needs plenty of refinement)
  • a low-level API providing direct access to whatever draw API the toolkit uses (I have some ideas, just needs some hacking)

Standard dialogs

GUIs should provide several standardised dialogs:

  • message box
  • progress box
  • file open/save
  • colour picker
  • font selector

Where possible, desktop-native implementations should be used; KAS implementations should be available for other cases.

Icons

Typically, desktop environments provide a library of standard icons (from "save" floppy disks and "undo" arrows to specific app icons). KAS should make it easy to:

  • select icons from the standard set available on the current desktop / theme
  • support multiple icon sizes and scaling appropriate for HiDPI
  • bundle extra icons with an app

Most of this functionality need not be specific to KAS but should be part of some other cross-desktop crate.

Geometry types

Currently KAS uses ad-hoc geometric types, because this was the fastest way to get started. There are several disadvantages however (frequent API extensions when required, some lack of uniformity of design).

There are already several vector / linear algebra libraries; one of these may suffice (although most include significantly more functionality than we require):

  • vek focuses on 2D and 3D geometry and graphics primitives and may be a good fit, and is templated over numeric types
  • euclid is used by Servo and Lyon with a design focus around 2D graphics (also some 3D), templated over numeric types and units
  • ultraviolet has a nice clean design (not templated) but is mostly aimed at 3D graphics (especially ray tracing)
  • cgmath is another 3D-graphics oriented lib, templated, and largely concerning vectors and matrices

Both Vek and Euclid may be viable choices. Euclid may make for easier interoperation with Lyon for an embedded canvas (as Iced now supports).

Coordinate system

Currently we place the origin at the top-left. Since version 0.5 WebGPU, now places the origin at the bottom-left. Should we also flip the Y axis?

Colour management

The current kas::draw::Colour type is naive about colour spaces and provides only a very basic API. For now it does what is needed, but we should probably replace it with the palette crate.

custom usage of MessageBox

Hi,

I am a bit struggling with a way to use a message box. What I want to achieve here is a custom message box that could used to edit a string value. In my case this is to bypass the limitation of the missing Open dialog. I copy pasted the default MessageBox and tweaked in to add the widget I need.

Here what it looks like:
image

My question is how I can send back data from the messageBox to "its parent" (ie, the code which created it). Basically I want to send a message with the following enum

enum MessageBoxResult {
    Ok(String), // new path,
    Cancel,
}

Also, I noted that when clicking the quit button of the messagebox sometime it close the popup as expected and sometime the program stop with this

error: process didn't exit successfully: `target\debug\kas-ui.exe` (exit code: 0xc000041d)

At first glance, it does not seems deterministic.

Do you have any idea what could happen here ?

Fonts are not clipped to scroll region when rendered

This is a known caveat of #19: most drawables are clipped to the region by use of a separate render pass; doing the same for fonts is less easy. To quote myself from that PR:

This is because glyph_brush draws everything in a single render pass, which doesn't let us apply GPU scissor regions as required. Options:

  • Queue sections for each region independently, using a new render pass for each region. This is what Iced does, but is not quite so easy with KAS's render pipe. This also implies one should use GlyphBrush::keep_cached to keep sections in the cache (which Iced currently doesn't; hint @hecrj).
  • Use a separate instance of GlyphBrush for each region. This makes it easy to queue sections correctly, but requires constructing new instances on demand (may cause lag) and prevents sharing of cached glyphs.
  • Use the z value included in sections and assign a distinct depth value to each layer. Needs further investigation.
  • Perform clipping within wgpu_glyph by adding a bounding rect to each section.

This turned out more complex than I had expected. @hecrj may I ask if you put any effort into investigating alternatives here?

Optimisation: avoid drawing out-of-bounds children

Now that ScrollRegion exists, child widgets may not be within a valid drawing region. We should cull these early. Probably the best way to do this is in [Widget::draw] implementations (i.e. culling as early as possible).

Potentially this allows taking advantage of knowledge of the widget representation in order to make drawing a fixed-size region of a large slice of children O(1) or at worst O(log n) instead of the current O(n) in the number of children — see the dynamic example which is slow with 3000 children.

Pointer/touch behaviour

In KAS, pointer/touch event behaviour is handled by widgets:

  • the obvious click-to-activate on buttons, checkboxes, etc.
  • the usual click-drag or hover to navigate menus
  • click-drag and touch-drag on "pan" widgets, e.g. Mandlebrot explorer
  • click-drag and touch-drag on empty space inside ScrollRegion to pan
  • touch-drag and ctrl+click-drag to pan text-edits
  • click-drag and touch-wait-drag in text-edits to select text

Most of the above are mostly standard UI behaviours with a couple of exceptions (ctrl+click-drag to pan in text-edits and click-drag to pan in empty space). Questions:

  1. Do we want to keep the non-standard ctrl+click-drag to pan? Ideally yes, but possibly with a configuration option.
  2. Alternatively we could emulate touch behaviour: click-wait-drag to select text. But definitely not as default behaviour.
  3. If we allow text-selection within labels, do we allow the same behaviour as above? The problem is that ScrollRegion currently supports panning by touch/click drag on any widget in the region which does not handle this event; if Label starts handling clicks for text selection, this behaviour will be mostly unavailable. We could make this compatible with the Ctrl+click behaviour to support both, but possibly not with the timer behaviour (currently the widget has to choose whether to handle a click or not immediately).

Add to 7GUIs

I just discovered this library and it looks good to me. It seems mature enough to "accept the challenge" and add it to 7GUIs 😉.

Scalability of reconfigure/resize/redraw

Currently redraw, reconfigure and resize affect a whole window at once. This is fine for small numbers of widgets (and only really causes perf. issues with around a million widgets when compiled with optimisations), but it would be nice to still support fast operations with large numbers of widgets.

  • event handling: almost all of this is targeted thus scales well (the dynamic example demonstrates an exception: radio box activations are sent to the whole group, causing lag from groups of around 10'000 radio boxes; real apps are unlikely to have this issue)
  • redraw: only visible parts of the widget tree get draw commands, so this already scales well
  • reconfigure: currently a whole-window operation; #91 has some notes on this
  • resize: currently a whole-window operation

Notifying that localised updates are needed should be easy: add Option<WidgetId> payload to some TkAction variants (none affect more than two widgets simultaneously, and only drawing affects two).

Carrying out localised reconfigure: see #91.

Carrying out localised resize: in general this may require significantly redesigning how size rules are calculated, but we can probably take a short-cut by only considering a few widgets (e.g. List and Stack) and having those only resize visible children.

Conclusion: all we really want is support for partial reconfigures (#91) and partial resizing within a few specific widgets, limited to visible children (which implies these must be able to resize other children on TkAction::RegionMoved events and must know which portion of themselves is visible).

Widget specification

I see three possible forms of specification:

  • Declarative, programmatic specification: this has been the main focus so far (via macros), and is the preferred option for anything which can be compiled in.
  • Dynamic, programmatic specification: at a basic level, we need support for dynamic content (reconfiguration); given this, the rest is easy.
  • Dynamic content from external specification (file input): many GUIs support configuration either via something like XML or via a scripting language. We should support at least one of these options but currently have no plans.

Support OpenGL / non-gfx backends

Via WebGPU, KAS already supports DX11 / DX12 / Vulkan / Metal APIs. It appears that wgpu aims to support GL / other backends directly, so maybe we can just wait for that.

There are some alternatives, e.g. Piet supports Direct2D and Cairo backends. In turn, Cairo supports multiple targets (with hardware acceleration in some cases). Building a KAS toolkit over Cairo or Piet (or maybe even Druid) would be feasible, though significant extra work.

Dynamic / animated widgets

Currently KAS supports updating widget state via callback, as in the clock and stopwatch examples, but it leaves something to be desired:

  • animated widgets need callbacks registered manually from the window
  • multiple animated widgets on the same window would each use their own timer
  • frame rates are controlled from the point of view of the animated widget, not the app

Instead, we should handle things more like a game engine:

  • give widgets an API to express frequency for self-updates
  • give widgets an API to do self-updates
  • unify timers across the window, scheduling frames according to required update frequency
  • allow frame-rate limiters
  • let all applicable widgets update themselves on each frame

Internationalisation

Currently low-priority but necessary in a UI:

  • string translation support
  • right-to-left fonts
  • ...

Layout generation

Support layout generation for widget trees:

  • fixed content horizontal/vertical lists
  • fixed content grids
  • cell spanning in grids
  • dynamic content parents
  • internally-adjustable regions

Draw passes

Currently, contents of a draw pass are tracked within a draw pipe, and the draw-order between pipes is hard-coded within DrawPipe.

We should probably separate handling of passes from the pipes. This means splitting the *Pipe types into the pipe-proper and per-pass data.

Possibly we could also allow more flexibility in the ordering of draw operations, perhaps by using only one *Pipe per pass and adding more passes.

Notation: *Pipe here refers to FlatRound, ShadedRound, ShadedSquare, and CustomPipe, not DrawPipe.

pop-ups input and drawing

#76 implemented pop-ups and a ComboBox widget, but some things are left wanting:

  • a "menu" widget (not much different than the ComboBox other than nested menus)
  • a menubar
  • close on click outside the pop-up
  • fix fonts from underneath widgets drawn over pop-ups
  • optimise: avoid need to reconfigure and to heap-allocate on opening a pop-up

Currently, a pop-up is spawned by creating new widgets, requesting these be added as a pop-up layer, and reconfiguring all widgets in order to enumerate the new widgets. All widgets continue to respond to input except for new mouse/touch inputs over the pop-up.

What should happen:

  • menubars and possibly ComboBox widgets should continue to respond to input while a pop-up is open while all other widgets from the base layer should (probably) not; this might imply that menubars need to be added to a pop-up
  • sub-menus may need to overlap previous menus; this implies they should be in a new pop-up layer, however lower pop-up layers should still respond to input
  • in-progress input actions may need to be cancelled when a pop-up opens (e.g. a mouse-click when a touch event opens a menu)
  • graphics from the base layer will therefore be static and could be drawn from a texture (possibly with a blur or de-saturation effect applied); this may be the easiest fix for fonts overlapping pop-ups but is not sufficient to fix the multi-layer menu case

The allocation and enumeration issues are less tightly coupled:

  • pop-up widgets could be statically allocated in the parent widgets to avoid allocation and reconfigure when opening a pop-up, however for ComboBox this might require a reconfigure when adding/removing entries (unless a single widget can have multiple instances somehow)
  • alternatively the pop-up layer could have a separate number-space (so that the reconfigure only affects pop-up widgets); this requires a separate event::ManagerState

Custom widgets

Custom widget implementations need to define several details. It turns out this is not trivial, though the following provides good inspiration for a design.

Size

The widget should return a SizeRules as e.g. Theme::size_rules. This requires some details, e.g. a draw object (to generate text dimensions), the axis, and the DPI.

Drawing

The question here is over which API should user-implemented widgets do their drawing? Some options:

  • kas::draw::Draw: a fixed draw trait; does not allow themes to influence custom widgets; does not allow direct access to things like wgpu::Device
  • generic draw object with user-specified bounds (e.g. a hypothetical kas_wgpu::DrawWgpu); allows lower-level drawing but still does not allow theme influence; additionally, use of generics will require some redesign of widget traits
  • theme: we could require themes to implement a larger API including things like "draw_button_surface"; this allows higher-level draw operations at the cost of a much bigger Theme API
  • draw trait(s) + theme colour: hybrid approach allowing some theming of custom widgets

Probably the best answer here is the most complex one: extend the Theme trait API and allow direct access to draw traits via generics, along with theme colours.

Alternative (also for size): allow custom widget classes, and use a theme-extension to draw this. This option requires much of the above still: high-level draw operations in the Theme trait, theme colours, access to low-level draw devices.

Event handling

This is covered already by the event::Handler trait (may require tweaking).

Errata

Further details which should be customisable:

  • alignment
  • whether the widget accepts keyboard grabs

Inter-window/thread communication

Inter-window communication

Currently, multiple windows have few options for communication.

Shared memory

Sharing data between windows is fairly easy, e.g.:

  • once-init data in a lazy_static
  • mutable data in a thread_local with a RefCell
  • mutable data on the stack (main function or other caller) with a RefCell

However, this does not enable update-on-demand or querying window state.

Message passing

Sending pollable messages via mutable storage or std::sync::mpsc::channel is
possible, but does not allow one window to update another or query its state
immediately.

A crude way to allow one window to update another would be to give each window
a user-defined "update" function, and add an API to TkWindow to call "update"
on all windows or on a specific window.

Due to the way Loop::handle calls Window::handle_event with a mutable
reference to the window and to the shared data, I don't believe it's possible to
directly call any methods on another window with the current set-up.
This could be changed if (a) the window list can be accessed from static memory
and (b) each window is stored via a RefCell.

Multi-threaded apps

All GUI functionality is single-threaded: winit (and several platforms) require
this, and nothing it does directly is very resource-intensive (except perhaps
drawing, which gets offloaded to the GPU).

Never-the-less, the GUI should support multithreading. The following requires no
special support:

  • accessing shared data
  • sending messages via channels
  • waking threads blocked via parking and barriers

However, responding to an external event or completion of some background
computation does. We need at least to be able to:

  • update one or all windows
  • terminate the GUI

Deliverables

  • closing a window from another window's event handler
  • closing a window from another thread
  • updating a window from another window's event handler
  • updating a window from another thread

#36 achieves the first two items above. The last two require some model for sending "update" messages (with or without data payload) to windows.

Macro syntax

Some things need review; e.g. the outer widget attribute on derive(Widget).

make_widget! has somewhat arbitrary syntax; likely this won't be revised before the next release.

Themes / customisable graphics / render abstraction

Since "toolkits" like kas-wgpu already have several complexities (interfacing with windowing, initialising graphics system, implementing some type of abstraction over the graphics system), it appears that this type of crate is not the best home for theme-specific code.
Additionally, some type of abstraction of the graphics system would be very useful for custom widgets.

Therefore it is desirable to have:

  • a high-level graphics abstraction (rectangle, frame, text, etc., each with a customisable attributes) for use by both themes and user-defined widgets (implying the abstraction should be independent of kas-wgpu)
  • low-level access to the graphics system (specific to wgpu)
  • a plug-in system, allowing a third-party theme to be injected into a kas-wgpu (or other) toolkit
  • a custom-widget system, independent of kas-wgpu but able to use extensions specific to this toolkit (direct access to wgpu)

Font selection

Font selection needs improvement. Current model:

  • embed /usr/share/fonts/dejavu/DejaVuSerif.ttf at compile-time
  • (default) discover "best sans-serif" system font

Since almost every platform already has a default font (usually configurable), ideally we would use that. Probably this would require a significant-size crate dedicated to reading platform font configuration (including multiple DEs on Linux) which is beyond the scope of this crate.

Basic graphics

The current focus is kas-rgx:

  • minimally-functional / proof of concept
  • hardware accelerated via wgpu

See also #10: Pretty graphics


Renderables include:

  • frames
  • optional visible separators for grid/row/column layouts
  • buttons
  • text
  • raster images
  • vector images

Shortcut key bindings

Issue: on X11, the calculator doesn't support some keyboard shortcuts, e.g. Asterisk for multiply and Return.

Also concerns Wayland: the key remapping situation is similar.

Blocker: rust-windowing/winit#1266

Bad error msg when widget response payload not convertible to parent's type

This error is inadequate:

$ cargo run --example gallery
   Compiling kas-rgx v0.0.2 (/home/dhardy/projects/gui/kas/kas-rgx)
error[E0277]: the trait bound `Item: std::convert::From<()>` is not satisfied
  --> kas-rgx/examples/gallery.rs:62:13
   |
62 |     toolkit.add(window)?;
   |             ^^^ the trait `std::convert::From<()>` is not implemented for `Item`
   |
   = note: required because of the requirements on the impl of `kas::event::Handler` for `main::AnonWidget<...
[three more lines]

Cause: parent widget's response message type is Item which does not implement From<()>; a child uses response message type ().

Expected: a pointer to the offending child widget with explanation. This would require type checking within the macro, which I don't believe is possible.

Possible improvements/workarounds:

  • force earlier type checking
  • use more meaningful names where possible (instead of AnonWidget)
  • special case for () responses (which always convert to Response::None)

Themes

List of short-term goals (in addition to #12 and #17):

  • add a second, "flat", theme
  • allow run-time switching of themes
  • allow multiple concurrent themes (override by widget)?
  • include better, dynamic lighting for "fancy" theme
  • add a "detail" layer
  • add multi-sampling

Full complement of basic widgets

Implement all expected basic widgets:

  • text labels
  • image/mixed labels
  • text buttons
  • image/mixed buttons
  • check boxes
  • radio boxes
  • combo/list menu boxes
  • single-line edit boxes
  • multi-line edit boxes
  • progress bars
  • sliders
  • spin boxes

Layout widgets:

  • frames
  • fixed-content parents
  • dynamic-content parents
  • scrollable regions
  • stacks

UI controls:

  • tab bars
  • scroll bars
  • resizing grips

Also some specialised widgets:

  • date / time picker(s)

Complex / derived widgets:

  • menu button
  • menu bar
  • scroll-area with scroll bars
  • tabbed stack
  • splitter region (manually adjustable internal sizes)

Correct event handling for scroll regions

In #19, scroll regions intercept mouse/touchpad scroll events and pass on the rest. This has two side-effects:

  1. If multiple scroll-regions are stacked, the outer one interprets all scroll events; ideally the inner should (when it can react to them)
  2. Touch events are not handled

Plan to handle this:

  1. Use "bubble-in, bubble-out" for some events: the handler for the inner-most widget receiving an event it can't handle should return it; certain widgets should check for returned events they can handle. This requires significant adaptations to event handling (e.g. a return of Response).
  2. Use unhandled touch events (those not on a button or other activatable widget) for scrolling. (May need refinement later.)

MenuBar and opening/closing menus

MenuBar currently has a lot of TODOs due to lacking a method for determining which sub-menus are open. A solution here might also replace Event::OpenPopup and ClosePopup.

Possible solutions:

  • A Menu trait with a method returning the WidgetId of the parent of an open menu, if any.
  • Menu trait + enum allowing matching of menus. (Not extensible.)
  • Adding more functionality to the base traits to avoid a special Menu trait — but in this case, only menu-specific traits would be implementing this correctly anyway, so it isn't really a more general solution.

Input handling

Input handling:

  • mouse activation
  • touch activation
  • keyboard navigation & shortcuts

Drag actions:

  • mouse drag
  • touch drag

File hover/drop: moved to #98

Text editing: moved to #13

Glossary:

  • touch: touchscreen support
  • activation: button click and click-to-focus

Field types created by impl_singleton! are opaque

When creating a widget with make_widget!{ .. }, fields cannot be accessed from outside the defining macro due to the anonymous type.

I had hoped this could be fixed with some type of type alias feature, but alas, this appears limited to trait implementations.

To some extent this can be worked around with traits, e.g. from the stopwatch example:

#[widget] display: impl HasText = make_widget!{
    frame => EmptyMsg;
    struct {
        #[widget] display: Label = Label::from("0.000"),
    }
    impl HasText {
        fn get_text(&self) -> &str {
            self.display.get_text()
        }
        fn set_string(&mut self, tk: &mut dyn TkWindow, text: String) {
            self.display.set_text(tk, text);
        }
    }
},

allowing self.display.set_text(tk, &self.dur_buf); to be called from the outer widget later.

Ideally in the above, it would be possible to directly call self.display.display.set_text(tk, ..).


Proposal: build_widget! macro

Move most of the make_widget! code into a separate macro, defining the type, and allow direct use of this.

Issue: implicit typing within make_widget! uses generics, thus the resulting type requires parameterisation on use. Ideally, we would not have to use generics but could use some type of "magic type alias".

Widgets: messages, handlers, generics

At the time of writing, TextButton looks like:

pub struct TextButton<M: Clone + Debug + 'static> {
    ..,
    msg: M,
}

This is simple, but incompatible with VoidMsg and does not support direct handlers.

In contrast, EditField uses the EditGuard trait to support direct handlers with an mgr parameter, shared mutable state and optional message result. CheckBoxBare is in between:

pub struct CheckBoxBare<M: 'static> {
    ..,
    state: bool,
    on_toggle: Option<Rc<dyn Fn(bool) -> M>>,
}

Its closure has a state parameter and allows some direct handling, but no mgr or mutable state or optional result.

Challenges:

  1. The HasBool etc. class traits allow setting values without an mgr parameter, thus if these impls are to call a handler they cannot provide mgr. EditField solves this via separate edit and update handlers.
  2. The obvious TextButton<M, F: FnMut(..) -> M> parametrisation results in unnameable types everywhere, which is a pain.
  3. Box<dyn FnMut..> or Rc<dyn Fn..> result in simpler, nameable types with minimal overhead, however the former does not support Clone and the latter does not support mutable state (Rc<dyn FnMut..> would, but it would be shared between clones, which is unexpected).
  4. The guard system used by EditField is very flexible, but much more complex (more API). It seems preferable to use a simpler solution without a strong reason for this complexity.

Dynamic window creation, modification, deletion

Currently, all windows must be added before the event loop is started and cannot be modified. It should be possible to:

  • add windows from within event-handlers
  • inspect window state between window-close and state-deletion
  • modify running windows (e.g. to adjust font properties)

Pretty graphics

There are several paths available to a prettier UI:

  • improvements to kas-rgx (see #6): this crate will likely remain focussed on development, but improvements which don't conflict with that goal are welcome
  • a new crate based on wgpu / glutin / ...
  • use of external / native GUI libraries for rendering: may be possible, but the last approach (kas-gtk) was abandoned due to complexity

Themes / Styling

There are currently no plans on how this is implemented; it has more to do with the rendering crate than with kas core; this is currently not of interest within kas-rgx.

Nightly feature usage

KAS currently requires may use unstable language features when using nightly Rust.

Features enabled by the nightly feature flag (optional but recommended):

  • proc_macro_diagnostic — used in kas-macros for better diagnostics
  • min_specialization — used by the Label widget to select appropriate draw routine
  • specialisation: used by TryFormat utility to use Debug impl where available on generic type
  • doc_cfg — enables annotation of feature flags in documentation
  • generic_associated_types — used in several places where associated types require a lifetime parameter, and desired to use in several more places. unsafe and boxing/cloning used as workarounds. This feature is now stable!

Features not used but desired:

Widget traits

This may see some adjustment but the basics are there:

  • Core data trait
  • Layout helper trait [could be merged into Widget, but little reason to do so]
  • Widget master trait
  • widget Classes, with class-specific properties
  • general properties (e.g. shortcut keys)

Additionally, we have a trait for windows and dialogs which may see revision (or be removed):

  • Window trait

Shortcuts and context menu configuration

Standard actions: currently some standard shortcuts like Ctrl+A, ..+X, +C, +V, Ctrl+Z are hard-coded. These are standard for most Linux and Windows systems but incorrect for OSX, and, further, should be configurable.

Custom actions: it should be possible for an app to create custom actions, most likely on widgets, and be able to set up (local) key bindings to these actions.

Global shortcuts: allow creation and configuration of shortcuts which are active globally, targetting custom actions.

Context menus: allow widgets to add actions to their context menu, both standard ones like Undo and custom ones (including sub-menus like spelling suggestions). [Also, implement context menus.]

  • platform-specific default shortcuts
  • configurable shortcuts
  • widget-targetted globally-active shortcuts?
  • global shortcuts?
  • context menus

File drag&drop

File hover/drop:

  • determine coordinate (may be a little tricky since winit doesn't communicate whether the input source a mouse or touch event)
  • send FileHovered and FileDropped to the appropriate widget (including when moved over a new widget)
  • support grabs (include source to allow use of Manager::request_grab)

Currently, winit only reports HoveredFile once on hover-over-window start, HoveredFileCancelled when leaving the window, and DroppedFile when releasing. These include a path but nothing else. Additionally (at least on X11), the usual cursor and touch events are not received. It is therefore not possible to implement this over the current winit API. See rust-windowing/winit#1550

Roadmap: 0.1 release

A priority list for making a somewhat usable release:

  • text processing #4, #13: at least add clipboard integration and ability to move edit position left/right
  • graphics #6: visible separators
  • dynamic layouts #5, #2
  • custom widgets #16, #3
  • layout memory & adjustment handles #3, #2
  • check-boxes #2: visible indicator needed
  • radio boxes #2: linking of multiple widgets needed
  • scrollable regions #2
  • multi-window example with synchronised data

Menubar keyboard navigation and drawing

Currently the menubar opts out of keyboard navigation. We should:

  • have some way to focus the menubar
  • support Tab-navigation of menus
  • support keyboard navigation (arrow keys)
  • support Alt+key accelerators as most GUIs do

Partially related:

  • draw menu backgrounds
  • use specific menu graphics (not buttons)

To achieve the above, likely we need menu-specific widgets and an extra trait.

Widget Identifiers

The WidgetId type is currently a simple numeric identifier assigned to widgets in a depth-first search. It is widely used and should ideally have all of these properties:

  • small, fixed-size type
  • be robust against view / resizing adjustments (i.e. not a coordinate)
  • allow O(1) tests of whether a widget is an ancestor of a given identifier (this could be achieved by giving widgets an identifier range, instead of just the last id in that range)
  • allow O(log n) identification of which child is the ancestor of an id given n children (hard in the general case when children can have different numbers of descendants; guesses assuming equal numbers of descendants may be enough)
  • allow widgets with dynamic content (e.g. List) to add/remove children without having to re-enumerate all widgets

Currently things are "fast enough" with a few thousand widgets on a decent machine (even a few hundred thousand in an optimised build, ignoring other issues like calculating size requirements and notifying all subscribed radio boxes) so this isn't a priority, but being able to scale well is a goal (e.g. for spreadsheets where each cell is a widget).

I have some additional notes offline, but no real solution.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.