1use super::overlay::time_picker::{self, TimePickerOverlay, TimePickerOverlayButtons};
6
7use chrono::Local;
8use iced_core::{
9 Clipboard, Element, Event, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget,
10 layout::{Limits, Node},
11 mouse::{self, Cursor},
12 overlay, renderer,
13 widget::tree::{self, Tag, Tree},
14};
15use iced_widget::{Renderer, button, container, text};
16
17pub use crate::{
18 core::time::{Period, Time},
19 style::{
20 Status, StyleFn,
21 time_picker::{Catalog, Style},
22 },
23};
24
25#[allow(missing_debug_implementations)]
50pub struct TimePicker<'a, Message, Theme>
51where
52 Message: Clone,
53 Theme: Catalog + button::Catalog,
54{
55 show_picker: bool,
57 time: Time,
59 underlay: Element<'a, Message, Theme, Renderer>,
61 on_cancel: Message,
63 on_submit: Box<dyn Fn(Time) -> Message>,
65 class: <Theme as Catalog>::Class<'a>,
67 overlay_state: Element<'a, Message, Theme, Renderer>,
69 use_24h: bool,
71 show_seconds: bool,
73}
74
75impl<'a, Message, Theme> TimePicker<'a, Message, Theme>
76where
77 Message: 'a + Clone,
78 Theme: 'a + Catalog + button::Catalog + text::Catalog,
79{
80 pub fn new<U, F>(
91 show_picker: bool,
92 time: impl Into<Time>,
93 underlay: U,
94 on_cancel: Message,
95 on_submit: F,
96 ) -> Self
97 where
98 U: Into<Element<'a, Message, Theme, Renderer>>,
99 F: 'static + Fn(Time) -> Message,
100 {
101 Self {
102 show_picker,
103 time: time.into(),
104 underlay: underlay.into(),
105 on_cancel,
106 on_submit: Box::new(on_submit),
107 class: <Theme as Catalog>::default(),
108 overlay_state: TimePickerOverlayButtons::default().into(),
109 use_24h: false,
110 show_seconds: false,
111 }
112 }
113
114 #[must_use]
116 pub fn show_seconds(mut self) -> Self {
117 self.show_seconds = true;
118 self
119 }
120
121 #[must_use]
123 pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
124 where
125 <Theme as Catalog>::Class<'a>: From<StyleFn<'a, Theme, Style>>,
126 {
127 self.class = (Box::new(style) as StyleFn<'a, Theme, Style>).into();
128 self
129 }
130
131 #[must_use]
133 pub fn use_24h(mut self) -> Self {
134 self.use_24h = true;
135 self
136 }
137
138 #[must_use]
140 pub fn class(mut self, class: impl Into<<Theme as Catalog>::Class<'a>>) -> Self {
141 self.class = class.into();
142 self
143 }
144}
145
146#[derive(Debug)]
148pub struct State {
149 pub(crate) overlay_state: time_picker::State,
151}
152
153impl State {
154 #[must_use]
156 pub fn now() -> Self {
157 Self {
158 overlay_state: time_picker::State::default(),
159 }
160 }
161
162 #[must_use]
164 pub fn new(time: Time, use_24h: bool, show_seconds: bool) -> Self {
165 Self {
166 overlay_state: time_picker::State::new(time, use_24h, show_seconds),
167 }
168 }
169
170 pub fn reset(&mut self) {
172 self.overlay_state.clock_cache.clear();
173 self.overlay_state.time = Local::now().naive_local().time();
174 }
175}
176
177impl<Message, Theme> Widget<Message, Theme, Renderer> for TimePicker<'_, Message, Theme>
178where
179 Message: 'static + Clone,
180 Theme: Catalog + button::Catalog + text::Catalog + container::Catalog,
181{
182 fn tag(&self) -> Tag {
183 Tag::of::<State>()
184 }
185
186 fn state(&self) -> tree::State {
187 tree::State::new(State::new(self.time, self.use_24h, self.show_seconds))
188 }
189
190 fn children(&self) -> Vec<Tree> {
191 vec![Tree::new(&self.underlay), Tree::new(&self.overlay_state)]
192 }
193
194 fn diff(&self, tree: &mut Tree) {
195 tree.diff_children(&[&self.underlay, &self.overlay_state]);
196 }
197
198 fn size(&self) -> Size<Length> {
199 self.underlay.as_widget().size()
200 }
201
202 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node {
203 self.underlay
204 .as_widget_mut()
205 .layout(&mut tree.children[0], renderer, limits)
206 }
207
208 fn update(
209 &mut self,
210 state: &mut Tree,
211 event: &Event,
212 layout: Layout<'_>,
213 cursor: Cursor,
214 renderer: &Renderer,
215 clipboard: &mut dyn Clipboard,
216 shell: &mut Shell<'_, Message>,
217 viewport: &Rectangle,
218 ) {
219 self.underlay.as_widget_mut().update(
220 &mut state.children[0],
221 event,
222 layout,
223 cursor,
224 renderer,
225 clipboard,
226 shell,
227 viewport,
228 );
229 }
230
231 fn mouse_interaction(
232 &self,
233 state: &Tree,
234 layout: Layout<'_>,
235 cursor: Cursor,
236 viewport: &Rectangle,
237 renderer: &Renderer,
238 ) -> mouse::Interaction {
239 self.underlay.as_widget().mouse_interaction(
240 &state.children[0],
241 layout,
242 cursor,
243 viewport,
244 renderer,
245 )
246 }
247
248 fn draw(
249 &self,
250 tree: &Tree,
251 renderer: &mut Renderer,
252 theme: &Theme,
253 style: &renderer::Style,
254 layout: Layout<'_>,
255 cursor: Cursor,
256 viewport: &Rectangle,
257 ) {
258 self.underlay.as_widget().draw(
259 &tree.children[0],
260 renderer,
261 theme,
262 style,
263 layout,
264 cursor,
265 viewport,
266 );
267 }
268
269 fn overlay<'b>(
270 &'b mut self,
271 tree: &'b mut Tree,
272 layout: Layout<'b>,
273 renderer: &Renderer,
274 viewport: &Rectangle,
275 translation: Vector,
276 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
277 let picker_state: &mut State = tree.state.downcast_mut();
278
279 if !self.show_picker {
280 return self.underlay.as_widget_mut().overlay(
281 &mut tree.children[0],
282 layout,
283 renderer,
284 viewport,
285 translation,
286 );
287 }
288
289 let bounds = layout.bounds();
290 let position = Point::new(bounds.center_x(), bounds.center_y());
291
292 Some(
293 TimePickerOverlay::new(
294 picker_state,
295 self.on_cancel.clone(),
296 &self.on_submit,
297 position,
298 &self.class,
299 &mut tree.children[1],
300 *viewport,
301 )
302 .overlay(),
303 )
304 }
305}
306
307impl<'a, Message, Theme> From<TimePicker<'a, Message, Theme>>
308 for Element<'a, Message, Theme, Renderer>
309where
310 Message: 'static + Clone,
311 Theme: 'a + Catalog + button::Catalog + text::Catalog + container::Catalog,
312{
313 fn from(time_picker: TimePicker<'a, Message, Theme>) -> Self {
314 Element::new(time_picker)
315 }
316}