use super::context::{EventCx, MeasureCx, PaintCx};
use super::{ClipboardKey, Flex, LayoutProps, Node, Widget, WidgetId};
use crate::event::Event;
use crate::geometry::{Rect, Size};
use crate::keymap::Scope;
pub(crate) trait Reached<Msg> {
fn id(&self) -> WidgetId;
fn layout(&self) -> LayoutProps;
fn event(&self, cx: &mut EventCx<'_, Msg>, event: &Event) -> bool;
fn paint_overlay(&self, cx: &mut PaintCx<'_>, anchor: Rect);
fn answer_action(&self, scope: Scope, action: &str) -> Option<Msg>;
fn answer_clipboard(&self, key: ClipboardKey) -> Option<Msg>;
}
impl<Msg: 'static> Reached<Msg> for &Node<Msg> {
fn id(&self) -> WidgetId {
self.id
}
fn layout(&self) -> LayoutProps {
self.layout
}
fn event(&self, cx: &mut EventCx<'_, Msg>, event: &Event) -> bool {
self.widget.event(cx, event)
}
fn paint_overlay(&self, cx: &mut PaintCx<'_>, anchor: Rect) {
self.widget.paint_overlay(cx, anchor);
}
fn answer_action(&self, scope: Scope, action: &str) -> Option<Msg> {
Node::answer_action(self, scope, action)
}
fn answer_clipboard(&self, key: ClipboardKey) -> Option<Msg> {
Node::answer_clipboard(self, key)
}
}
struct ReachedThrough<'a, Inner, Msg> {
inner: Box<dyn Reached<Inner> + 'a>,
map: &'a dyn Fn(Inner) -> Msg,
}
impl<Inner, Msg> Reached<Msg> for ReachedThrough<'_, Inner, Msg> {
fn id(&self) -> WidgetId {
self.inner.id()
}
fn layout(&self) -> LayoutProps {
self.inner.layout()
}
fn event(&self, cx: &mut EventCx<'_, Msg>, event: &Event) -> bool {
let (used, messages) = cx.with_messages(|inner| self.inner.event(inner, event));
for message in messages {
cx.emit((self.map)(message));
}
used
}
fn paint_overlay(&self, cx: &mut PaintCx<'_>, anchor: Rect) {
self.inner.paint_overlay(cx, anchor);
}
fn answer_action(&self, scope: Scope, action: &str) -> Option<Msg> {
self.inner.answer_action(scope, action).map(self.map)
}
fn answer_clipboard(&self, key: ClipboardKey) -> Option<Msg> {
self.inner.answer_clipboard(key).map(self.map)
}
}
struct Subtree<Inner, Msg> {
column: Flex<Inner>,
map: Box<dyn Fn(Inner) -> Msg>,
}
trait Erased<Msg> {
fn measure(&self, cx: &mut MeasureCx<'_>, available: Size) -> Size;
fn paint(&self, cx: &mut PaintCx<'_>, area: Rect);
fn assign_ids(&mut self, parent: WidgetId);
fn find(&self, id: WidgetId) -> Option<Box<dyn Reached<Msg> + '_>>;
fn count_focusable(&self) -> usize;
}
impl<Inner: 'static, Msg: 'static> Erased<Msg> for Subtree<Inner, Msg> {
fn measure(&self, cx: &mut MeasureCx<'_>, available: Size) -> Size {
self.column.measure(cx, available)
}
fn paint(&self, cx: &mut PaintCx<'_>, area: Rect) {
self.column.paint(cx, area);
}
fn assign_ids(&mut self, parent: WidgetId) {
for child in self.column.children_mut() {
child.assign_ids(parent);
}
}
fn find(&self, id: WidgetId) -> Option<Box<dyn Reached<Msg> + '_>> {
let found = self.column.children().iter().find_map(|child| child.find(id))?;
Some(Box::new(ReachedThrough { inner: found, map: &*self.map }))
}
fn count_focusable(&self) -> usize {
self.column.children().iter().map(Node::count_focusable).sum()
}
}
pub(crate) struct Mapped<Msg> {
subtree: Box<dyn Erased<Msg>>,
}
impl<Msg: 'static> Mapped<Msg> {
pub(crate) fn new<Inner: 'static>(children: Vec<Node<Inner>>, map: impl Fn(Inner) -> Msg + 'static) -> Self {
let column = Flex::new(super::Axis::Column, children);
Self { subtree: Box::new(Subtree { column, map: Box::new(map) }) }
}
pub(crate) fn assign_ids(&mut self, parent: WidgetId) {
self.subtree.assign_ids(parent);
}
pub(crate) fn find(&self, id: WidgetId) -> Option<Box<dyn Reached<Msg> + '_>> {
self.subtree.find(id)
}
pub(crate) fn count_focusable(&self) -> usize {
self.subtree.count_focusable()
}
}
impl<Msg: 'static> Widget<Msg> for Mapped<Msg> {
fn measure(&self, cx: &mut MeasureCx<'_>, available: Size) -> Size {
self.subtree.measure(cx, available)
}
fn paint(&self, cx: &mut PaintCx<'_>, area: Rect) {
self.subtree.paint(cx, area);
}
}