cushy::widget

Trait Widget

source
pub trait Widget:
    Send
    + Debug
    + 'static {
Show 23 methods // Required method fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>); // Provided methods fn summarize(&self, f: &mut Formatter<'_>) -> Result { ... } fn full_control_redraw(&self) -> bool { ... } fn layout( &mut self, available_space: Size<ConstraintLimit>, context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size<UPx> { ... } fn mounted(&mut self, context: &mut EventContext<'_>) { ... } fn unmounted(&mut self, context: &mut EventContext<'_>) { ... } fn hit_test( &mut self, location: Point<Px>, context: &mut EventContext<'_>, ) -> bool { ... } fn hover( &mut self, location: Point<Px>, context: &mut EventContext<'_>, ) -> Option<CursorIcon> { ... } fn unhover(&mut self, context: &mut EventContext<'_>) { ... } fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool { ... } fn focus(&mut self, context: &mut EventContext<'_>) { ... } fn advance_focus( &mut self, direction: VisualOrder, context: &mut EventContext<'_>, ) -> EventHandling { ... } fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool { ... } fn blur(&mut self, context: &mut EventContext<'_>) { ... } fn activate(&mut self, context: &mut EventContext<'_>) { ... } fn deactivate(&mut self, context: &mut EventContext<'_>) { ... } fn mouse_down( &mut self, location: Point<Px>, device_id: DeviceId, button: MouseButton, context: &mut EventContext<'_>, ) -> EventHandling { ... } fn mouse_drag( &mut self, location: Point<Px>, device_id: DeviceId, button: MouseButton, context: &mut EventContext<'_>, ) { ... } fn mouse_up( &mut self, location: Option<Point<Px>>, device_id: DeviceId, button: MouseButton, context: &mut EventContext<'_>, ) { ... } fn keyboard_input( &mut self, device_id: DeviceId, input: KeyEvent, is_synthetic: bool, context: &mut EventContext<'_>, ) -> EventHandling { ... } fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling { ... } fn mouse_wheel( &mut self, device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, context: &mut EventContext<'_>, ) -> EventHandling { ... } fn root_behavior( &mut self, context: &mut EventContext<'_>, ) -> Option<(RootBehavior, WidgetInstance)> { ... }
}
Expand description

A type that makes up a graphical user interface.

This type can go by many names in other UI frameworks: View, Component, Control.

§Widgets are hierarchical

Cushy’s widgets are organized in a hierarchical structure: widgets can contain other widgets. A window in Cushy contains a single root widget, which may contain one or more additional widgets.

§How Widgets are created

Cushy offers several approaches to creating widgets. The primary trait that is used to instantiate a widget is MakeWidget. This trait is automatically implemented for all types that implement Widget.

MakeWidget::make_widget is responsible for returning a WidgetInstance. This is a wrapper for a type that implements Widget that can be used without knowing the original type of the Widget.

While all MakeWidget is automatically implemented for all Widget types, it can also be implemented by types that do not implement Widget. This is a useful strategy when designing reusable widgets that are able to be completely represented by composing existing widgets. The ProgressBar type uses this strategy, as it uses either a Spinner or a Slider to show its progress.

One last convenience trait is provided to help create widgets that contain exactly one child: WrapperWidget. WrapperWidget exposes most of the same functions, but provides purpose-built functions for tweaking child’s layout and rendering behavior to minimize the amount of redundant code between these types of widgets.

§Identifying Widgets

Once a widget has been instantiated as a WidgetInstance, it will be assigned a unique WidgetId. Sometimes, it may be helpful to pre-create a WidgetId before the widget has been created. For these situations, WidgetTag allows creating a tag that can be passed to MakeWidgetWithTag::make_with_tag to set the returned WidgetInstance’s id.

§How to “talk” to another widget

Once a widget has been wrapped inside of a WidgetInstance, it is no longer possible to invoke Widget/s functions directly. Instead, a context must be created for that widget. In each of the Widget functions, a context is provided that represents the current widget. Each context type has a for_other() function that accepts any widget type: a WidgetId, a WidgetInstance, a MountedWidget, or a WidgetRef. The returned context will represent the associate widget, allowing access to the exposed APIs through the context.

While WidgetInstance::lock can be used to gain access to the underlying Widget type, this behavior should only be reserved for limited situations. It should be preferred to pass data between widgets using Dynamics or style components if possible. This ensures that your code can work with as many other widgets as possible, instead of restricting features to a specific set of types.

§How layout and rendering works

When a window is rendered, the root widget has its layout() function called with both constraints specifying ConstraintLimit::SizeToFit with the window’s inner size. The root widget measures its content to try to fit within the specified constraints, and returns its calculated size. If a widget has children, it can invoke LayoutContext::layout() on a context for each of its children to determine their required sizes.

Next, the window sets the root’s layout. When a widget contains another widget, it must call LayoutContext::set_child_layout for the child to be able to be rendered. This tells Cushy the location to draw the widget. While it is possible to provide any rectangle, Cushy clips all widgets and their children so that they cannot draw outside of their assigned bounds.

