1use iced::{
2 Border, Color, Element, Length, alignment,
3 widget::{button, row, text},
4};
5use iced_fonts::lucide::{chevron_right, square, square_check_big};
6
7pub use iced_aw::{menu_bar, menu_items};
8
9pub fn base_button<'a, Message>(
10 content: impl Into<Element<'a, Message>>,
11 msg: Message,
12) -> button::Button<'a, Message>
13where
14 Message: Clone + 'a,
15{
16 button(content)
17 .padding([4, 8])
18 .style(|theme, status| {
19 use button::{Status, Style};
20
21 let palette = theme.extended_palette();
22 let base = Style {
23 text_color: palette.background.base.text,
24 border: Border::default().rounded(6.0),
25 ..Style::default()
26 };
27 match status {
28 Status::Active => base.with_background(Color::TRANSPARENT),
29 Status::Hovered => base.with_background(Color::from_rgb(
30 (palette.primary.weak.color.r * 1.2).min(1.0),
31 (palette.primary.weak.color.g * 1.2).min(1.0),
32 (palette.primary.weak.color.b * 1.2).min(1.0),
33 )),
34 Status::Disabled => base.with_background(Color::from_rgb(0.5, 0.5, 0.5)),
35 Status::Pressed => base.with_background(palette.primary.weak.color),
36 }
37 })
38 .on_press(msg)
39}
40
41pub fn menu_button<Message>(
42 label: impl Into<String>,
43 width: Option<Length>,
44 height: Option<Length>,
45 msg: Message,
46) -> Element<'static, Message, iced::Theme, iced::Renderer>
47where
48 Message: Clone + 'static,
49{
50 let label = label.into();
51 base_button(
52 text(label)
53 .height(height.unwrap_or(Length::Shrink))
54 .align_y(alignment::Vertical::Center),
55 msg,
56 )
57 .width(width.unwrap_or(Length::Shrink))
58 .height(height.unwrap_or(Length::Shrink))
59 .into()
60}
61
62pub fn menu_dropdown<Message>(
63 label: impl Into<String>,
64 message: Message,
65) -> Element<'static, Message, iced::Theme, iced::Renderer>
66where
67 Message: Clone + 'static,
68{
69 menu_button(label, Some(Length::Shrink), Some(Length::Shrink), message)
70}
71
72pub fn menu_item<Message>(
73 label: impl Into<String>,
74 message: Message,
75) -> Element<'static, Message, iced::Theme, iced::Renderer>
76where
77 Message: Clone + 'static,
78{
79 menu_button(label, Some(Length::Fill), Some(Length::Shrink), message)
80}
81
82pub fn menu_checkbox_item<Message>(
83 label: impl Into<String>,
84 checked: bool,
85 message: Message,
86) -> Element<'static, Message, iced::Theme, iced::Renderer>
87where
88 Message: Clone + 'static,
89{
90 let label = label.into();
91 let icon = if checked {
92 square_check_big()
93 } else {
94 square()
95 };
96 base_button(
97 row![
98 icon.width(20).align_y(alignment::Vertical::Center),
99 text(label)
100 .height(Length::Shrink)
101 .align_y(alignment::Vertical::Center),
102 ]
103 .spacing(6)
104 .align_y(iced::Alignment::Center),
105 message,
106 )
107 .width(Length::Fill)
108 .height(Length::Shrink)
109 .into()
110}
111
112pub fn menu_item_maybe<Message>(
113 label: impl Into<String>,
114 message: Option<Message>,
115) -> Element<'static, Message, iced::Theme, iced::Renderer>
116where
117 Message: Clone + 'static,
118{
119 let label = label.into();
120 let btn = button(
121 text(label)
122 .height(Length::Shrink)
123 .align_y(alignment::Vertical::Center),
124 )
125 .padding([4, 8])
126 .style(|theme: &iced::Theme, status| {
127 use button::{Status, Style};
128
129 let palette = theme.extended_palette();
130 let base = Style {
131 text_color: palette.background.base.text,
132 border: Border::default().rounded(6.0),
133 ..Style::default()
134 };
135 match status {
136 Status::Active => base.with_background(Color::TRANSPARENT),
137 Status::Hovered => base.with_background(Color::from_rgb(
138 palette.primary.weak.color.r * 1.2,
139 palette.primary.weak.color.g * 1.2,
140 palette.primary.weak.color.b * 1.2,
141 )),
142 Status::Disabled => base.with_background(Color::from_rgb(0.5, 0.5, 0.5)),
143 Status::Pressed => base.with_background(palette.primary.weak.color),
144 }
145 })
146 .width(Length::Fill)
147 .height(Length::Shrink);
148 if let Some(message) = message {
149 btn.on_press(message).into()
150 } else {
151 btn.into()
152 }
153}
154
155pub fn submenu<Message>(
156 label: impl Into<String>,
157 msg: Message,
158) -> Element<'static, Message, iced::Theme, iced::Renderer>
159where
160 Message: Clone + 'static,
161{
162 let label = label.into();
163 base_button(
164 row![
165 text(label)
166 .width(Length::Fill)
167 .align_y(alignment::Vertical::Center),
168 chevron_right(),
169 ]
170 .align_y(iced::Alignment::Center),
171 msg,
172 )
173 .width(Length::Fill)
174 .height(Length::Shrink)
175 .into()
176}