use crate::event::EventType;
use crate::layout::box_model::{IntrinsicSize, LayoutConstraint};
use crate::layout::measurable::{EmptyMeasure, FixedMeasure, Measurable, TextMeasure};
use crate::layout::style::FlexStyle;
use crate::view::paint::{ImageStyle, PaintStyle, TextStyle};
use std::rc::Rc;
use std::sync::Arc;
#[derive(Clone)]
pub enum Callback {
Simple(Rc<dyn Fn()>),
WithCtx(Rc<dyn Fn(&mut crate::event::EventContext)>),
}
impl std::fmt::Debug for Callback {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Callback").finish()
}
}
impl PartialEq for Callback {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Callback::Simple(a), Callback::Simple(b)) => Rc::ptr_eq(a, b),
(Callback::WithCtx(a), Callback::WithCtx(b)) => Rc::ptr_eq(a, b),
_ => false,
}
}
}
impl Eq for Callback {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Listener {
pub event: EventType,
pub callback: Callback,
pub kind: ListenerKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum ListenerKind {
BuiltIn,
#[default]
User,
}
impl Listener {
pub fn on_click(cb: Rc<dyn Fn()>) -> Self {
Self {
event: EventType::Click,
callback: Callback::Simple(Rc::clone(&cb)),
kind: ListenerKind::User,
}
}
pub fn on_click_with_ctx(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::Click,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_mouse_down(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::MouseDown,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_mouse_up(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::MouseUp,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_mouse_move(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::MouseMove,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_mouse_wheel(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::MouseWheel,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_mouse_enter(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::MouseEnter,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_mouse_leave(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::MouseLeave,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_drag_start(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::DragStart,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_drag_move(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::DragMove,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_drag_end(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::DragEnd,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_key_down(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::KeyDown,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_key_up(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::KeyUp,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_focus_in(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::FocusIn,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_focus_out(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::FocusOut,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_ime_preedit(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::ImePreedit,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_ime_commit(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::ImeCommit,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn on_ime_disabled(cb: Rc<dyn Fn(&mut crate::event::EventContext)>) -> Self {
Self {
event: EventType::ImeDisabled,
callback: Callback::WithCtx(cb),
kind: ListenerKind::User,
}
}
pub fn builtin(mut self) -> Self {
self.kind = ListenerKind::BuiltIn;
self
}
pub fn kind(&self) -> ListenerKind {
self.kind
}
}
pub fn listeners_sig_eq(a: &[Listener], b: &[Listener]) -> bool {
a.len() == b.len()
&& a.iter().zip(b.iter()).all(|(x, y)| {
x.event == y.event
&& x.kind == y.kind
&& matches!(
(&x.callback, &y.callback),
(Callback::Simple(_), Callback::Simple(_))
| (Callback::WithCtx(_), Callback::WithCtx(_))
)
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NodeType {
Text,
Image,
Div,
}
#[derive(Debug, Clone)]
pub enum ViewNode {
Text {
content: String,
style: TextStyle,
layout: FlexStyle,
key: Option<String>,
listeners: Vec<Listener>,
},
Image {
data: Arc<Vec<u8>>,
style: ImageStyle,
layout: FlexStyle,
key: Option<String>,
listeners: Vec<Listener>,
},
Div {
layout: FlexStyle,
paint: PaintStyle,
key: Option<String>,
children: Vec<ViewNode>,
listeners: Vec<Listener>,
},
}
impl ViewNode {
pub fn node_type(&self) -> NodeType {
match self {
ViewNode::Text { .. } => NodeType::Text,
ViewNode::Image { .. } => NodeType::Image,
ViewNode::Div { .. } => NodeType::Div,
}
}
pub fn type_name(&self) -> &'static str {
match self {
ViewNode::Text { .. } => "text",
ViewNode::Image { .. } => "image",
ViewNode::Div { .. } => "div",
}
}
pub fn key(&self) -> Option<&str> {
match self {
ViewNode::Text { key, .. }
| ViewNode::Image { key, .. }
| ViewNode::Div { key, .. } => key.as_deref(),
}
}
pub fn set_key(&mut self, k: String) {
match self {
ViewNode::Text { key, .. }
| ViewNode::Image { key, .. }
| ViewNode::Div { key, .. } => *key = Some(k),
}
}
pub fn add_listener(&mut self, l: Listener) {
match self {
ViewNode::Text { listeners, .. }
| ViewNode::Image { listeners, .. }
| ViewNode::Div { listeners, .. } => listeners.push(l),
}
}
pub fn with_listener(mut self, l: Listener) -> Self {
self.add_listener(l);
self
}
pub fn children(&self) -> &[ViewNode] {
match self {
ViewNode::Div { children, .. } => children,
_ => &[],
}
}
pub fn layout(&self) -> &FlexStyle {
match self {
ViewNode::Text { layout, .. }
| ViewNode::Image { layout, .. }
| ViewNode::Div { layout, .. } => layout,
}
}
pub fn measure(&self, constraint: &LayoutConstraint) -> IntrinsicSize {
match self {
ViewNode::Text { content, style, .. } => {
TextMeasure::new(content.clone(), style.font_size).measure(constraint)
}
ViewNode::Image { style, .. } => {
FixedMeasure::new(IntrinsicSize::new(style.width as f32, style.height as f32))
.measure(constraint)
}
ViewNode::Div { layout, .. } => {
use crate::layout::types::{Dimension as LayoutDimension, is_defined};
let w = layout.dim[LayoutDimension::Width as usize];
let h = layout.dim[LayoutDimension::Height as usize];
if is_defined(w) || is_defined(h) {
FixedMeasure::new(IntrinsicSize::new(
if is_defined(w) { w } else { 0.0 },
if is_defined(h) { h } else { 0.0 },
))
.measure(constraint)
} else {
EmptyMeasure.measure(constraint)
}
}
}
}
pub fn config_eq(&self, other: &Self) -> bool {
use ViewNode::*;
match (self, other) {
(
Text {
content: a,
style: b,
layout: l1,
listeners: l2,
..
},
Text {
content: x,
style: y,
layout: r1,
listeners: r2,
..
},
) => a == x && b == y && l1 == r1 && listeners_sig_eq(l2, r2),
(
Image {
data: a,
style: b,
layout: l1,
listeners: l2,
..
},
Image {
data: x,
style: y,
layout: r1,
listeners: r2,
..
},
) => a == x && b == y && l1 == r1 && listeners_sig_eq(l2, r2),
(
Div {
layout: l1,
paint: p1,
listeners: l2,
..
},
Div {
layout: r1,
paint: p2,
listeners: r2,
..
},
) => l1 == r1 && p1 == p2 && listeners_sig_eq(l2, r2),
_ => false,
}
}
pub fn is_container(&self) -> bool {
matches!(self, ViewNode::Div { .. })
}
pub fn tree_eq(&self, other: &Self) -> bool {
if !self.node_config_eq_cached(other) {
return false;
}
let a = self.children();
let b = other.children();
if a.len() != b.len() {
return false;
}
a.iter().zip(b.iter()).all(|(x, y)| x.tree_eq(y))
}
fn node_config_eq_cached(&self, other: &Self) -> bool {
use ViewNode::*;
match (self, other) {
(
Text {
content: a,
style: b,
layout: l1,
listeners: l2,
..
},
Text {
content: x,
style: y,
layout: r1,
listeners: r2,
..
},
) => a == x && b == y && l1 == r1 && listeners_sig_eq(l2, r2),
(
Image {
data: a,
style: b,
layout: l1,
listeners: l2,
..
},
Image {
data: x,
style: y,
layout: r1,
listeners: r2,
..
},
) => Arc::ptr_eq(a, x) && b == y && l1 == r1 && listeners_sig_eq(l2, r2),
(
Div {
layout: l1,
paint: p1,
listeners: l2,
..
},
Div {
layout: r1,
paint: p2,
listeners: r2,
..
},
) => l1 == r1 && p1 == p2 && listeners_sig_eq(l2, r2),
_ => false,
}
}
pub fn listeners(&self) -> &[Listener] {
match self {
ViewNode::Text { listeners, .. }
| ViewNode::Image { listeners, .. }
| ViewNode::Div { listeners, .. } => listeners,
}
}
pub fn click_listeners(&self) -> Vec<&Listener> {
self.listeners()
.iter()
.filter(|l| l.event == EventType::Click)
.collect()
}
pub fn is_interactive(&self) -> bool {
match self {
ViewNode::Div { paint, .. } => {
paint.hover_background.is_some() || paint.pressed_background.is_some()
}
ViewNode::Text { style, .. } => {
style.hover_color.is_some() || style.pressed_color.is_some()
}
ViewNode::Image { .. } => false,
}
}
}