[][src]Module kas::macros

Library macros

This documentation is provided as a reference. The macros may be easier to understand from the example apps provided with kas-wgpu.

This module provides two important macros:

Note that these macros are defined in the external crate, kas-macros, only because procedural macros must be defined in a special crate. The kas-macros crate should not be used directly.

Note further that these macros require gated functionality only available in nightly rustc builds:

#![feature(proc_macro_hygiene)]

The derive(Widget) macro

The Widget trait requires the base traits WidgetCore and Layout be implemented; additionally, widgets should implement Handler. This macro can generate implementations for all of these traits or only for WidgetCore as required.

For parent widgets, the make_widget macro is even more concise.

Type attributes

This derive attribute may only be used on structs. Example:

use kas::macros::Widget;
use kas::event::{Handler, VoidMsg};
use kas::{CoreData, LayoutData, Widget};

#[widget]
#[layout(single)]
#[handler(generics = <> where W: Handler<Msg = VoidMsg>)]
#[derive(Clone, Debug, Widget)]
struct WrapperWidget<W: Widget> {
    #[core] core: CoreData,
    #[layout_data] layout_data: <Self as LayoutData>::Data,
    #[widget] child: W,
}

Widget

If the #[widget] attribute is present, the Widget trait is derived (with default method implementations).

Layout

If the #[layout(..)] attribute is present, the Layout trait is derived. Derivation is only supported for parent widgets.

The following attribute parameters are expected:

  • (first position): one of single, horizontal, vertical, grid
  • (optional): frame

Child widgets are arranged as specified by the first parameter:

  • single — the widget wraps a single child, with no border or margin
  • vertical — child widgets are arranged in a vertical column, in order of child fields
  • horizontal — child widgets are arranged in a horizontal row, in order of child fields
  • grid — child widgets are arranged in a grid; position is specified via parameters to the #[widget] attribute on child fields

If the frame parameter is given, a frame is drawn around child widgets.

Derivation of Layout requires data storage be provided by the widget as follows (the LayoutData implementation is implicitly derived):

#[layout_data] layout_data: <Self as kas::LayoutData>::Data,

This field is supports Default and Clone, thus may be constructed with layout_data: Default::default().

Handler

If the #[handler] attribute is present, then the Handler trait is derived. This attribute accepts the following arguments:

  • (optional) msg = ... — the Handler::Msg associated type; if not specified, this type defaults to kas::event::VoidMsg
  • (last position, optional): generics = < X, Y, ... > where CONDS

Commonly the Handler implementation requires extra bounds on generic types, and sometimes also additional type parameters; the generics argument allows this. This argument is optional and if present must be the last argument. Note that the generic types and bounds given are added to the generics defined on the struct itself.

Fields

One struct field with specification #[core] core: CoreData is required. When deriving layouts a #[layout_data] field is also required (see above).

Other fields may be child widgets or simply data fields. Those with a #[widget] attribute are interpreted as child widgets, affecting the implementation of derived WidgetCore, Layout and Handler methods.

The #[widget] attribute accepts the following parameters. All are optional, and the first four are only useful with the grid layout.

  • col = ... — grid column, from left (defaults to 0)
  • row = ... — grid row, from top (defaults to 0)
  • cspan = ... — number of columns to span (defaults to 1)
  • rspan = ... — number of rows to span (defaults to 1)
  • handler = ... — the name (f) of a method defined on this type which handles a message from the child (type M) and converts it to the appropriate response type for this widget (R); this method should have signature fn f(&mut self, mgr: &mut Manager, msg: M) -> R.

Examples

A simple example is included above. The example below includes multiple children and custom event handling.

use kas::event::{Handler, Manager, VoidResponse, VoidMsg};
use kas::macros::Widget;
use kas::widget::Label;
use kas::{CoreData, LayoutData, Widget};

#[derive(Debug)]
enum ChildMessage { A }

