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