//! Widget used to display timestamped error and warning reports in an overlay.
use iced::advanced::{layout, overlay};
use iced::{mouse, Alignment, Element, Event, Point, Rectangle, Size, Vector};
use iced_core::widget::{Operation, Tree};
use iced_core::{renderer, Clipboard, Layout, Shell};
use jiff::Timestamp;
#[derive(Clone, Copy, Debug)]
pub enum Level {
Warning,
Error,
}
#[derive(Debug)]
pub struct Entry {
pub level: Level,
pub msg: String,
pub time: Timestamp,
pub is_read: bool,
}
#[derive(Debug, Default)]
pub struct Container {
pub hidden: bool,
pub entries: Vec<Entry>,
}
struct Widget<'a, Msg, Theme, Renderer> {
pub overlay: Element<'a, Msg, Theme, Renderer>,
pub overlaid: Element<'a, Msg, Theme, Renderer>,
}
pub fn view<'a, Msg, Theme, Renderer, FV>(
container: &'a Container,
overlaid: Element<'a, Msg, Theme, Renderer>,
view_container: FV,
) -> Element<'a, Msg, Theme, Renderer>
where
Msg: 'a,
Theme: 'a,
Renderer: 'a + iced::advanced::Renderer,
FV: Fn(&'a Container) -> Element<'a, Msg, Theme, Renderer>,
{
if container.hidden || container.entries.is_empty() {
overlaid
} else {
let overlay = view_container(container);
Element::new(Widget::<'a, Msg, _, _> { overlay, overlaid })
}
}
impl<Msg, Theme, Renderer> iced::advanced::Widget<Msg, Theme, Renderer>
for Widget<'_, Msg, Theme, Renderer>
where
Renderer: iced::advanced::Renderer,
{
fn size(&self) -> iced::Size<iced::Length> {
self.overlaid.as_widget().size()
}
fn layout(
&self,
tree: &mut iced_core::widget::Tree,
renderer: &Renderer,
limits: &iced_core::layout::Limits,
) -> iced_core::layout::Node {
self.overlaid.as_widget().layout(
&mut tree.children[0],
renderer,
limits,
)
}
fn update(
&mut self,
state: &mut Tree,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Msg>,
viewport: &Rectangle,
) {
self.overlaid.as_widget_mut().update(
&mut state.children[0],
event,
layout,
cursor,
renderer,
clipboard,
shell,
viewport,
);
}
fn draw(
&self,
tree: &iced_core::widget::Tree,
renderer: &mut Renderer,
theme: &Theme,
style: &iced_core::renderer::Style,
layout: iced_core::Layout<'_>,
cursor: iced_core::mouse::Cursor,
viewport: &iced::Rectangle,
) {
self.overlaid.as_widget().draw(
&tree.children[0],
renderer,
theme,
style,
layout,
cursor,
viewport,
)
}
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.overlaid), Tree::new(&self.overlay)]
}
fn diff(&self, tree: &mut Tree) {
tree.diff_children(&[&self.overlaid, &self.overlay]);
}
fn operate(
&self,
state: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation,
) {
operation.container(None, layout.bounds(), &mut |operation| {
self.overlaid.as_widget().operate(
&mut state.children[0],
layout,
renderer,
operation,
);
});
}
fn mouse_interaction(
&self,
state: &Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
self.overlaid.as_widget().mouse_interaction(
&state.children[0],
layout,
cursor,
viewport,
renderer,
)
}
fn overlay<'a>(
&'a mut self,
tree: &'a mut Tree,
layout: Layout<'a>,
renderer: &Renderer,
viewport: &Rectangle,
translation: Vector,
) -> Option<overlay::Element<'a, Msg, Theme, Renderer>> {
let (content_tree, toast_tree) = tree.children.split_at_mut(1);
let content_overlay = self.overlaid.as_widget_mut().overlay(
&mut content_tree[0],
layout,
renderer,
viewport,
translation,
);
let toast_overlay = Overlay {
overlay: &mut self.overlay,
tree: &mut toast_tree[0],
position: layout.bounds().position() + translation,
bounds: layout.bounds(),
};
let toast_overlay = overlay::Element::new(Box::new(toast_overlay));
let overlays = content_overlay
.into_iter()
.chain(std::iter::once(toast_overlay))
.collect::<Vec<_>>();
Some(overlay::Group::with_children(overlays).overlay())
}
}
struct Overlay<'a, 'b, Msg, Theme, Renderer> {
overlay: &'b mut Element<'a, Msg, Theme, Renderer>,
tree: &'b mut Tree,
position: Point,
bounds: Rectangle,
}
impl<'a, Msg, Theme, Renderer> overlay::Overlay<Msg, Theme, Renderer>
for Overlay<'a, '_, Msg, Theme, Renderer>
where
Renderer: iced::advanced::Renderer,
{
fn layout(&mut self, renderer: &Renderer, _bounds: Size) -> layout::Node {
let bounds = self.bounds.size();
let limits = layout::Limits::new(Size::ZERO, bounds);
layout::Node::with_children(
bounds,
vec![self
.overlay
.as_widget()
.layout(self.tree, renderer, &limits)
.align(
// TODO parameterize
Alignment::End,
Alignment::End,
bounds,
)],
)
.translate([self.position.x, self.position.y])
}
fn draw(
&self,
renderer: &mut Renderer,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
) {
let viewport = layout.bounds();
self.overlay.as_widget().draw(
self.tree,
renderer,
theme,
style,
layout.children().next().unwrap(),
cursor,
&viewport,
)
}
fn update(
&mut self,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Msg>,
) {
let viewport = layout.bounds();
self.overlay.as_widget_mut().update(
self.tree,
event,
layout.children().next().unwrap(),
cursor,
renderer,
clipboard,
shell,
&viewport,
);
}
fn operate(
&mut self,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation,
) {
operation.container(None, layout.bounds(), &mut |operation| {
self.overlay.as_widget().operate(
self.tree,
layout.children().next().unwrap(),
renderer,
operation,
);
});
}
fn mouse_interaction(
&self,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
) -> mouse::Interaction {
let viewport = layout.bounds();
self.overlay.as_widget().mouse_interaction(
self.tree,
layout.children().next().unwrap(),
cursor,
&viewport,
renderer,
)
}
}