From 766ee836b5b2ecaf5067f853c3e0a2fcb1862bb9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 20 Oct 2023 11:00:52 +0200 Subject: [PATCH] Checkpoint --- crates/gpui3/src/element.rs | 433 +------------------------------- crates/gpui3/src/focusable.rs | 98 +++++++- crates/gpui3/src/interactive.rs | 395 ++++++++++++++++++++++++++--- 3 files changed, 463 insertions(+), 463 deletions(-) diff --git a/crates/gpui3/src/element.rs b/crates/gpui3/src/element.rs index ee9501d21e838b2e31b6bba33da7de18ec9afc05..f360ae6231d0676056e7b05fac0327e936ff3b98 100644 --- a/crates/gpui3/src/element.rs +++ b/crates/gpui3/src/element.rs @@ -1,16 +1,7 @@ -use crate::{ - AppContext, BorrowWindow, Bounds, DispatchPhase, ElementId, FocusHandle, FocusListeners, - KeyDownEvent, KeyListener, KeyMatch, LayoutId, MouseClickEvent, MouseClickListener, - MouseDownEvent, MouseDownListener, MouseMoveEvent, MouseMoveListener, MouseUpEvent, - MouseUpListener, Pixels, Point, ScrollWheelEvent, ScrollWheelListener, SharedString, Style, - StyleRefinement, ViewContext, WindowContext, -}; -use collections::HashMap; +use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, ViewContext}; use derive_more::{Deref, DerefMut}; -use parking_lot::Mutex; -use refineable::Refineable; pub(crate) use smallvec::SmallVec; -use std::{any::TypeId, mem, sync::Arc}; +use std::mem; pub trait Element: 'static + Send + Sync + IntoAnyElement { type ViewState: 'static + Send + Sync; @@ -44,426 +35,6 @@ pub trait Element: 'static + Send + Sync + IntoAnyElement { #[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)] pub struct GlobalElementId(SmallVec<[ElementId; 32]>); -pub trait ElementInteraction: 'static + Send + Sync { - fn as_stateless(&self) -> &StatelessInteraction; - fn as_stateless_mut(&mut self) -> &mut StatelessInteraction; - fn as_stateful(&self) -> Option<&StatefulInteractivity>; - fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity>; - - fn initialize( - &mut self, - cx: &mut ViewContext, - f: impl FnOnce(&mut ViewContext) -> R, - ) -> R { - if let Some(stateful) = self.as_stateful_mut() { - cx.with_element_id(stateful.id.clone(), |global_id, cx| { - stateful.key_listeners.push(( - TypeId::of::(), - Arc::new(move |_, key_down, context, phase, cx| { - if phase == DispatchPhase::Bubble { - let key_down = key_down.downcast_ref::().unwrap(); - if let KeyMatch::Some(action) = - cx.match_keystroke(&global_id, &key_down.keystroke, context) - { - return Some(action); - } - } - - None - }), - )); - let result = stateful.stateless.initialize(cx, f); - stateful.key_listeners.pop(); - result - }) - } else { - cx.with_key_listeners(&self.as_stateless().key_listeners, f) - } - } - - fn refine_style( - &self, - style: &mut Style, - bounds: Bounds, - element_state: &InteractiveElementState, - cx: &mut ViewContext, - ) { - let mouse_position = cx.mouse_position(); - let stateless = self.as_stateless(); - if let Some(group_hover) = stateless.group_hover_style.as_ref() { - if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) { - if group_bounds.contains_point(&mouse_position) { - style.refine(&group_hover.style); - } - } - } - if bounds.contains_point(&mouse_position) { - style.refine(&stateless.hover_style); - } - - if let Some(stateful) = self.as_stateful() { - let active_state = element_state.active_state.lock(); - if active_state.group { - if let Some(group_style) = stateful.group_active_style.as_ref() { - style.refine(&group_style.style); - } - } - if active_state.element { - style.refine(&stateful.active_style); - } - } - } - - fn paint( - &mut self, - bounds: Bounds, - element_state: &InteractiveElementState, - cx: &mut ViewContext, - ) { - let stateless = self.as_stateless(); - for listener in stateless.mouse_down_listeners.iter().cloned() { - cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - - for listener in stateless.mouse_up_listeners.iter().cloned() { - cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - - for listener in stateless.mouse_move_listeners.iter().cloned() { - cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - - for listener in stateless.scroll_wheel_listeners.iter().cloned() { - cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - - let hover_group_bounds = stateless - .group_hover_style - .as_ref() - .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx)); - - if let Some(group_bounds) = hover_group_bounds { - paint_hover_listener(group_bounds, cx); - } - - if stateless.hover_style.is_some() { - paint_hover_listener(bounds, cx); - } - - if let Some(stateful) = self.as_stateful() { - let click_listeners = stateful.mouse_click_listeners.clone(); - - let pending_click = element_state.pending_click.clone(); - let mouse_down = pending_click.lock().clone(); - if let Some(mouse_down) = mouse_down { - cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| { - if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { - let mouse_click = MouseClickEvent { - down: mouse_down.clone(), - up: event.clone(), - }; - for listener in &click_listeners { - listener(state, &mouse_click, cx); - } - } - - *pending_click.lock() = None; - }); - } else { - cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| { - if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { - *pending_click.lock() = Some(event.clone()); - } - }); - } - - let active_state = element_state.active_state.clone(); - if active_state.lock().is_none() { - let active_group_bounds = stateful - .group_active_style - .as_ref() - .and_then(|group_active| GroupBounds::get(&group_active.group, cx)); - cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| { - if phase == DispatchPhase::Bubble { - let group = active_group_bounds - .map_or(false, |bounds| bounds.contains_point(&down.position)); - let element = bounds.contains_point(&down.position); - if group || element { - *active_state.lock() = ActiveState { group, element }; - cx.notify(); - } - } - }); - } else { - cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| { - if phase == DispatchPhase::Capture { - *active_state.lock() = ActiveState::default(); - cx.notify(); - } - }); - } - } - } -} - -fn paint_hover_listener(bounds: Bounds, cx: &mut ViewContext) -where - V: 'static + Send + Sync, -{ - let hovered = bounds.contains_point(&cx.mouse_position()); - cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| { - if phase == DispatchPhase::Capture { - if bounds.contains_point(&event.position) != hovered { - cx.notify(); - } - } - }); -} - -#[derive(Deref, DerefMut)] -pub struct StatefulInteractivity { - pub id: ElementId, - #[deref] - #[deref_mut] - stateless: StatelessInteraction, - pub mouse_click_listeners: SmallVec<[MouseClickListener; 2]>, - pub active_style: StyleRefinement, - pub group_active_style: Option, -} - -impl ElementInteraction for StatefulInteractivity -where - V: 'static + Send + Sync, -{ - fn as_stateful(&self) -> Option<&StatefulInteractivity> { - Some(self) - } - - fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity> { - Some(self) - } - - fn as_stateless(&self) -> &StatelessInteraction { - &self.stateless - } - - fn as_stateless_mut(&mut self) -> &mut StatelessInteraction { - &mut self.stateless - } -} - -impl From for StatefulInteractivity -where - V: 'static + Send + Sync, -{ - fn from(id: ElementId) -> Self { - Self { - id, - stateless: StatelessInteraction::default(), - mouse_click_listeners: SmallVec::new(), - active_style: StyleRefinement::default(), - group_active_style: None, - } - } -} - -pub struct StatelessInteraction { - pub mouse_down_listeners: SmallVec<[MouseDownListener; 2]>, - pub mouse_up_listeners: SmallVec<[MouseUpListener; 2]>, - pub mouse_move_listeners: SmallVec<[MouseMoveListener; 2]>, - pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener; 2]>, - pub key_listeners: SmallVec<[(TypeId, KeyListener); 32]>, - pub hover_style: StyleRefinement, - pub group_hover_style: Option, -} - -pub struct GroupStyle { - pub group: SharedString, - pub style: StyleRefinement, -} - -#[derive(Default)] -pub struct GroupBounds(HashMap; 1]>>); - -impl GroupBounds { - pub fn get(name: &SharedString, cx: &mut AppContext) -> Option> { - cx.default_global::() - .0 - .get(name) - .and_then(|bounds_stack| bounds_stack.last()) - .cloned() - } - - pub fn push(name: SharedString, bounds: Bounds, cx: &mut AppContext) { - cx.default_global::() - .0 - .entry(name) - .or_default() - .push(bounds); - } - - pub fn pop(name: &SharedString, cx: &mut AppContext) { - cx.default_global::() - .0 - .get_mut(name) - .unwrap() - .pop(); - } -} - -#[derive(Copy, Clone, Default, Eq, PartialEq)] -struct ActiveState { - pub group: bool, - pub element: bool, -} - -impl ActiveState { - pub fn is_none(&self) -> bool { - !self.group && !self.element - } -} - -#[derive(Default)] -pub struct InteractiveElementState { - active_state: Arc>, - pending_click: Arc>>, -} - -impl Default for StatelessInteraction { - fn default() -> Self { - Self { - mouse_down_listeners: SmallVec::new(), - mouse_up_listeners: SmallVec::new(), - mouse_move_listeners: SmallVec::new(), - scroll_wheel_listeners: SmallVec::new(), - key_listeners: SmallVec::new(), - hover_style: StyleRefinement::default(), - group_hover_style: None, - } - } -} - -impl ElementInteraction for StatelessInteraction -where - V: 'static + Send + Sync, -{ - fn as_stateful(&self) -> Option<&StatefulInteractivity> { - None - } - - fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity> { - None - } - - fn as_stateless(&self) -> &StatelessInteraction { - self - } - - fn as_stateless_mut(&mut self) -> &mut StatelessInteraction { - self - } -} - -pub trait ElementFocus: 'static + Send + Sync { - fn as_focusable(&self) -> Option<&FocusEnabled>; - - fn initialize( - &self, - cx: &mut ViewContext, - f: impl FnOnce(&mut ViewContext) -> R, - ) -> R { - if let Some(focusable) = self.as_focusable() { - for listener in focusable.focus_listeners.iter().cloned() { - cx.on_focus_changed(move |view, event, cx| listener(view, event, cx)); - } - cx.with_focus(focusable.focus_handle.clone(), |cx| f(cx)) - } else { - f(cx) - } - } - - fn refine_style(&self, style: &mut Style, cx: &WindowContext) { - if let Some(focusable) = self.as_focusable() { - if focusable.focus_handle.contains_focused(cx) { - style.refine(&focusable.focus_in_style); - } - - if focusable.focus_handle.within_focused(cx) { - style.refine(&focusable.in_focus_style); - } - - if focusable.focus_handle.is_focused(cx) { - style.refine(&focusable.focus_style); - } - } - } - - fn paint(&self, bounds: Bounds, cx: &mut WindowContext) { - if let Some(focusable) = self.as_focusable() { - let focus_handle = focusable.focus_handle.clone(); - cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| { - if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { - if !cx.default_prevented() { - cx.focus(&focus_handle); - cx.prevent_default(); - } - } - }) - } - } -} - -pub struct FocusEnabled { - pub focus_handle: FocusHandle, - pub focus_listeners: FocusListeners, - pub focus_style: StyleRefinement, - pub focus_in_style: StyleRefinement, - pub in_focus_style: StyleRefinement, -} - -impl ElementFocus for FocusEnabled -where - V: 'static + Send + Sync, -{ - fn as_focusable(&self) -> Option<&FocusEnabled> { - Some(self) - } -} - -impl From for FocusEnabled -where - V: 'static + Send + Sync, -{ - fn from(value: FocusHandle) -> Self { - Self { - focus_handle: value, - focus_listeners: FocusListeners::default(), - focus_style: StyleRefinement::default(), - focus_in_style: StyleRefinement::default(), - in_focus_style: StyleRefinement::default(), - } - } -} - -pub struct FocusDisabled; - -impl ElementFocus for FocusDisabled -where - V: 'static + Send + Sync, -{ - fn as_focusable(&self) -> Option<&FocusEnabled> { - None - } -} - pub trait ParentElement: Element { fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>; diff --git a/crates/gpui3/src/focusable.rs b/crates/gpui3/src/focusable.rs index 0665e5fb60c70671eaccfe2b0a88711f05759b12..3fcc9b330acaceeff90ead1bda488b344a557438 100644 --- a/crates/gpui3/src/focusable.rs +++ b/crates/gpui3/src/focusable.rs @@ -1,4 +1,8 @@ -use crate::{Element, FocusEvent, FocusHandle, StyleRefinement, ViewContext}; +use crate::{ + Bounds, DispatchPhase, Element, FocusEvent, FocusHandle, MouseDownEvent, Pixels, Style, + StyleRefinement, ViewContext, WindowContext, +}; +use refineable::Refineable; use smallvec::SmallVec; use std::sync::Arc; @@ -135,3 +139,95 @@ pub trait Focusable: Element { self } } + +pub trait ElementFocus: 'static + Send + Sync { + fn as_focusable(&self) -> Option<&FocusEnabled>; + + fn initialize( + &self, + cx: &mut ViewContext, + f: impl FnOnce(&mut ViewContext) -> R, + ) -> R { + if let Some(focusable) = self.as_focusable() { + for listener in focusable.focus_listeners.iter().cloned() { + cx.on_focus_changed(move |view, event, cx| listener(view, event, cx)); + } + cx.with_focus(focusable.focus_handle.clone(), |cx| f(cx)) + } else { + f(cx) + } + } + + fn refine_style(&self, style: &mut Style, cx: &WindowContext) { + if let Some(focusable) = self.as_focusable() { + if focusable.focus_handle.contains_focused(cx) { + style.refine(&focusable.focus_in_style); + } + + if focusable.focus_handle.within_focused(cx) { + style.refine(&focusable.in_focus_style); + } + + if focusable.focus_handle.is_focused(cx) { + style.refine(&focusable.focus_style); + } + } + } + + fn paint(&self, bounds: Bounds, cx: &mut WindowContext) { + if let Some(focusable) = self.as_focusable() { + let focus_handle = focusable.focus_handle.clone(); + cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { + if !cx.default_prevented() { + cx.focus(&focus_handle); + cx.prevent_default(); + } + } + }) + } + } +} + +pub struct FocusEnabled { + pub focus_handle: FocusHandle, + pub focus_listeners: FocusListeners, + pub focus_style: StyleRefinement, + pub focus_in_style: StyleRefinement, + pub in_focus_style: StyleRefinement, +} + +impl ElementFocus for FocusEnabled +where + V: 'static + Send + Sync, +{ + fn as_focusable(&self) -> Option<&FocusEnabled> { + Some(self) + } +} + +impl From for FocusEnabled +where + V: 'static + Send + Sync, +{ + fn from(value: FocusHandle) -> Self { + Self { + focus_handle: value, + focus_listeners: FocusListeners::default(), + focus_style: StyleRefinement::default(), + focus_in_style: StyleRefinement::default(), + in_focus_style: StyleRefinement::default(), + } + } +} + +pub struct FocusDisabled; + +impl ElementFocus for FocusDisabled +where + V: 'static + Send + Sync, +{ + fn as_focusable(&self) -> Option<&FocusEnabled> { + None + } +} diff --git a/crates/gpui3/src/interactive.rs b/crates/gpui3/src/interactive.rs index 6b7f2099f42b5d73c46fb7af66e8071496ae4e97..4cf0e3f0148398106873aff3de5dff6a6e015aae 100644 --- a/crates/gpui3/src/interactive.rs +++ b/crates/gpui3/src/interactive.rs @@ -1,8 +1,13 @@ use crate::{ - point, Action, Bounds, DispatchContext, DispatchPhase, Element, FocusHandle, GroupStyle, - Keystroke, Modifiers, Pixels, Point, SharedString, StatefulInteractivity, StatelessInteraction, + point, Action, AppContext, BorrowWindow, Bounds, DispatchContext, DispatchPhase, Element, + ElementId, FocusHandle, KeyMatch, Keystroke, Modifiers, Pixels, Point, SharedString, Style, StyleRefinement, ViewContext, }; +use collections::HashMap; +use derive_more::{Deref, DerefMut}; +use parking_lot::Mutex; +use refineable::Refineable; +use smallvec::SmallVec; use std::{ any::{Any, TypeId}, ops::Deref, @@ -12,6 +17,29 @@ use std::{ pub trait StatelessInteractive: Element { fn stateless_interactivity(&mut self) -> &mut StatelessInteraction; + fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self + where + Self: Sized, + { + self.stateless_interactivity().hover_style = f(StyleRefinement::default()); + self + } + + fn group_hover( + mut self, + group_name: impl Into, + f: impl FnOnce(StyleRefinement) -> StyleRefinement, + ) -> Self + where + Self: Sized, + { + self.stateless_interactivity().group_hover_style = Some(GroupStyle { + group: group_name.into(), + style: f(StyleRefinement::default()), + }); + self + } + fn on_mouse_down( mut self, button: MouseButton, @@ -148,29 +176,6 @@ pub trait StatelessInteractive: Element { self } - fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self - where - Self: Sized, - { - self.stateless_interactivity().hover_style = f(StyleRefinement::default()); - self - } - - fn group_hover( - mut self, - group_name: impl Into, - f: impl FnOnce(StyleRefinement) -> StyleRefinement, - ) -> Self - where - Self: Sized, - { - self.stateless_interactivity().group_hover_style = Some(GroupStyle { - group: group_name.into(), - style: f(StyleRefinement::default()), - }); - self - } - fn on_key_down( mut self, listener: impl Fn( @@ -196,9 +201,9 @@ pub trait StatelessInteractive: Element { self } - fn on_key_up( + fn on_action( mut self, - listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext) + listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, @@ -207,7 +212,7 @@ pub trait StatelessInteractive: Element { Self: Sized, { self.stateless_interactivity().key_listeners.push(( - TypeId::of::(), + TypeId::of::(), Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); listener(view, event, phase, cx); @@ -217,9 +222,9 @@ pub trait StatelessInteractive: Element { self } - fn on_action( + fn on_key_up( mut self, - listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext) + listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, @@ -228,7 +233,7 @@ pub trait StatelessInteractive: Element { Self: Sized, { self.stateless_interactivity().key_listeners.push(( - TypeId::of::(), + TypeId::of::(), Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); listener(view, event, phase, cx); @@ -282,6 +287,334 @@ pub trait StatefulInteractive: StatelessInteractive { } } +pub trait ElementInteraction: 'static + Send + Sync { + fn as_stateless(&self) -> &StatelessInteraction; + fn as_stateless_mut(&mut self) -> &mut StatelessInteraction; + fn as_stateful(&self) -> Option<&StatefulInteractivity>; + fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity>; + + fn initialize( + &mut self, + cx: &mut ViewContext, + f: impl FnOnce(&mut ViewContext) -> R, + ) -> R { + if let Some(stateful) = self.as_stateful_mut() { + cx.with_element_id(stateful.id.clone(), |global_id, cx| { + stateful.key_listeners.push(( + TypeId::of::(), + Arc::new(move |_, key_down, context, phase, cx| { + if phase == DispatchPhase::Bubble { + let key_down = key_down.downcast_ref::().unwrap(); + if let KeyMatch::Some(action) = + cx.match_keystroke(&global_id, &key_down.keystroke, context) + { + return Some(action); + } + } + + None + }), + )); + let result = stateful.stateless.initialize(cx, f); + stateful.key_listeners.pop(); + result + }) + } else { + cx.with_key_listeners(&self.as_stateless().key_listeners, f) + } + } + + fn refine_style( + &self, + style: &mut Style, + bounds: Bounds, + element_state: &InteractiveElementState, + cx: &mut ViewContext, + ) { + let mouse_position = cx.mouse_position(); + let stateless = self.as_stateless(); + if let Some(group_hover) = stateless.group_hover_style.as_ref() { + if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) { + if group_bounds.contains_point(&mouse_position) { + style.refine(&group_hover.style); + } + } + } + if bounds.contains_point(&mouse_position) { + style.refine(&stateless.hover_style); + } + + if let Some(stateful) = self.as_stateful() { + let active_state = element_state.active_state.lock(); + if active_state.group { + if let Some(group_style) = stateful.group_active_style.as_ref() { + style.refine(&group_style.style); + } + } + if active_state.element { + style.refine(&stateful.active_style); + } + } + } + + fn paint( + &mut self, + bounds: Bounds, + element_state: &InteractiveElementState, + cx: &mut ViewContext, + ) { + let stateless = self.as_stateless(); + for listener in stateless.mouse_down_listeners.iter().cloned() { + cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| { + listener(state, event, &bounds, phase, cx); + }) + } + + for listener in stateless.mouse_up_listeners.iter().cloned() { + cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| { + listener(state, event, &bounds, phase, cx); + }) + } + + for listener in stateless.mouse_move_listeners.iter().cloned() { + cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| { + listener(state, event, &bounds, phase, cx); + }) + } + + for listener in stateless.scroll_wheel_listeners.iter().cloned() { + cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| { + listener(state, event, &bounds, phase, cx); + }) + } + + let hover_group_bounds = stateless + .group_hover_style + .as_ref() + .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx)); + + if let Some(group_bounds) = hover_group_bounds { + paint_hover_listener(group_bounds, cx); + } + + if stateless.hover_style.is_some() { + paint_hover_listener(bounds, cx); + } + + if let Some(stateful) = self.as_stateful() { + let click_listeners = stateful.mouse_click_listeners.clone(); + + let pending_click = element_state.pending_click.clone(); + let mouse_down = pending_click.lock().clone(); + if let Some(mouse_down) = mouse_down { + cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { + let mouse_click = MouseClickEvent { + down: mouse_down.clone(), + up: event.clone(), + }; + for listener in &click_listeners { + listener(state, &mouse_click, cx); + } + } + + *pending_click.lock() = None; + }); + } else { + cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { + *pending_click.lock() = Some(event.clone()); + } + }); + } + + let active_state = element_state.active_state.clone(); + if active_state.lock().is_none() { + let active_group_bounds = stateful + .group_active_style + .as_ref() + .and_then(|group_active| GroupBounds::get(&group_active.group, cx)); + cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| { + if phase == DispatchPhase::Bubble { + let group = active_group_bounds + .map_or(false, |bounds| bounds.contains_point(&down.position)); + let element = bounds.contains_point(&down.position); + if group || element { + *active_state.lock() = ActiveState { group, element }; + cx.notify(); + } + } + }); + } else { + cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| { + if phase == DispatchPhase::Capture { + *active_state.lock() = ActiveState::default(); + cx.notify(); + } + }); + } + } + } +} + +fn paint_hover_listener(bounds: Bounds, cx: &mut ViewContext) +where + V: 'static + Send + Sync, +{ + let hovered = bounds.contains_point(&cx.mouse_position()); + cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| { + if phase == DispatchPhase::Capture { + if bounds.contains_point(&event.position) != hovered { + cx.notify(); + } + } + }); +} + +#[derive(Deref, DerefMut)] +pub struct StatefulInteractivity { + pub id: ElementId, + #[deref] + #[deref_mut] + stateless: StatelessInteraction, + pub mouse_click_listeners: SmallVec<[MouseClickListener; 2]>, + pub active_style: StyleRefinement, + pub group_active_style: Option, +} + +impl ElementInteraction for StatefulInteractivity +where + V: 'static + Send + Sync, +{ + fn as_stateful(&self) -> Option<&StatefulInteractivity> { + Some(self) + } + + fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity> { + Some(self) + } + + fn as_stateless(&self) -> &StatelessInteraction { + &self.stateless + } + + fn as_stateless_mut(&mut self) -> &mut StatelessInteraction { + &mut self.stateless + } +} + +impl From for StatefulInteractivity +where + V: 'static + Send + Sync, +{ + fn from(id: ElementId) -> Self { + Self { + id, + stateless: StatelessInteraction::default(), + mouse_click_listeners: SmallVec::new(), + active_style: StyleRefinement::default(), + group_active_style: None, + } + } +} + +pub struct StatelessInteraction { + pub mouse_down_listeners: SmallVec<[MouseDownListener; 2]>, + pub mouse_up_listeners: SmallVec<[MouseUpListener; 2]>, + pub mouse_move_listeners: SmallVec<[MouseMoveListener; 2]>, + pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener; 2]>, + pub key_listeners: SmallVec<[(TypeId, KeyListener); 32]>, + pub hover_style: StyleRefinement, + pub group_hover_style: Option, +} + +pub struct GroupStyle { + pub group: SharedString, + pub style: StyleRefinement, +} + +#[derive(Default)] +pub struct GroupBounds(HashMap; 1]>>); + +impl GroupBounds { + pub fn get(name: &SharedString, cx: &mut AppContext) -> Option> { + cx.default_global::() + .0 + .get(name) + .and_then(|bounds_stack| bounds_stack.last()) + .cloned() + } + + pub fn push(name: SharedString, bounds: Bounds, cx: &mut AppContext) { + cx.default_global::() + .0 + .entry(name) + .or_default() + .push(bounds); + } + + pub fn pop(name: &SharedString, cx: &mut AppContext) { + cx.default_global::() + .0 + .get_mut(name) + .unwrap() + .pop(); + } +} + +#[derive(Copy, Clone, Default, Eq, PartialEq)] +struct ActiveState { + pub group: bool, + pub element: bool, +} + +impl ActiveState { + pub fn is_none(&self) -> bool { + !self.group && !self.element + } +} + +#[derive(Default)] +pub struct InteractiveElementState { + active_state: Arc>, + pending_click: Arc>>, +} + +impl Default for StatelessInteraction { + fn default() -> Self { + Self { + mouse_down_listeners: SmallVec::new(), + mouse_up_listeners: SmallVec::new(), + mouse_move_listeners: SmallVec::new(), + scroll_wheel_listeners: SmallVec::new(), + key_listeners: SmallVec::new(), + hover_style: StyleRefinement::default(), + group_hover_style: None, + } + } +} + +impl ElementInteraction for StatelessInteraction +where + V: 'static + Send + Sync, +{ + fn as_stateful(&self) -> Option<&StatefulInteractivity> { + None + } + + fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity> { + None + } + + fn as_stateless(&self) -> &StatelessInteraction { + self + } + + fn as_stateless_mut(&mut self) -> &mut StatelessInteraction { + self + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub struct KeyDownEvent { pub keystroke: Keystroke,