Once the layout has been determined, the window will invoke the root widget’s redraw() function. If a widget contains one or more children, it needs to invoke GraphicsContext::redraw() on a context for each of its children during its own render function. This allows full control over the order of drawing calls, allowing widgets to draw behind, in-between, or in front of their children.

The last responsibility the window has each frame is size adjustment. The window will potentially adjust its size automatically based on the root widget’s root_behavior().

§Controlling Invalidation and Redrawing

Cushy only redraws window contents when requested by the operating system or a tracked Dynamic is updated. Similarly, Cushy caches the known layout sizes and locations for widgets unless they are invalidated. Invalidation is done automatically when the window size changes or a tracked Dynamic is updated.

These systems require Cushy to track which Dynamic values a widget depends on for redrawing and invalidation. During a widget’s redraw and layout functions, it needs to ensure that all depended upon Dynamics are tracked using one of the various *_tracking_redraw()/*_tracking_invalidate() functions. For example, Source::get_tracking_redraw() and Source::get_tracking_invalidate().

§Hover State: Hit Testing

Before any cursor-related events are sent to a widget, the cursor’s position is tested with Widget::hit_test. When a widget returns true for a position, it is eligible to receive events such as mouse buttons.

When a widget returns false, it will not receive any cursor related events with one exception: hover events. Hover events will fire for widgets whose children are currently being hovered, regardless of whether Widget::hit_test returned true.

The provided Widget::hit_test implementation returns false.

As the cursor moves across the window, the window will look at the render information to see what widgets are positioned under the cursor and the order in which they were drawn. Beginning at the topmost widget, Widget::hit_test is called on each widget.

The currently hovered widget state is tracked for events that target widgets beneath the current cursor.

§Mouse Button Events

When a window receives an event for a mouse button being pressed, it calls the hovered widget’s mouse_down() function. If the function returns HANDLED/ControlFlow::Break, the widget becomes the tracking widget for that mouse button.

If the widget returns IGNORED/ControlFlow::Continue, the window will call the parent’s mouse_down() function. This repeats until the root widget is reached or a widget returns HANDLED.

Once a tracking widget is found, any cursor-related movements will cause Widget::mouse_drag() to be called. Upon the mouse button being released, the tracking widget’s mouse_up() function will be called.

§User Input Focus

A window can have a widget be focused for user input. For example, a text Input only responds to keyboard input once user input focus has been directed at the widget. This state is generally represented by drawing the theme’s highlight color around the border of the widget. GraphicsContext::draw_focus_ring can be used to draw the standard focus ring for rectangular-shaped widgets.

The most direct way to give a widget focus is to call WidgetContext::focus. However, not all widgets can accept focus. If a widget returns true from its accept_focus() function, focus will be given to it and its focus() function will be invoked.

If a widget returns false from its accept_focus() function, the window will perform these steps:

  1. If the widget has any children, sort its children visually and attempt to focus each one until a widget accepts focus. If any of these children have children, those children should also be checked.
  2. The widget asks its parent to find the next focus after itself. The parent finds the current widget in that list and attempts to focus each widget after the current widget in the visual order.
  3. This repeats until the root widget is reached, at which point focus is attempted using this algorithm until either a focused widget is found or the original widget is reached again. If no widget can be found in a full cycle of the widget tree, focus will be cleared.

When a window first opens, it call focus() on the root widget’s context.

§Losing Focus

A Widget can deny the ability for focus to be taken away from it by returning false from Widget::allow_blur(). In general, widgets should not do this. However, some user interfaces are designed to always keep focus on a single widget, and this feature enables that functionality.

When a widget currently has focused and loses it, its blur() function will be invoked.

§Styling

Cushy allows widgets to receive styling information through the widget hierarchy using Styles. Cushy calculates the effectives styles for each widget by inheriting all inheritable styles from its parent.

The Style widget allows assigining Styles to all of its children widget. It works by calling WidgetContext::attach_styles, and Cushy takes care of the rest.

Styling in Cushy aims to be simple, easy-to-understand, and extensible.

§Color Themes

Cushy aims to make it easy for developers to customize the appearance of its applications. The way color themes work in Cushy begins with the ColorScheme. A color scheme is a set of ColorSource that are used to generate a variety of shades of colors for various roles color plays in a user interface. In a way, coloring Cushy apps is a bit like paint-by-number, where the number is the name of the color role.

A ColorScheme can be used to create a ThemePair, which is theme definition that a theme for light and dark mode.

In the repository, the theme example is a good way to explore how the color system works in Cushy.

Required Methods§

source

fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>)

Redraw the contents of this widget.

Provided Methods§

source

fn summarize(&self, f: &mut Formatter<'_>) -> Result

Writes a summary of this widget into fmt.

The default implementation calls Debug::fmt. This function allows widget authors to print only publicly relevant information that will appear when debug formatting a WidgetInstance.

source

fn full_control_redraw(&self) -> bool

Returns true if this widget handles all built-in style components that apply.

These components are:

source

fn layout( &mut self, available_space: Size<ConstraintLimit>, context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size<UPx>

Layout this widget and returns the ideal size based on its contents and the available_space.

source

fn mounted(&mut self, context: &mut EventContext<'_>)

The widget has been mounted into a parent widget.

Widgets that contain MountedWidget references should call MountedWidget::remount_if_needed in this function.

source

fn unmounted(&mut self, context: &mut EventContext<'_>)

The widget has been removed from its parent widget.

source

fn hit_test( &mut self, location: Point<Px>, context: &mut EventContext<'_>, ) -> bool

Returns true if this widget should respond to mouse input at location.

This function is critical for how event propagation works for these functions:

See Hover State: Hit Testing for an explanation of how these events work together.

source

fn hover( &mut self, location: Point<Px>, context: &mut EventContext<'_>, ) -> Option<CursorIcon>

The widget is currently has a cursor hovering it at location.

This function will not be invoked if Self::hit_test returns false. See Hover State: Hit Testing for more information on how hover state is handled in Cushy.

source

fn unhover(&mut self, context: &mut EventContext<'_>)

The widget is no longer being hovered.

This function will only be invoked after Self::hover.

source

fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool

This widget has been targeted to be focused. If this function returns true, the widget will be focused. If false, Cushy will continue searching for another focus target.

source

fn focus(&mut self, context: &mut EventContext<'_>)

The widget has received focus for user input.

source

fn advance_focus( &mut self, direction: VisualOrder, context: &mut EventContext<'_>, ) -> EventHandling

The widget should switch to the next focusable area within this widget, honoring direction in a consistent manner. Returning HANDLED will cause the search for the next focus widget stop.

source

fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool

The widget is about to lose focus. Returning true allows the focus to switch away from this widget.

source

fn blur(&mut self, context: &mut EventContext<'_>)

The widget is no longer focused for user input.

source

fn activate(&mut self, context: &mut EventContext<'_>)

The widget has become the active widget.

source

fn deactivate(&mut self, context: &mut EventContext<'_>)

The widget is no longer active.

source

fn mouse_down( &mut self, location: Point<Px>, device_id: DeviceId, button: MouseButton, context: &mut EventContext<'_>, ) -> EventHandling

A mouse button event has occurred at location. Returns whether the event has been handled or not.

If an event is handled, the widget will receive callbacks for mouse_drag and mouse_up. See Mouse Button Events for more information on how mouse events work in Cushy.

This function will only be invoked if it or a child is the currently hovered widget. See Hover State: Hit Testing for more information on how hover state is handled in Cushy.

source

fn mouse_drag( &mut self, location: Point<Px>, device_id: DeviceId, button: MouseButton, context: &mut EventContext<'_>, )

A mouse button is being held down as the cursor is moved across the widget.

This function will only be invoked if Self::mouse_down returns HANDLED. See Mouse Button Events for more information on how mouse events work in Cushy.

source

fn mouse_up( &mut self, location: Option<Point<Px>>, device_id: DeviceId, button: MouseButton, context: &mut EventContext<'_>, )

A mouse button is no longer being pressed.

This function will only be invoked if Self::mouse_down returns HANDLED. See Mouse Button Events for more information on how mouse events work in Cushy.

source

fn keyboard_input( &mut self, device_id: DeviceId, input: KeyEvent, is_synthetic: bool, context: &mut EventContext<'_>, ) -> EventHandling

A keyboard event has been sent to this widget. Returns whether the event has been handled or not.

source

fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling

An input manager event has been sent to this widget. Returns whether the event has been handled or not.

source

fn mouse_wheel( &mut self, device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, context: &mut EventContext<'_>, ) -> EventHandling

A mouse wheel event has been sent to this widget. Returns whether the event has been handled or not.

This function will only be invoked if it or a child is the currently hovered widget. See Hover State: Hit Testing for more information on how hover state is handled in Cushy.

source

fn root_behavior( &mut self, context: &mut EventContext<'_>, ) -> Option<(RootBehavior, WidgetInstance)>

Returns a reference to a single child widget if this widget is a widget that primarily wraps a single other widget to customize its behavior.

Implementors§

source§

impl Widget for Button

source§

impl Widget for ColorSourcePicker

source§

impl Widget for Container

source§

impl Widget for Delimiter

source§

impl Widget for Image

source§

impl Widget for Layers

source§

impl Widget for OverlayLayer

source§

impl Widget for Spinner

source§

impl Widget for Scroll

source§

impl Widget for ScrollBar

source§

impl Widget for Stack

source§

impl Widget for Canvas

source§

impl Widget for Space

source§

impl Widget for VirtualList

source§

impl Widget for Wrap

source§

impl<Component> Widget for ComponentPicker<Component>
where Component: ColorComponent,

source§

impl<Layers> Widget for TileMap<Layers>
where Layers: Layers,

source§

impl<Storage> Widget for Input<Storage>
where Storage: InputStorage + Debug,

source§

impl<T> Widget for Indicator<T>

source§

impl<T> Widget for Label<T>
where T: Debug + DynamicDisplay + Send + 'static,

source§

impl<T> Widget for Slider<T>
where T: SliderValue,

source§

impl<T> Widget for T
where T: WrapperWidget,

source§

impl<const COLUMNS: usize> Widget for Grid<COLUMNS>