summaryrefslogtreecommitdiffstats
path: root/core/src/menu.rs
blob: 521861408e0b641e8e75f5a26a60e6f39f575d45 (plain) (blame)
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
//! Build menus for your application.
use crate::keyboard::Hotkey;

/// Menu representation.
///
/// This can be used by `shell` implementations to create a menu.
#[derive(Debug, Clone)]
pub struct Menu<Message> {
    entries: Vec<Entry<Message>>,
}

impl<Message> PartialEq for Menu<Message> {
    fn eq(&self, other: &Self) -> bool {
        self.entries == other.entries
    }
}

impl<Message> Menu<Message> {
    /// Creates an empty [`Menu`].
    pub fn new() -> Self {
        Self::with_entries(Vec::new())
    }

    /// Creates a new [`Menu`] with the given entries.
    pub fn with_entries(entries: Vec<Entry<Message>>) -> Self {
        Self { entries }
    }

    /// Adds an [`Entry`] to the [`Menu`].
    pub fn push(mut self, entry: Entry<Message>) -> Self {
        self.entries.push(entry);
        self
    }

    /// Returns a [`MenuEntry`] iterator.
    pub fn iter(&self) -> impl Iterator<Item = &Entry<Message>> {
        self.entries.iter()
    }
}

/// Represents one of the possible entries used to build a [`Menu`].
#[derive(Debug, Clone)]
pub enum Entry<Message> {
    /// Item for a [`Menu`]
    Item {
        /// The title of the item
        content: String,
        /// The [`Hotkey`] to activate the item, if any
        hotkey: Option<Hotkey>,
        /// The message generated when the item is activated
        on_activation: Message,
    },
    /// Dropdown for a [`Menu`]
    Dropdown {
        /// Title of the dropdown
        content: String,
        /// The submenu of the dropdown
        submenu: Menu<Message>,
    },
    /// Separator for a [`Menu`]
    Separator,
}

impl<Message> Entry<Message> {
    /// Creates an [`Entry::Item`].
    pub fn item<S: Into<String>>(
        content: S,
        hotkey: impl Into<Option<Hotkey>>,
        on_activation: Message,
    ) -> Self {
        let content = content.into();
        let hotkey = hotkey.into();

        Entry::Item {
            content,
            hotkey,
            on_activation,
        }
    }

    /// Creates an [`Entry::Dropdown`].
    pub fn dropdown<S: Into<String>>(
        content: S,
        submenu: Menu<Message>,
    ) -> Self {
        let content = content.into();

        Entry::Dropdown { content, submenu }
    }
}

impl<Message> PartialEq for Entry<Message> {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (
                Entry::Item {
                    content, hotkey, ..
                },
                Entry::Item {
                    content: other_content,
                    hotkey: other_hotkey,
                    ..
                },
            ) => content == other_content && hotkey == other_hotkey,
            (
                Entry::Dropdown { content, submenu },
                Entry::Dropdown {
                    content: other_content,
                    submenu: other_submenu,
                },
            ) => content == other_content && submenu == other_submenu,
            (Entry::Separator, Entry::Separator) => true,
            _ => false,
        }
    }
}