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