use crate::base::Rect;
use crate::layout::Style;
use crate::reactive::Signal;
use crate::widgets::PasteAction;
use super::canvas::StyledCanvas;
use super::event::{EventCtx, KeyChord, Phase, UiEvent};
pub type DrawFn = Box<dyn FnMut(&mut dyn StyledCanvas, Rect)>;
pub type HandlerFn = Box<dyn FnMut(&mut EventCtx, &UiEvent)>;
pub type ShortcutFn = Box<dyn FnMut(&mut EventCtx)>;
pub(crate) struct Handler {
pub phase: Phase,
pub run: HandlerFn,
}
pub(crate) struct Shortcut {
pub chord: KeyChord,
pub label: Option<String>,
pub run: ShortcutFn,
}
pub enum ViewNode {
Element(Element),
Text(TextView),
Dyn(DynView),
}
pub struct View(pub(crate) ViewNode);
impl View {
pub(crate) fn for_each_style_mut(&mut self, f: &mut impl FnMut(&mut Style)) {
match &mut self.0 {
ViewNode::Element(el) => {
f(&mut el.style);
for child in &mut el.children {
child.for_each_style_mut(f);
}
}
ViewNode::Text(t) => f(&mut t.style),
ViewNode::Dyn(d) => f(&mut d.style),
}
}
}
pub struct TextView {
pub(crate) content: String,
pub(crate) style: Style,
}
pub struct DynView {
pub(crate) style: Style,
pub(crate) build: Box<dyn FnMut(crate::reactive::Scope) -> View>,
}
pub struct Element {
pub(crate) style: Style,
pub(crate) style_fn: Option<Box<dyn FnMut() -> Style>>,
pub(crate) measure: Option<crate::layout::MeasureFn>,
pub(crate) draw: Option<DrawFn>,
pub(crate) handlers: Vec<Handler>,
pub(crate) shortcuts: Vec<Shortcut>,
pub(crate) focusable: bool,
pub(crate) focus_trap: bool,
pub(crate) focus_memory: bool,
pub(crate) autofocus: bool,
pub(crate) probe_when_culled: bool,
pub(crate) padding_floor: Option<crate::layout::Edges>,
pub(super) access: super::access::AccessProps,
pub(crate) children: Vec<View>,
}
impl Element {
pub fn new() -> Element {
Element {
style: Style::default(),
style_fn: None,
measure: None,
draw: None,
handlers: Vec::new(),
shortcuts: Vec::new(),
focusable: false,
focus_trap: false,
focus_memory: false,
autofocus: false,
probe_when_culled: false,
padding_floor: None,
access: super::access::AccessProps::default(),
children: Vec::new(),
}
}
pub fn style(mut self, style: Style) -> Element {
self.style = style;
self
}
pub fn style_signal(mut self, f: impl FnMut() -> Style + 'static) -> Element {
self.style_fn = Some(Box::new(f));
self
}
pub fn draw(mut self, f: impl FnMut(&mut dyn StyledCanvas, Rect) + 'static) -> Element {
self.draw = Some(Box::new(f));
self
}
pub(crate) fn probe_when_culled(mut self) -> Element {
self.probe_when_culled = true;
self
}
pub fn measure(
mut self,
f: impl Fn(crate::base::Size) -> crate::base::Size + 'static,
) -> Element {
self.measure = Some(Box::new(f));
self
}
pub fn on(mut self, phase: Phase, f: impl FnMut(&mut EventCtx, &UiEvent) + 'static) -> Element {
self.handlers.push(Handler {
phase,
run: Box::new(f),
});
self
}
pub fn on_event(self, f: impl FnMut(&mut EventCtx, &UiEvent) + 'static) -> Element {
self.on(Phase::Bubble, f)
}
pub fn on_paste(self, mut f: impl FnMut(&str) -> PasteAction + 'static) -> Element {
self.on(Phase::Capture, move |ctx, ev| {
if let UiEvent::Paste(text) = ev {
match f(text) {
PasteAction::Consume => ctx.stop_propagation(),
PasteAction::Insert => {}
}
}
})
}
pub fn shortcut(mut self, chord: KeyChord, f: impl FnMut(&mut EventCtx) + 'static) -> Element {
self.shortcuts.push(Shortcut {
chord,
label: None,
run: Box::new(f),
});
self
}
pub fn shortcut_labeled(
mut self,
chord: KeyChord,
label: impl Into<String>,
f: impl FnMut(&mut EventCtx) + 'static,
) -> Element {
self.shortcuts.push(Shortcut {
chord,
label: Some(label.into()),
run: Box::new(f),
});
self
}
pub fn focusable(mut self) -> Element {
self.focusable = true;
self
}
pub fn focus_trap(mut self) -> Element {
self.focus_trap = true;
self
}
pub fn focus_memory(mut self) -> Element {
self.focus_memory = true;
self
}
pub fn autofocus(mut self) -> Element {
self.autofocus = true;
self.focusable = true;
self
}
pub fn padding_floor(mut self, floor: crate::layout::Edges) -> Element {
self.padding_floor = Some(floor);
self
}
pub fn role(mut self, role: super::access::Role) -> Element {
self.access.role = Some(role);
self
}
pub fn access_label(mut self, label: impl Into<String>) -> Element {
self.access.label = Some(label.into());
self
}
pub fn access_value(mut self, value: impl Fn() -> String + 'static) -> Element {
self.access.value = Some(std::rc::Rc::new(value));
self
}
pub fn hover_signal(self, hovered: Signal<bool>) -> Element {
self.on(Phase::Bubble, move |_ctx, ev| match ev {
UiEvent::MouseEnter => hovered.set(true),
UiEvent::MouseLeave => hovered.set(false),
_ => {}
})
}
pub fn focus_signal(self, focused: Signal<bool>) -> Element {
self.on(Phase::Bubble, move |_ctx, ev| match ev {
UiEvent::FocusIn => focused.set(true),
UiEvent::FocusOut => focused.set(false),
_ => {}
})
}
pub fn child(mut self, view: View) -> Element {
self.children.push(view);
self
}
pub fn children(mut self, views: impl IntoIterator<Item = View>) -> Element {
self.children.extend(views);
self
}
pub fn build(self) -> View {
View(ViewNode::Element(self))
}
}
impl Default for Element {
fn default() -> Self {
Element::new()
}
}
impl From<Element> for View {
fn from(e: Element) -> View {
e.build()
}
}
pub fn text(content: impl Into<String>) -> View {
View(ViewNode::Text(TextView {
content: content.into(),
style: Style::default(),
}))
}
pub fn styled_text(content: impl Into<String>, style: Style) -> View {
View(ViewNode::Text(TextView {
content: content.into(),
style,
}))
}
pub fn dyn_view(style: Style, mut build: impl FnMut() -> View + 'static) -> View {
View(ViewNode::Dyn(DynView {
style,
build: Box::new(move |_| build()),
}))
}
pub fn dyn_view_scoped(
style: Style,
build: impl FnMut(crate::reactive::Scope) -> View + 'static,
) -> View {
View(ViewNode::Dyn(DynView {
style,
build: Box::new(build),
}))
}