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