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}