awesome_glib/lib.rs
1extern crate proc_macro;
2
3mod actions;
4
5use proc_macro::TokenStream;
6use syn::{parse_macro_input, ItemImpl};
7
8use crate::actions::ActionImplAttributes;
9
10/// Macro for creating [`gio::Action`]s and registering them in a given
11/// [`gio::ActionMap`]. It generates a method `register_actions` with
12/// a following signature:
13///
14/// ```rust
15/// # struct X; impl X {
16/// fn register_actions<AM: glib::object::IsA<gio::ActionMap>>(&self, map: &AM)
17/// # {} }
18/// ```
19///
20/// ## Name of an action
21///
22/// Name of an action is the same as the name of a function. This can be
23/// changed by an annotation.
24///
25/// ```rust
26/// # use gio::prelude::ActionMapExt;
27/// # #[derive(glib::Downgrade)]
28/// # pub struct X;
29/// # #[awesome_glib::actions]
30/// # impl X {
31/// // Name of the action is "play".
32/// fn play(&self) {
33/// }
34///
35/// // Name of the action is "pause".
36/// #[action(name = "pause")]
37/// fn pause_handler(&self) {
38/// }
39/// # }
40/// ```
41///
42/// ## Stateful action
43///
44/// An action may have a state. A handler need to be annotated accordingly.
45/// A handler must have a following signature:
46///
47/// ```rust,ignore
48/// #[action(stateful, initial_state = <initial-value>)]
49/// fn action_handler(&self, state: StateType) -> Option<StateType>
50/// ```
51///
52/// `StateType` must implement [`glib::variant::StaticVariantType`]. Returning
53/// `Some(value)` from such handler triggers a change of an action's state.
54///
55/// An `initial_state` annotation may be omited. In this case `StateType` must
56/// implement [`std::default::Default`], which is used to initialize an action.
57///
58/// ## Parameter
59///
60/// An action may have a parameter. In this case a handler need to have one of
61/// the following signatures:
62///
63/// ```rust,ignore
64/// // Stateless action
65/// fn action_handler(&self, parameter: ParameterType)
66///
67/// // Stateful action
68/// #[action(stateful)]
69/// fn action_handler(&self, state: StateType, parameter: ParameterType) -> Option<StateType>
70/// ```
71///
72/// `ParameterType` must also implement [`glib::variant::StaticVariantType`].
73///
74/// ## Change state handler
75///
76/// A handler may be annotated as `change_state`. In this case it will be bound
77/// to a `change-state` signal of an action (in opposite to an `activate`
78/// signal used by default). In this case a signature should look like this:
79///
80/// ```rust,ignore
81/// // Stateful action
82/// #[action(stateful, change_state)]
83/// fn change_state_action_handler(&self, state: StateType) -> Option<StateType>
84/// ```
85///
86/// If you need an action with both `activate` and `change-state` handlers, just
87/// create two methods and annotate them with the same action name.
88///
89/// ## Defaults
90///
91/// A `stateful` annotation may be omited if `initial_state` or `change_state` is
92/// specified.
93///
94/// # Example
95/// ```rust
96/// use gio::prelude::*;
97///
98/// #[derive(glib::Downgrade)]
99/// pub struct MyApplication(gio::Application);
100///
101/// impl MyApplication {
102/// pub fn new() -> Self {
103/// let app = Self(gio::Application::new(
104/// Some("com.example.MyApplication"),
105/// gio::ApplicationFlags::FLAGS_NONE,
106/// ));
107/// app.register_actions(&app.0);
108/// app
109/// }
110/// }
111///
112/// #[awesome_glib::actions]
113/// impl MyApplication {
114/// fn action1(&self) {
115/// // handle "action1"
116/// }
117///
118/// // Explicitely specify action name
119/// #[action(name = "action_second")]
120/// fn action2(&self) {
121/// // handle "action_second"
122/// }
123///
124/// // Action with a parameter
125/// fn action3(&self, param: String) {
126/// // handle "action3"
127/// }
128///
129/// // Stateful action with a specified initial state
130/// #[action(stateful, initial_state = false)]
131/// fn stateful_toggle(&self, state: bool) -> Option<bool> {
132/// // handle action
133/// Some(!state) // return new state
134/// }
135///
136/// // Stateful action with a default initial state (`false`)
137/// #[action(stateful)]
138/// fn stateful_toggle_default(&self, state: bool) -> Option<bool> {
139/// Some(!state) // return new state
140/// }
141///
142/// // Stateful action with a state of `String` type
143/// #[action(stateful, initial_state = "")]
144/// fn stateful_text(&self, state: String) -> Option<String> {
145/// if state.len() >= 10 {
146/// None // do not change state
147/// } else {
148/// Some(state + "!") // change state
149/// }
150/// }
151///
152/// // Stateful action with a `String` parameter
153/// #[action(stateful, initial_state = true)]
154/// fn stateful_toggle_with_parameter(&self, state: bool, param: String) -> Option<bool> {
155/// // Do not change the state of the action
156/// None
157/// }
158///
159/// // Stateful action with a handler for a `change-state` signal
160/// #[action(stateful, initial_state = 0.0_f64, change_state)]
161/// fn volume(&self, value: f64) -> Option<f64> {
162/// if value >= 0.0 && value <= 10.0 {
163/// // accept new state
164/// Some(value)
165/// } else {
166/// // reject
167/// None
168/// }
169/// }
170///
171/// // Stateful action with a handler for a `change-state` signal and a parameter of the same type.
172/// // `change_state` also implies `stateful`, so it may be omitted.
173/// #[action(initial_state = true, change_state)]
174/// fn pause(&self, paused: bool) -> Option<bool> {
175/// Some(paused)
176/// }
177///
178/// // Stateful action with a handler for a `change-state` signal and without a parameter.
179/// #[action(change_state, initial_state = true, no_parameter)]
180/// fn pause_no_param(&self, paused: bool) -> Option<bool> {
181/// Some(paused)
182/// }
183/// }
184/// ```
185///
186/// [`gio::Action`]: struct.Action.html
187/// [`gio::ActionMap`]: struct.ActionMap.html
188/// [`glib::variant::StaticVariantType`]: ../glib/variant/trait.StaticVariantType.html
189#[proc_macro_attribute]
190pub fn actions(args: TokenStream, item: TokenStream) -> TokenStream {
191 let input = parse_macro_input!(item as ItemImpl);
192
193 let mut action_args = ActionImplAttributes::default();
194 if !args.is_empty() {
195 let action_args_parser = syn::meta::parser(|meta| action_args.parse(meta));
196 parse_macro_input!(args with action_args_parser);
197 }
198
199 actions::actions(action_args, input)
200 .unwrap_or_else(syn::Error::into_compile_error)
201 .into()
202}