@@ -866,7 +866,8 @@ impl MutableAppContext {
fn remove_dropped_entities(&mut self) {
loop {
- let (dropped_models, dropped_views, dropped_values) = self.ctx.ref_counts.lock().take_dropped();
+ let (dropped_models, dropped_views, dropped_values) =
+ self.ctx.ref_counts.lock().take_dropped();
if dropped_models.is_empty() && dropped_views.is_empty() && dropped_values.is_empty() {
break;
}
@@ -1361,7 +1362,7 @@ impl AppContext {
&self.thread_pool
}
- pub fn value<T: 'static + Default, Tag: 'static>(&self, id: usize) -> ValueHandle<T> {
+ pub fn value<Tag: 'static, T: 'static + Default>(&self, id: usize) -> ValueHandle<T> {
let key = (TypeId::of::<Tag>(), id);
let mut values = self.values.lock();
values.entry(key).or_insert_with(|| Box::new(T::default()));
@@ -2439,7 +2440,13 @@ impl RefCounts {
}
}
- fn take_dropped(&mut self) -> (HashSet<usize>, HashSet<(usize, usize)>, HashSet<(TypeId, usize)>) {
+ fn take_dropped(
+ &mut self,
+ ) -> (
+ HashSet<usize>,
+ HashSet<(usize, usize)>,
+ HashSet<(TypeId, usize)>,
+ ) {
let mut dropped_models = HashSet::new();
let mut dropped_views = HashSet::new();
let mut dropped_values = HashSet::new();
@@ -0,0 +1,118 @@
+use crate::{
+ geometry::{rect::RectF, vector::Vector2F},
+ AfterLayoutContext, AppContext, DebugContext, Element, ElementBox, Event, EventContext,
+ LayoutContext, PaintContext, SizeConstraint, ValueHandle,
+};
+use serde_json::json;
+
+pub struct MouseEventHandler {
+ state: ValueHandle<MouseState>,
+ child: ElementBox,
+}
+
+#[derive(Clone, Copy, Default)]
+pub struct MouseState {
+ hovered: bool,
+ clicked: bool,
+}
+
+impl MouseEventHandler {
+ pub fn new<Tag: 'static>(
+ id: usize,
+ ctx: &AppContext,
+ render_child: impl FnOnce(MouseState) -> ElementBox,
+ ) -> Self {
+ let state = ctx.value::<Tag, _>(id);
+ let child = state.map(ctx, |state| render_child(*state));
+ Self { state, child }
+ }
+}
+
+impl Element for MouseEventHandler {
+ type LayoutState = ();
+
+ type PaintState = ();
+
+ fn layout(
+ &mut self,
+ constraint: SizeConstraint,
+ ctx: &mut LayoutContext,
+ ) -> (Vector2F, Self::LayoutState) {
+ (self.child.layout(constraint, ctx), ())
+ }
+
+ fn after_layout(
+ &mut self,
+ _: Vector2F,
+ _: &mut Self::LayoutState,
+ ctx: &mut AfterLayoutContext,
+ ) {
+ self.child.after_layout(ctx);
+ }
+
+ fn paint(
+ &mut self,
+ bounds: RectF,
+ _: &mut Self::LayoutState,
+ ctx: &mut PaintContext,
+ ) -> Self::PaintState {
+ self.child.paint(bounds.origin(), ctx);
+ }
+
+ fn dispatch_event(
+ &mut self,
+ event: &Event,
+ bounds: RectF,
+ _: &mut Self::LayoutState,
+ _: &mut Self::PaintState,
+ ctx: &mut EventContext,
+ ) -> bool {
+ self.state.map(ctx.app, |state| match event {
+ Event::MouseMoved { position } => {
+ let mouse_in = bounds.contains_point(*position);
+ if state.hovered != mouse_in {
+ state.hovered = mouse_in;
+ log::info!("hovered {}", state.hovered);
+ // ctx.notify();
+ true
+ } else {
+ false
+ }
+ }
+ Event::LeftMouseDown { position, .. } => {
+ if bounds.contains_point(*position) {
+ log::info!("clicked");
+ state.clicked = true;
+ // ctx.notify();
+ true
+ } else {
+ false
+ }
+ }
+ Event::LeftMouseUp { .. } => {
+ if state.clicked {
+ log::info!("unclicked");
+ state.clicked = false;
+ // ctx.notify();
+ true
+ } else {
+ false
+ }
+ }
+ _ => false,
+ })
+ }
+
+ fn debug(
+ &self,
+ _: RectF,
+ _: &Self::LayoutState,
+ _: &Self::PaintState,
+ ctx: &DebugContext,
+ ) -> serde_json::Value {
+ json!({
+ "type": "MouseEventHandler",
+ "child": self.child.debug(ctx),
+ })
+ }
+}
@@ -108,6 +108,12 @@ impl Event {
),
precise: native_event.hasPreciseScrollingDeltas() == YES,
}),
+ NSEventType::NSMouseMoved => window_height.map(|window_height| Self::MouseMoved {
+ position: vec2f(
+ native_event.locationInWindow().x as f32,
+ window_height - native_event.locationInWindow().y as f32,
+ ),
+ }),
_ => None,
}
}