Attribute Macro kas::widget

#[widget]
Expand description

Attribute to implement the kas::Widget family of traits

This may only be used within the impl_scope! macro.

Assists implementation of the Widget, Events and Layout traits. Implementations of these traits are generated if missing or augmented with missing method implementations.

This macro may inject methods into existing Layout / Events / Widget implementations. This is used both to provide default implementations which could not be written on the trait and to implement properties like navigable. (In the case of multiple implementations of the same trait, as used for specialization, only the first implementation of each trait is extended.)

Syntax

WidgetAttr :
   # [ widget WidgetAttrArgs? ]

WidgetAttrArgs :
   { (WidgetAttrArg ;) * }

Supported arguments (WidgetAttrArg) are:

  • Data = Type: the Widget::Data associated type
  • derive = self.field where field is the name (or number) of a field: enables “derive mode” (see below) over the given field
  • navigable = bool — a quick implementation of Events::navigable: whether this widget supports keyboard focus via the Tab key (default is false)
  • hover_highlight = bool — if true, generate Events::handle_hover to request a redraw on focus gained/lost
  • cursor_icon = expr — if used, generate Event::handle_hover, calling cx.set_hover_cursor(expr)
  • layout = layout — defines widget layout via an expression; see below for documentation

The struct must contain a field of type widget_core!() (usually named core). The macro widget_core!() is a placeholder, expanded by #[widget] and used to identify the field used (any name may be used). This field might have type CoreData or might use a special generated type; either way it has fields id: Id (assigned by during configure) and rect: Rect (usually assigned by Layout::set_rect). It may contain additional fields for layout data. The type supports Default and Clone (although Clone actually default-initializes all fields other than rect since clones of widgets must themselves be configured).

Assuming the deriving type is a struct or tuple struct, fields support the following attributes:

  • #[widget]: marks the field as a Widget to be configured, enumerated by Widget::get_child and included by glob layouts
  • #[widget(expr)]: the same, but maps the data reference type; expr is an expression returning a reference to the child widget’s input data; available inputs are self, data (own input data) and index (of the child).

Layout

Widget layout may be specified either by implementing the Layout trait or via the layout property of #[widget]. The latter accepts the following syntax, where Layout is any of the below.

Using the layout = ...; property will also generate a corresponding implementation of Events::nav_next, with a couple of exceptions (where macro-time analysis is insufficient to implement this method).

Column, Row, List, AlignedColumn, AlignedRow, Grid, Float, Align, Pack, Margins :
   These stand-alone macros are explicitly supported in this position.
   Optionally, a Storage specifier is supported immediately after the macro name, e.g.
   column! 'storage_name ["one", "two"]

Single :
   self . Member
   A named child: self.foo (more precisely, this matches any expression starting self, and uses &mut (#expr))

Slice :
   slice! Storage? ( Direction , self . Member )
   A field with type [W] for some W: Layout. (Note: this does not automatically register the slice widgets as children for the purpose of configuration and event-handling. An explicit implementation of Widget::get_child will be required.)

Frame :
   frame! Storage? ( Layout ( , style = Expr )? )
   Adds a frame of type Expr around content, defaulting to FrameStyle::Frame.

Button :
   button! Storage? ( Layout ( , color = Expr )? )
   Adds a button frame (optionally with color Expr) around content.

WidgetConstructor :
   Expr
   An expression yielding a widget, e.g. Label::new("Hello world"). The result must be an object of some type W: Widget.

LabelLit :
   StrLit
   A string literal generates a label widget, e.g. “Hello world”. This is an internal type without text wrapping.

NonNavigable :
   non_navigable! ( Layout )
   Does not affect layout. Specifies that the content is excluded from tab-navigation order.

Additional syntax rules (not layout items):

Member :
   Ident | Index
   The name of a struct field or an index into a tuple struct.

Direction :
   left | right | up | down | Expr:
   Note that an Expr must start with self

Storage :
   ' Ident
   Used to explicitly name the storage used by a generated widget or layout; for example row 'x: ["A", "B", "C"] will add a field x: R where R: RowStorage within the generated widget_core!(). If omitted, the field name will be anonymous (generated).

Examples

A simple example is the Frame widget:

impl_scope! {
    /// A frame around content
    #[autoimpl(Deref, DerefMut using self.inner)]
    #[autoimpl(class_traits using self.inner where W: trait)]
    #[derive(Clone, Default)]
    #[widget{
        layout = frame!(self.inner, style = kas::theme::FrameStyle::Frame);
    }]
    pub struct Frame<W: Widget> {
        core: widget_core!(),
        #[widget]
        pub inner: W,
    }

    impl Self {
        /// Construct a frame
        #[inline]
        pub fn new(inner: W) -> Self {
            Frame {
                core: Default::default(),
                inner,
            }
        }
    }
}

A simple row layout: layout = row! [self.a, self.b];

Derive

It is possible to derive from a field which is itself a widget, e.g.:

impl_scope! {
    #[autoimpl(Deref, DerefMut using self.0)]
    #[derive(Clone, Default)]
    #[widget{ derive = self.0; }]
    pub struct ScrollBarRegion<W: Widget>(ScrollBars<ScrollRegion<W>>);
}

This is a special mode where most features of #[widget] are not available. Only Layout methods may be specified (overriding those from the derived widget); everything else is derived.

Debugging

To inspect the output of this macro, set the environment variable KAS_DEBUG_WIDGET to the name of the widget concerned, dump the output to a temporary file and format. For example:

KAS_DEBUG_WIDGET=Border cargo build > temp.rs
rustfmt temp.rs