[−][src]Module kas::macros
Library macros
This module provides three important macros:
derive(Widget)
implements theWidget
trait (including super-traits likeCore
and optionallyHandler
); this exists partly as a convenience, but mostly because details of theWidget
trait family are not part of the public API, preventing manual implementation.make_widget
is a convenience macro to create a single instance of a custom widget typederive(NoResponse)
is a convenience macro to implementFrom<NoResponse>
for the deriving enum
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(unrestricted_attribute_tokens)] #![feature(proc_macro_hygiene)]
The derive(Widget)
macro
The Widget
trait requires multiple base traits to be implemented:
Core
and Layout
. These base traits should be considered
implementation details and not used directly; this macro therefore
implements both base traits and Widget
directly.
Additionally, widgets are usually required to implement the Handler
trait. If (and only if) the deriving struct is marked with a
#[handler]
attribute, the Handler
trait will also be implemented.
Note that it is also possible to implement this trait manually.
Type attributes
This derive
attribute may only be used on structs. This struct must have
a #[widget]
attribute and may have a #[handler]
attribute, as follows.
#[widget(class = Class::X, ...)]
#[handler(response = ...)]
#[derive(Clone, Debug, Widget)]
struct MyWidget {
...
}
The #[widget]
attribute on the struct supports the following arguments:
class = ...
(required) — an expression yielding the widget'sClass
layout = ...
(optional) — see below
Widgets with no children or only a single child do not need to specify the
layout
(or may specify single
), but those with multiple children must
specify this arguments, as follows:
single
— single child onlyhorizontal
— widgets are laid out in a row from left to right in the order specifiedvertical
— same except top-to-bottomgrid
— widgets are placed on a grid, with position specified by the per-field#[widget]
attribute
If there is a #[handler]
attribute on the struct, then the Handler
trait will be implemented. This attribute expects the following arguments:
response = ...
— theHandler::Response
associated typegenerics = < X, Y, ... > where CONDS
— see below
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 must be marked with #[core]
and implement the Core
trait; usually this field has the specification #[core] core: CoreData
.
A #[widget]
attribute is used to denote fields as child widgets. This
attribute accepts the following optional arguments, for use with grid
layouts and for handlers:
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, tk: &TkWidget, msg: M) -> R
.
Examples
A short example, without an implementation for Handler
(which could
still be implemented separately):
#![feature(unrestricted_attribute_tokens)] use kas::{Widget, Class, CoreData}; use kas::macros::Widget; #[widget(class = Class::Window, layout = single)] #[derive(Debug, Widget)] struct MyWidget<W: Widget> { #[core] core: CoreData, #[widget] child: W, }
A longer example, including derivation of the Handler
trait:
#![feature(unrestricted_attribute_tokens)] use kas::{Widget, Class, CoreData, TkWidget}; use kas::event::{Handler, err_unhandled}; use kas::macros::{Widget, NoResponse}; #[derive(Debug, NoResponse)] enum ChildMessage { None } #[derive(NoResponse)] enum MyMessage { None } #[widget(class = Class::Container)] #[handler(response = MyMessage, generics = <> where W: Handler<Response = ChildMessage>)] #[derive(Debug, Widget)] struct MyWidget<W: Widget> { #[core] core: CoreData, #[widget(handler = handler)] child: W, } impl<W: Widget> MyWidget<W> { fn handler(&mut self, tk: &TkWidget, msg: ChildMessage) -> MyMessage { // handle msg and respond somehow err_unhandled(msg) } }
The make_widget
macro
This macro supports widgets of the following classes:
- Container
- Frame
This exists purely to save you some typing. You could instead make your own
struct, derive Widget
(with attributes to enable Core, Layout and Widget
implementation), manually implement event::Handler
, and instantiate an
object.
Syntax should match the following Backus-Naur Form:
<input> ::= <class> "=>" <response> ";" <fields> ";" <funcs>
<class> ::= "container" "(" <layout> ")" | "frame"
<layout> ::= "single" | "horizontal" | "vertical" | "grid"
<response> ::= <type>
<fields> ::= "" | <field> | <field> "," <fields>
<field> ::= <w_attr> <opt_ident> <field_ty> = <expr>
<opt_ident> ::= "_" | <ident>
<field_ty> ::= "" | ":" <type> | ":" impl <bound> | "->" <type> | ":" impl <bound> "->" <type>
<w_attr> ::= "" | "#" "[" <widget> <w_params> "]"
<w_params> ::= "" | "(" <w_args> ")"
<w_args> ::= <w_arg> | <w_arg> "," <w_args>
<w_arg> ::= <pos_arg> "=" <lit> | "handler" = <ident>
<pos_arg> ::= "col" | "row" | "cspan" | "rspan"
<funcs> ::= "" | <func> <funcs>
where <type>
is a type expression, <expr>
is a (value) expression,
<ident>
is an identifier, <lit>
is a literal, <path>
is a path,
<bound>
is a trait object bound, and
<func>
is a Rust method definition. ""
is the empty string (i.e. nothing).
The effect of this macro is to create an anonymous struct with the above
fields (plus an implicit core
), implement Core
, Layout
, Widget
and Handler
(with the specified <response>
type), implement the
additional <funcs>
listed on this type, then construct and return an
instance using the given value expressions to initialise each field.
Each field is considered a child widget if the #[widget]
attribute is
present, or a simple data field otherwise. The specification of this
attribute is identical to that used when deriving Widget
.
The layout
specifier should be self-explanatory, with the exception of
grid
, where each widget's position must be specified via attribute
arguments (e.g. #[widget(col=1, row=2)]
). The col
and row
parameters
both default to 0, while cspan
and rspan
(column and row spans) both
default to 1.
Fields may have an identifier or may be anonymous (via usage of _
). This
is often convenient for child widgets which don't need to be referred to.
Fields may have an explicit type (ident : type = ...
), or the type may be
skipped, or (for widgets only) just the response type can be specified via
ident -> type = ...
. Note that some type specification is usually needed
when referring to the field later.
Optionally, a message handler may be specified for child widgets via
#[widget(handler = f)] ident = value
where f
is a method defined on the
anonymous struct with signature fn f(&mut self, tk: &TkWidget, msg: M) -> R
where M
is the type of response received from the child widget, and R
is
the type of response sent from this widget.
Example
#![feature(proc_macro_hygiene)] #![feature(unrestricted_attribute_tokens)] use kas::control::TextButton; use kas::macros::{NoResponse, make_widget}; #[derive(NoResponse)] enum OkCancel { None, Ok, Cancel, } let button_box = make_widget!{ container(horizontal) => OkCancel; struct { #[widget] _ = TextButton::new("Ok", || OkCancel::Ok), #[widget] _ = TextButton::new("Cancel", || OkCancel::Cancel), } };
The derive(NoResponse)
macro
This macro implements From<NoResponse>
for the given type.
It assumes that the type is an enum with a simple variant named None
.
Example
use kas::macros::NoResponse; #[derive(NoResponse)] enum MyMessage { None, A, B };
Re-exports
pub use kas_macros::NoResponse; |
pub use kas_macros::Widget; |
pub use kas_macros::make_widget; |