1use crate::{
2 geometry::{rect::RectF, vector::Vector2F},
3 AfterLayoutContext, AppContext, DebugContext, Element, ElementBox, Event, EventContext,
4 LayoutContext, PaintContext, SizeConstraint, ValueHandle,
5};
6use serde_json::json;
7
8pub struct MouseEventHandler {
9 state: ValueHandle<MouseState>,
10 child: ElementBox,
11}
12
13#[derive(Clone, Copy, Default)]
14pub struct MouseState {
15 hovered: bool,
16 clicked: bool,
17}
18
19impl MouseEventHandler {
20 pub fn new<Tag: 'static>(
21 id: usize,
22 ctx: &AppContext,
23 render_child: impl FnOnce(MouseState) -> ElementBox,
24 ) -> Self {
25 let state = ctx.value::<Tag, _>(id);
26 let child = state.map(ctx, |state| render_child(*state));
27 Self { state, child }
28 }
29}
30
31impl Element for MouseEventHandler {
32 type LayoutState = ();
33
34 type PaintState = ();
35
36 fn layout(
37 &mut self,
38 constraint: SizeConstraint,
39 ctx: &mut LayoutContext,
40 ) -> (Vector2F, Self::LayoutState) {
41 (self.child.layout(constraint, ctx), ())
42 }
43
44 fn after_layout(
45 &mut self,
46 _: Vector2F,
47 _: &mut Self::LayoutState,
48 ctx: &mut AfterLayoutContext,
49 ) {
50 self.child.after_layout(ctx);
51 }
52
53 fn paint(
54 &mut self,
55 bounds: RectF,
56 _: &mut Self::LayoutState,
57 ctx: &mut PaintContext,
58 ) -> Self::PaintState {
59 self.child.paint(bounds.origin(), ctx);
60 }
61
62 fn dispatch_event(
63 &mut self,
64 event: &Event,
65 bounds: RectF,
66 _: &mut Self::LayoutState,
67 _: &mut Self::PaintState,
68 ctx: &mut EventContext,
69 ) -> bool {
70 self.state.map(ctx.app, |state| match event {
71 Event::MouseMoved { position } => {
72 let mouse_in = bounds.contains_point(*position);
73 if state.hovered != mouse_in {
74 state.hovered = mouse_in;
75 log::info!("hovered {}", state.hovered);
76 // ctx.notify();
77 true
78 } else {
79 false
80 }
81 }
82 Event::LeftMouseDown { position, .. } => {
83 if bounds.contains_point(*position) {
84 log::info!("clicked");
85 state.clicked = true;
86 // ctx.notify();
87 true
88 } else {
89 false
90 }
91 }
92 Event::LeftMouseUp { .. } => {
93 if state.clicked {
94 log::info!("unclicked");
95 state.clicked = false;
96 // ctx.notify();
97 true
98 } else {
99 false
100 }
101 }
102 _ => false,
103 })
104 }
105
106 fn debug(
107 &self,
108 _: RectF,
109 _: &Self::LayoutState,
110 _: &Self::PaintState,
111 ctx: &DebugContext,
112 ) -> serde_json::Value {
113 json!({
114 "type": "MouseEventHandler",
115 "child": self.child.debug(ctx),
116 })
117 }
118}