1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Take a look at the license at the top of the repository in the LICENSE file.

use glib::{prelude::*, Variant, VariantTy, VariantType};

use crate::{ActionMap, SimpleAction};

/// This struct defines a single action. It is for use with
/// [`ActionMapExtManual::add_action_entries()`][crate::prelude::ActionMapExtManual::add_action_entries()].
///
/// The order of the items in the structure are intended to reflect
/// frequency of use. It is permissible to use an incomplete initialiser
/// in order to leave some of the later values as [`None`]. All values
/// after `name` are optional. Additional optional fields may be added in
/// the future.
///
/// See [`ActionMapExtManual::add_action_entries()`][crate::prelude::ActionMapExtManual::add_action_entries()] for an example.
#[doc(alias = "GActionEntry")]
pub struct ActionEntry<O>
where
    O: IsA<ActionMap>,
{
    name: String,
    parameter_type: Option<VariantType>,
    state: Option<Variant>,
    #[allow(clippy::type_complexity)]
    pub(crate) activate: Option<Box<dyn Fn(&O, &SimpleAction, Option<&Variant>) + 'static>>,
    #[allow(clippy::type_complexity)]
    pub(crate) change_state: Option<Box<dyn Fn(&O, &SimpleAction, Option<&Variant>) + 'static>>,
}

impl<O> ActionEntry<O>
where
    O: IsA<ActionMap>,
{
    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn parameter_type(&self) -> Option<&VariantTy> {
        self.parameter_type.as_deref()
    }

    pub fn state(&self) -> Option<&Variant> {
        self.state.as_ref()
    }

    pub fn builder(name: &str) -> ActionEntryBuilder<O> {
        ActionEntryBuilder::new(name)
    }
}

impl<O> std::fmt::Debug for ActionEntry<O>
where
    O: IsA<ActionMap>,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ActionEntry")
            .field("name", &self.name())
            .field("parameter_type", &self.parameter_type())
            .field("state", &self.state())
            .finish()
    }
}

#[derive(Debug)]
pub struct ActionEntryBuilder<O>(ActionEntry<O>)
where
    O: IsA<ActionMap>;

impl<O> ActionEntryBuilder<O>
where
    O: IsA<ActionMap>,
{
    pub fn new(name: &str) -> Self {
        Self(ActionEntry {
            name: name.to_owned(),
            parameter_type: Default::default(),
            state: Default::default(),
            activate: Default::default(),
            change_state: Default::default(),
        })
    }

    pub fn parameter_type(mut self, parameter_type: Option<&VariantTy>) -> Self {
        self.0.parameter_type = parameter_type.map(|vt| vt.to_owned());
        self
    }

    pub fn state(mut self, state: Variant) -> Self {
        self.0.state = Some(state);
        self
    }

    pub fn activate<F: Fn(&O, &SimpleAction, Option<&Variant>) + 'static>(
        mut self,
        callback: F,
    ) -> Self {
        self.0.activate = Some(Box::new(callback));
        self
    }

    pub fn change_state<F: Fn(&O, &SimpleAction, Option<&Variant>) + 'static>(
        mut self,
        callback: F,
    ) -> Self {
        self.0.change_state = Some(Box::new(callback));
        self
    }

    pub fn build(self) -> ActionEntry<O> {
        self.0
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::prelude::*;

    #[test]
    fn action_entry() {
        let app = crate::Application::new(None, Default::default());

        app.add_action_entries(vec![
            ActionEntry::builder("close")
                .activate(move |_app, _, _| {
                    //Do something
                })
                .build(),
            ActionEntry::builder("enable")
                .state(true.to_variant())
                .change_state(move |_app, _, _| {
                    //Do something
                })
                .build(),
        ]);
        assert!(app.lookup_action("close").is_some());
        assert!(app.lookup_action("enable").is_some());
    }
}