#[widget]
#[layout(vertical)]
#[handler(generics = <> where W: Handler<Msg = ChildMessage>)]
#[derive(Debug, Widget)]
struct MyWidget<W: Widget> {
    #[core] core: CoreData,
    #[layout_data] layout_data: <Self as LayoutData>::Data,
    #[widget] label: Label,
    #[widget(handler = handler)] child: W,
}

impl<W: Widget> MyWidget<W> {
    fn handler(&mut self, mgr: &mut Manager, msg: ChildMessage) -> VoidResponse {
        match msg {
            ChildMessage::A => { println!("handling ChildMessage::A"); }
        }
        VoidResponse::None
    }
}

The make_widget macro

This macro allows easy creation of "layout" widgets (those whose purpose is to house one or more child widgets) by introducing syntax for a struct literal and adding the additional fields and implementations required by all widgets.

Syntax is similar to a Rust type definition, but with most of the types and identifiers omitted. It's easiest to study an example:

#[derive(Clone, Copy, Debug)]
enum Item {
    Button,
    Check(bool),
}
let widget = make_widget! {
    #[widget]
    #[layout(vertical)]
    #[handler(msg = VoidMsg)]
    struct {
        #[widget] _ = Label::from("Widget Gallery"),
        #[widget(handler = activations)] _ = inner_widgets,
        last_item: Item = Item::Button,
    }
    impl {
        fn activations(&mut self, mgr: &mut Manager, item: Item)
            -> VoidResponse
        {
            match item {
                Item::Button => println!("Clicked!"),
                Item::Check(b) => println!("Checkbox: {}", b),
            };
            self.last_item = item;
            VoidResponse::None
        }
    }
};

Struct and fields

Starting from the middle, we have a struct definition, though two things are unusual here: (1) the type is anonymous (unnamed), and (2) fields are simultaneously given both type and value.

Field specifications can get more unusual too, since both the field name and the field type are optional. For example, all of the following are equivalent:

#[widget] l1: Label = Label::new("label 1"),
#[widget] _: Label = Label::new("label 2"),
#[widget] l3 = Label::new("label 3"),
#[widget] _ = Label::new("label 4"),

Omitting field names is fine, so long as you don't need to refer to them. Omitting types, however, comes at a small cost: Rust does not support fields of unspecified types, thus this must be emulated with generics. The macro deals with the necessary type arguments to implementations, however macro expansions (as sometimes seen in error messages) are ugly and, perhaps worst of all, the field will have opaque type (making methods and inner fields inaccessible). The latter can be partially remedied via trait bounds:

#[widget] display: impl HasText = EditBox::new("editable"),

Implementations

Now, back to the example above, we see attributes and an impl block:

let widget = make_widget! {
    #[widget]
    #[layout(vertical)]
    #[handler(msg = VoidMsg)]
    struct {
        ...
    }
    impl {
        fn on_tick(&mut self, mgr: &mut Manager) {
            ...
        }
    }
};

Attributes may be applied to the anonymous struct like normal, with two exceptions:

  1. #[derive(Clone, Debug, kas::macros::Widget)] is implied
  2. #[handler(msg = ..)] is required and most only have an msg parameter

impl blocks work like usual except that the struct name and type parameters are omitted. Traits may also be implemented this way:

impl Trait { ... }

Example

#![feature(proc_macro_hygiene)]

use kas::macros::{make_widget};
use kas::widget::TextButton;

#[derive(Copy, Clone, Debug)]
enum OkCancel {
    Ok,
    Cancel,
}

let button_box = make_widget!{
    #[widget]
    #[layout(horizontal)]
    #[handler(msg = OkCancel)]
    struct {
        #[widget] _ = TextButton::new("Ok", OkCancel::Ok),
        #[widget] _ = TextButton::new("Cancel", OkCancel::Cancel),
    }
};

The derive(VoidMsg) macro

This macro implements From<VoidMsg> for the given type (see VoidMsg).

Example

use kas::macros::VoidMsg;

#[derive(VoidMsg)]
enum MyMessage { A, B };

Macros

make_widget

Macro to create a widget with anonymous type

Derive Macros

VoidMsg

Macro to derive From<VoidMsg>

Widget

Macro to derive widget traits