use chrono::Local;
use iced_graphics::{Backend, Renderer};
use iced_native::widget::button;
use iced_native::widget::tree::{self, Tag};
use iced_native::widget::Tree;
use iced_native::{event, mouse, Clipboard, Event, Layout, Point, Rectangle, Shell};
use iced_native::{Element, Widget};
pub use crate::core::date::Date;
pub use crate::style::date_picker::{Appearance, StyleSheet};
use super::overlay::date_picker::{self, DatePickerOverlay, DatePickerOverlayButtons};
#[allow(missing_debug_implementations)]
pub struct DatePicker<'a, Message, B, Theme>
where
Message: Clone,
B: Backend + iced_graphics::backend::Text,
Theme: StyleSheet + button::StyleSheet,
{
show_picker: bool,
date: Date,
underlay: Element<'a, Message, Renderer<B, Theme>>,
on_cancel: Message,
on_submit: Box<dyn Fn(Date) -> Message>,
style: <Theme as StyleSheet>::Style,
overlay_state: Element<'a, Message, Renderer<B, Theme>>,
}
impl<'a, Message, B, Theme> DatePicker<'a, Message, B, Theme>
where
Message: 'a + Clone,
B: 'a + Backend + iced_graphics::backend::Text,
Theme: 'a
+ StyleSheet
+ button::StyleSheet
+ iced_style::text::StyleSheet
+ iced_style::container::StyleSheet,
{
pub fn new<U, F>(
show_picker: bool,
date: impl Into<Date>,
underlay: U,
on_cancel: Message,
on_submit: F,
) -> Self
where
U: Into<Element<'a, Message, Renderer<B, Theme>>>,
F: 'static + Fn(Date) -> Message,
{
Self {
show_picker,
date: date.into(),
underlay: underlay.into(),
on_cancel,
on_submit: Box::new(on_submit),
style: <Theme as StyleSheet>::Style::default(),
overlay_state: DatePickerOverlayButtons::default().into(),
}
}
#[must_use]
pub fn style(mut self, style: <Theme as StyleSheet>::Style) -> Self {
self.style = style;
self
}
}
#[derive(Debug)]
pub struct State {
pub(crate) overlay_state: date_picker::State,
}
impl State {
#[must_use]
pub fn now() -> Self {
Self {
overlay_state: date_picker::State::default(),
}
}
#[must_use]
pub fn new(date: Date) -> Self {
Self {
overlay_state: date_picker::State::new(date.into()),
}
}
pub fn reset(&mut self) {
self.overlay_state.date = Local::today().naive_local();
}
}
impl<'a, Message, B, Theme> Widget<Message, Renderer<B, Theme>>
for DatePicker<'a, Message, B, Theme>
where
Message: 'static + Clone,
B: 'a + Backend + iced_graphics::backend::Text,
Theme: StyleSheet
+ button::StyleSheet
+ iced_style::text::StyleSheet
+ iced_style::container::StyleSheet,
{
fn tag(&self) -> Tag {
Tag::of::<State>()
}
fn state(&self) -> tree::State {
tree::State::new(State::new(self.date))
}
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.underlay), Tree::new(&self.overlay_state)]
}
fn diff(&self, tree: &mut Tree) {
tree.diff_children(&[&self.underlay, &self.overlay_state]);
}
fn width(&self) -> iced_native::Length {
self.underlay.as_widget().width()
}
fn height(&self) -> iced_native::Length {
self.underlay.as_widget().width()
}
fn layout(
&self,
renderer: &Renderer<B, Theme>,
limits: &iced_native::layout::Limits,
) -> iced_native::layout::Node {
self.underlay.as_widget().layout(renderer, limits)
}
fn on_event(
&mut self,
state: &mut Tree,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer<B, Theme>,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.underlay.as_widget_mut().on_event(
&mut state.children[0],
event,
layout,
cursor_position,
renderer,
clipboard,
shell,
)
}
fn mouse_interaction(
&self,
state: &Tree,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
renderer: &Renderer<B, Theme>,
) -> mouse::Interaction {
self.underlay.as_widget().mouse_interaction(
&state.children[0],
layout,
cursor_position,
viewport,
renderer,
)
}
fn draw(
&self,
state: &iced_native::widget::Tree,
renderer: &mut Renderer<B, Theme>,
theme: &Theme,
style: &iced_native::renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
) {
self.underlay.as_widget().draw(
&state.children[0],
renderer,
theme,
style,
layout,
cursor_position,
viewport,
);
}
fn overlay<'b>(
&'b mut self,
state: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer<B, Theme>,
) -> Option<iced_native::overlay::Element<'b, Message, Renderer<B, Theme>>> {
let picker_state: &mut State = state.state.downcast_mut();
if !self.show_picker {
return self
.underlay
.as_widget_mut()
.overlay(&mut state.children[0], layout, renderer);
}
let bounds = layout.bounds();
let position = Point::new(bounds.center_x(), bounds.center_y());
Some(
DatePickerOverlay::new(
picker_state,
self.on_cancel.clone(),
&self.on_submit,
position,
self.style,
&mut state.children[1],
)
.overlay(),
)
}
}
impl<'a, Message, B, Theme> From<DatePicker<'a, Message, B, Theme>>
for Element<'a, Message, Renderer<B, Theme>>
where
Message: 'static + Clone,
B: 'a + Backend + iced_graphics::backend::Text,
Theme: 'a
+ StyleSheet
+ button::StyleSheet
+ iced_style::text::StyleSheet
+ iced_style::container::StyleSheet,
{
fn from(date_picker: DatePicker<'a, Message, B, Theme>) -> Self {
Element::new(date_picker)
}
}