1use gtk::gio;
4use gtk::prelude::{ActionExt, ActionMapExt, FromVariant, StaticVariantType, ToVariant, WidgetExt};
5
6use std::marker::PhantomData;
7
8pub mod traits;
10pub use traits::*;
11
12#[macro_export]
13macro_rules! new_action_group {
15 ($vis:vis $ty:ident, $name:expr) => {
16 $vis struct $ty;
17
18 impl relm4::actions::ActionGroupName for $ty {
19 const NAME: &'static str = $name;
20 }
21 };
22}
23
24#[macro_export]
25macro_rules! new_stateless_action {
27 ($vis:vis $ty:ident, $group:ty, $name:expr) => {
28 $vis struct $ty;
29
30 impl relm4::actions::ActionName for $ty {
31 type Group = $group;
32 type Target = ();
33 type State = ();
34
35 const NAME: &'static str = $name;
36 }
37 };
38}
39
40#[macro_export]
41macro_rules! new_stateful_action {
45 ($vis:vis $ty:ident, $group:ty, $name:expr, $value:ty, $state:ty) => {
46 $vis struct $ty;
47
48 impl relm4::actions::ActionName for $ty {
49 type Group = $group;
50 type Target = $value;
51 type State = $state;
52
53 const NAME: &'static str = $name;
54 }
55 };
56}
57
58pub struct RelmAction<Name: ActionName> {
60 name: PhantomData<Name>,
61 action: gio::SimpleAction,
62}
63
64impl<Name: ActionName> Clone for RelmAction<Name> {
65 fn clone(&self) -> Self {
66 Self {
67 name: self.name,
68 action: self.action.clone(),
69 }
70 }
71}
72
73impl<Name: ActionName> std::fmt::Debug for RelmAction<Name> {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 f.debug_struct("RelmAction")
76 .field("name", &self.name)
77 .field("action", &self.action)
78 .finish()
79 }
80}
81
82impl<Name: ActionName> From<RelmAction<Name>> for gio::SimpleAction {
83 fn from(value: RelmAction<Name>) -> Self {
84 value.action
85 }
86}
87
88impl<Name: ActionName> RelmAction<Name>
89where
90 Name::State: ToVariant + FromVariant,
91 Name::Target: ToVariant + FromVariant,
92{
93 pub fn new_stateful_with_target_value<
95 Callback: Fn(&gio::SimpleAction, &mut Name::State, Name::Target) + 'static,
96 >(
97 start_value: &Name::State,
98 callback: Callback,
99 ) -> Self {
100 let ty = Name::Target::static_variant_type();
101
102 let action =
103 gio::SimpleAction::new_stateful(Name::NAME, Some(&ty), &start_value.to_variant());
104
105 action.connect_activate(move |action, variant| {
106 let value = variant.unwrap().get().unwrap();
107 let mut state = action.state().unwrap().get().unwrap();
108
109 callback(action, &mut state, value);
110 action.set_state(&state.to_variant());
111 });
112
113 Self {
114 name: PhantomData,
115 action,
116 }
117 }
118}
119
120impl<Name: ActionName> RelmAction<Name>
121where
122 Name::State: ToVariant + FromVariant,
123 Name::Target: EmptyType,
124{
125 pub fn new_stateful<Callback: Fn(&gio::SimpleAction, &mut Name::State) + 'static>(
127 start_value: &Name::State,
128 callback: Callback,
129 ) -> Self {
130 let action = gio::SimpleAction::new_stateful(Name::NAME, None, &start_value.to_variant());
131
132 action.connect_activate(move |action, _variant| {
133 let mut state = action.state().unwrap().get().unwrap();
134 callback(action, &mut state);
135 action.set_state(&state.to_variant());
136 });
137
138 Self {
139 name: PhantomData,
140 action,
141 }
142 }
143}
144
145impl<Name: ActionName> RelmAction<Name>
146where
147 Name::State: EmptyType,
148 Name::Target: ToVariant + FromVariant,
149{
150 pub fn new_with_target_value<Callback: Fn(&gio::SimpleAction, Name::Target) + 'static>(
152 callback: Callback,
153 ) -> Self {
154 let ty = Name::Target::static_variant_type();
155
156 let action = gio::SimpleAction::new(Name::NAME, Some(&ty));
157
158 action.connect_activate(move |action, variant| {
159 let value = variant.unwrap().get().unwrap();
160 callback(action, value);
161 });
162
163 Self {
164 name: PhantomData,
165 action,
166 }
167 }
168}
169
170impl<Name: ActionName> RelmAction<Name>
171where
172 Name::Target: EmptyType,
173 Name::State: EmptyType,
174{
175 pub fn new_stateless<Callback: Fn(&gio::SimpleAction) + 'static>(callback: Callback) -> Self {
177 let action = gio::SimpleAction::new(Name::NAME, None);
178
179 action.connect_activate(move |action, _variant| {
180 callback(action);
181 });
182
183 Self {
184 name: PhantomData,
185 action,
186 }
187 }
188}
189
190impl<Name: ActionName> RelmAction<Name>
191where
192 Name::Target: ToVariant + FromVariant,
193{
194 pub fn to_menu_item_with_target_value(
196 label: &str,
197 target_value: &Name::Target,
198 ) -> gio::MenuItem {
199 let menu_item = gio::MenuItem::new(Some(label), Some(&Name::action_name()));
200 menu_item.set_action_and_target_value(
201 Some(&Name::action_name()),
202 Some(&target_value.to_variant()),
203 );
204
205 menu_item
206 }
207}
208
209impl<Name: ActionName> RelmAction<Name>
210where
211 Name::Target: EmptyType,
212{
213 #[must_use]
215 pub fn to_menu_item(label: &str) -> gio::MenuItem {
216 gio::MenuItem::new(Some(label), Some(&Name::action_name()))
217 }
218}
219
220impl<Name: ActionName> RelmAction<Name> {
221 pub fn set_enabled(&self, enabled: bool) {
225 self.action.set_enabled(enabled);
226 }
227
228 #[must_use]
233 pub fn gio_action(&self) -> &gio::SimpleAction {
234 &self.action
235 }
236}
237
238#[derive(Debug)]
239pub struct RelmActionGroup<GroupName: ActionGroupName> {
241 group_name: PhantomData<GroupName>,
242 actions: Vec<gio::SimpleAction>,
243}
244
245impl<GroupName: ActionGroupName> RelmActionGroup<GroupName> {
246 #[must_use]
248 pub fn new() -> Self {
249 Self::default()
250 }
251
252 pub fn add_action<Name: ActionName>(&mut self, action: RelmAction<Name>) {
254 self.actions.push(action.action);
255 }
256
257 pub fn register_for_main_application(self) {
259 let app = crate::main_application();
260 for action in self.actions {
261 app.add_action(&action);
262 }
263 }
264
265 pub fn register_for_widget<W>(self, widget: W)
267 where
268 W: AsRef<gtk::Widget>,
269 {
270 let group = self.into_action_group();
271 widget
272 .as_ref()
273 .insert_action_group(GroupName::NAME, Some(&group));
274 }
275
276 #[must_use]
278 pub fn into_action_group(self) -> gio::SimpleActionGroup {
279 let group = gio::SimpleActionGroup::new();
280 for action in self.actions {
281 group.add_action(&action);
282 }
283 group
284 }
285}
286
287impl<GroupName, A> FromIterator<A> for RelmActionGroup<GroupName>
288where
289 A: Into<gio::SimpleAction>,
290 GroupName: ActionGroupName,
291{
292 fn from_iter<I>(iter: I) -> Self
293 where
294 I: IntoIterator<Item = A>,
295 {
296 Self {
297 group_name: PhantomData,
298 actions: iter.into_iter().map(Into::into).collect(),
299 }
300 }
301}
302
303impl<GroupName: ActionGroupName> Default for RelmActionGroup<GroupName> {
304 fn default() -> Self {
305 Self {
306 group_name: PhantomData,
307 actions: Vec::new(),
308 }
309 }
310}