1use std::borrow::Cow;
5
6use crate::widget::{FlexRow, Row, column, container, flex_row, list, row, text};
7use crate::{Element, Theme, theme};
8use derive_setters::Setters;
9use iced_core::Length;
10use iced_core::text::Wrapping;
11use iced_widget::space;
12use taffy::AlignContent;
13
14#[must_use]
16#[allow(clippy::module_name_repetitions)]
17pub fn item<'a, Message: 'static>(
18 title: impl Into<Cow<'a, str>> + 'a,
19 widget: impl Into<Element<'a, Message>> + 'a,
20) -> Row<'a, Message, Theme> {
21 #[inline(never)]
22 fn inner<'a, Message: 'static>(
23 title: Cow<'a, str>,
24 widget: Element<'a, Message>,
25 ) -> Row<'a, Message, Theme> {
26 item_row(vec![
27 text(title).wrapping(Wrapping::Word).into(),
28 space::horizontal().into(),
29 widget,
30 ])
31 }
32
33 inner(title.into(), widget.into())
34}
35
36#[must_use]
38#[allow(clippy::module_name_repetitions)]
39pub fn item_row<Message>(children: Vec<Element<Message>>) -> Row<Message, Theme> {
40 row::with_children(children)
41 .spacing(theme::spacing().space_xs)
42 .align_y(iced::Alignment::Center)
43 .width(Length::Fill)
44}
45
46#[allow(clippy::module_name_repetitions)]
48pub fn flex_item<'a, Message: 'static>(
49 title: impl Into<Cow<'a, str>> + 'a,
50 widget: impl Into<Element<'a, Message>> + 'a,
51) -> FlexRow<'a, Message> {
52 #[inline(never)]
53 fn inner<'a, Message: 'static>(
54 title: Cow<'a, str>,
55 widget: Element<'a, Message>,
56 ) -> FlexRow<'a, Message> {
57 flex_item_row(vec![
58 text(title)
59 .wrapping(Wrapping::Word)
60 .width(Length::Fill)
61 .into(),
62 container(widget).width(Length::Shrink).into(),
63 ])
64 .width(Length::Fill)
65 }
66
67 inner(title.into(), widget.into())
68}
69
70#[allow(clippy::module_name_repetitions)]
72pub fn flex_item_row<Message>(children: Vec<Element<Message>>) -> FlexRow<Message> {
73 flex_row(children)
74 .spacing(theme::spacing().space_xs)
75 .min_item_width(200.0)
76 .justify_items(iced::Alignment::Center)
77 .justify_content(AlignContent::SpaceBetween)
78 .width(Length::Fill)
79}
80
81pub fn builder<'a, Message: 'static>(title: impl Into<Cow<'a, str>>) -> Item<'a, Message> {
83 Item {
84 title: title.into(),
85 description: None,
86 icon: None,
87 }
88}
89
90#[derive(Setters)]
92pub struct Item<'a, Message> {
93 title: Cow<'a, str>,
95
96 #[setters(strip_option, into)]
98 description: Option<Cow<'a, str>>,
99
100 #[setters(strip_option, into)]
102 icon: Option<Element<'a, Message>>,
103}
104
105impl<'a, Message: Clone + 'static> Item<'a, Message> {
106 pub fn control(self, widget: impl Into<Element<'a, Message>>) -> Row<'a, Message, Theme> {
108 item_row(self.control_(widget.into()))
109 }
110
111 pub fn flex_control(self, widget: impl Into<Element<'a, Message>>) -> FlexRow<'a, Message> {
113 flex_item_row(self.control_(widget.into()))
114 }
115
116 fn label(self) -> Element<'a, Message> {
117 if let Some(description) = self.description {
118 column::with_capacity(2)
119 .spacing(2)
120 .push(text::body(self.title).wrapping(Wrapping::Word))
121 .push(text::caption(description).wrapping(Wrapping::Word))
122 .width(Length::Fill)
123 .into()
124 } else {
125 text(self.title).width(Length::Fill).into()
126 }
127 }
128
129 #[inline(never)]
130 fn control_(mut self, widget: Element<'a, Message>) -> Vec<Element<'a, Message>> {
131 let mut contents = Vec::with_capacity(3);
132 if let Some(icon) = self.icon.take() {
133 contents.push(icon);
134 }
135 contents.push(self.label());
136 contents.push(widget);
137 contents
138 }
139
140 fn control_start(self, widget: impl Into<Element<'a, Message>>) -> Row<'a, Message, Theme> {
141 item_row(vec![widget.into(), self.label()])
142 }
143
144 pub fn toggler(
145 self,
146 is_checked: bool,
147 message: impl Fn(bool) -> Message + 'static,
148 ) -> list::ListButton<'a, Message> {
149 let on_press = message(!is_checked);
150 list::button(
151 self.control(
152 crate::widget::toggler(is_checked)
153 .width(Length::Shrink)
154 .on_toggle(message),
155 ),
156 )
157 .on_press(on_press)
158 }
159
160 pub fn toggler_maybe(
161 self,
162 is_checked: bool,
163 message: Option<impl Fn(bool) -> Message + 'static>,
164 ) -> list::ListButton<'a, Message> {
165 let on_press = message.as_ref().map(|f| f(!is_checked));
166 list::button(
167 self.control(
168 crate::widget::toggler(is_checked)
169 .width(Length::Shrink)
170 .on_toggle_maybe(message),
171 ),
172 )
173 .on_press_maybe(on_press)
174 }
175
176 pub fn checkbox(
177 self,
178 is_checked: bool,
179 message: impl Fn(bool) -> Message + 'static,
180 ) -> list::ListButton<'a, Message> {
181 let on_press = message(!is_checked);
182 list::button(
183 self.control_start(
184 crate::widget::checkbox(is_checked)
185 .width(Length::Shrink)
186 .on_toggle(message),
187 ),
188 )
189 .on_press(on_press)
190 }
191
192 pub fn checkbox_maybe(
193 self,
194 is_checked: bool,
195 message: Option<impl Fn(bool) -> Message + 'static>,
196 ) -> list::ListButton<'a, Message> {
197 let on_press = message.as_ref().map(|f| f(!is_checked));
198 list::button(
199 self.control_start(
200 crate::widget::checkbox(is_checked)
201 .width(Length::Shrink)
202 .on_toggle_maybe(message),
203 ),
204 )
205 .on_press_maybe(on_press)
206 }
207
208 pub fn radio<V, F>(self, value: V, selected: Option<V>, f: F) -> list::ListButton<'a, Message>
209 where
210 V: Eq + Copy,
211 F: Fn(V) -> Message,
212 {
213 let on_press = f(value);
214 list::button(
215 self.control_start(crate::widget::radio::Radio::new_no_label(
216 value, selected, f,
217 )),
218 )
219 .on_press(on_press)
220 }
221}