[−][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, #[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
- (optional):
area=FIELD
whereFIELD
is a child widget; if specified, the area of self is considered to refer to childFIELD
. This causes thekas::Layout::find_id
function to directly return the child's Id.
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
for non-single layouts requires a data storage
field as follows; for the single
layout this field is optional:
#[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()
.
(Note: the LayoutData
trait is also implemented by this macro.)
Handler
If one or more #[handler]
attributes are present, then the Handler
trait is implemented (potentially multiple times with different
substitutions of generic parameters).
This attribute accepts the following arguments:
- (optional)
msg = TYPE
— theHandler::Msg
associated type; if not specified, this type defaults tokas::event::VoidMsg
- (optional)
substitutions = TUPLE
— a tuple of subsitutions for type generics, for example:(T1 = MyType, T2 = some::other::Type)
- (optional):
generics = < X, Y, ... > where CONDS
(where CONDS
is optional, and if present must be the last argument)
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.
If you need to substitute generic parameters on the struct with concrete
types for the Handler
implementation, use the substitutions
argument.
Be aware that this behaviour is a hack which only supports type generics
on parameters without where clause constraints.
(Note that ideally we would use equality constraints in where
predicates
instead of adding special parameter substitution support, but type equality
constraints are not supported by Rust yet: #20041.)
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 several parameters affecting both layout
and event-handling. All are optional.
The first four affect positioning are only used by 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)
These two affect alignment in the case that a widget finds itself within a
cell larger than its ideal size. Application of alignment is determined by
the child widget's implementation of Layout::set_rect
, which may simply
ignore these alignment hints.
halign = ...
— one ofbegin
,centre
,end
,stretch
valign = ...
— one ofbegin
,centre
,end
,stretch
Finally, a parent widget may handle event-responses from a child widget
(see Handler
). The parent widget should implement a utility method
with signautre fn f(&mut self, mgr: &mut Manager, msg: M) -> R
where
M
is the type Handler::Msg
in the child widget's implementation,
then reference this method:
handler = f
— the namef
of a utility method defined on this type
If there is no handler
parameter, the child widget's Handler::Msg
type
should convert into the parent's Handler::Msg
type via From
.
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 |