diff --git a/src/widget/mod.rs b/src/widget/mod.rs index a1b6cb51..4953b127 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -349,3 +349,7 @@ pub mod tooltip { pub mod warning; #[doc(inline)] pub use warning::*; + +mod toaster; +#[doc(inline)] +pub use toaster::toaster; \ No newline at end of file diff --git a/src/widget/toaster/mod.rs b/src/widget/toaster/mod.rs new file mode 100644 index 00000000..9d7cc7d1 --- /dev/null +++ b/src/widget/toaster/mod.rs @@ -0,0 +1,106 @@ +// Copyright 2024 wiiznokes +// SPDX-License-Identifier: MIT + +use std::time::Duration; + +use iced::{Command, Length}; +use iced_core::{Element}; +use crate::widget::container; +use crate::widget::Column; +use widget::Toaster; + +use crate::ext::CollectionWidget; + +use super::{button, row, text}; + + +mod widget; + +pub fn toaster<'a, Message: Clone + 'a, Theme, Renderer>( + toasts: &'a Toasts, + content: impl Into>, +) -> Element<'a, Message, Theme, Renderer> +where + Renderer: iced_core::Renderer, +{ + + + let make_toast = |toast: &Toast| { + + let row = row() + .push(text(toast.message)) + .push_maybe(toast.action.map(|t| button(text(t.0)).on_press(t.1))); + + container(row) + .into() + + }; + + + let col = Column::with_children(toasts.toasts.iter().map(|t| { + make_toast(t) + })); + + + + Toaster::new(col.into(), content.into()).into() +} + +pub enum ToastDuration { + Short, + Medium, + Long, +} + +impl ToastDuration { + fn duration(&self) -> Duration { + match self { + ToastDuration::Short => Duration::from_millis(200), + ToastDuration::Medium => Duration::from_millis(1000), + ToastDuration::Long => Duration::from_millis(5000), + } + } +} + +pub struct Toast { + message: String, + /// (Description, message) + action: Option<(String, Message)>, + + duration: ToastDuration, + + id: u32, +} + +pub struct Toasts { + id_count: u32, + toasts: Vec>, +} + +pub struct ToastMessage(u32); + +impl Toasts { + pub fn push(&mut self, mut toast: Toast) -> Command { + toast.id = self.id_count; + self.id_count += 1; + + let message = ToastMessage(toast.id); + let duration = toast.duration.duration(); + + self.toasts.push(toast); + + Command::perform( + async move { + tokio::time::sleep(duration).await; + }, + |_| message, + ) + } + + pub fn handle_message(&mut self, message: ToastMessage) { + self.toasts + .iter() + .position(|e| e.id == message.0) + .map(|index| self.toasts.remove(index)); + } +} diff --git a/src/widget/toaster/widget.rs b/src/widget/toaster/widget.rs new file mode 100644 index 00000000..ec52b221 --- /dev/null +++ b/src/widget/toaster/widget.rs @@ -0,0 +1,97 @@ +// Copyright 2024 wiiznokes +// SPDX-License-Identifier: MIT + +use iced::Size; +use iced_runtime::core::widget::Id; +use iced_runtime::{keyboard, Command}; + +use iced_core::event::{self, Event}; +use iced_core::renderer::{self, Quad, Renderer}; +use iced_core::touch; +use iced_core::widget::tree::{self, Tree}; +use iced_core::widget::Operation; +use iced_core::Element; +use iced_core::{layout, svg}; +use iced_core::{mouse, Border}; +use iced_core::{overlay, Shadow}; +use iced_core::{ + Background, Clipboard, Color, Layout, Length, Padding, Point, Rectangle, Shell, Vector, Widget, +}; +use iced_renderer::core::widget::{operation, OperationOutputWrapper}; + + + +use super::Toasts; + +pub struct Toaster<'a, Message, Theme, Renderer> { + toasts: Element<'a, Message, Theme, Renderer>, + content: Element<'a, Message, Theme, Renderer>, +} + +impl<'a, Message, Theme, Renderer> Toaster<'a, Message, Theme, Renderer> { + pub fn new( + toasts: Element<'a, Message, Theme, Renderer>, + content: Element<'a, Message, Theme, Renderer>, + ) -> Self { + Self { + toasts, + content, + } + } +} + +impl<'a, Message, Theme, Renderer> Widget + for Toaster<'a, Message, Theme, Renderer> +where + Renderer: iced_core::Renderer, +{ + fn size(&self) -> Size { + todo!() + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + todo!() + } + + fn mouse_interaction( + &self, + _state: &Tree, + _layout: Layout<'_>, + _cursor: mouse::Cursor, + _viewport: &Rectangle, + _renderer: &Renderer, + ) -> mouse::Interaction { + todo!() + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + } +} + +impl<'a, Message, Theme, Renderer> From> + for Element<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer + 'a, + Theme: 'a, + Message: 'a +{ + fn from( + toggler: Toaster<'a, Message, Theme, Renderer>, + ) -> Element<'a, Message, Theme, Renderer> { + Element::new(toggler) + } +}