[−][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:
derive(Widget)
implements theWidgetCore
trait and optionally alsoLayout
,Widget
andHandler
make_widget
is a convenience macro to create a single instance of a custom widget typederive(VoidMsg)
is a convenience macro to implementFrom<VoidMsg>
for the deriving type
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 marginvertical
— child widgets are arranged in a vertical column, in order of child fieldshorizontal
— child widgets are arranged in a horizontal row, in order of child fieldsgrid
— 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 = ...
— theHandler::Msg
associated type; if not specified, this type defaults tokas::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 (typeM
) and converts it to the appropriate response type for this widget (R
); this method should have signaturefn 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:
#[derive(Clone, Debug, kas::macros::Widget)]
is implied#[handler(msg = ..)]
is required and most only have anmsg
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 |
Widget | Macro to derive widget traits |