diff --git a/crates/gpui3/src/element.rs b/crates/gpui3/src/element.rs index 7e0e46cb260cfa7049f8072d57163166cc0ec5dd..435cedd4619b104dde77f1c8881a5b5835a8e55a 100644 --- a/crates/gpui3/src/element.rs +++ b/crates/gpui3/src/element.rs @@ -1,11 +1,15 @@ use crate::{ - BorrowWindow, Bounds, DispatchPhase, ElementId, FocusHandle, FocusListeners, LayoutId, - MouseDownEvent, Pixels, Point, Style, StyleRefinement, ViewContext, WindowContext, + BorrowWindow, Bounds, DispatchPhase, ElementId, FocusHandle, FocusListeners, KeyDownEvent, + KeyListener, KeyMatch, LayoutId, MouseClickEvent, MouseClickListener, MouseDownEvent, + MouseDownListener, MouseMoveEvent, MouseMoveListener, MouseUpEvent, MouseUpListener, Pixels, + Point, ScrollWheelEvent, ScrollWheelListener, Style, StyleRefinement, ViewContext, + WindowContext, }; use derive_more::{Deref, DerefMut}; +use parking_lot::Mutex; use refineable::Refineable; pub(crate) use smallvec::SmallVec; -use std::{marker::PhantomData, mem}; +use std::{any::TypeId, mem, sync::Arc}; pub trait Element: 'static + Send + Sync + IntoAnyElement { type ViewState: 'static + Send + Sync; @@ -40,19 +44,98 @@ pub trait Element: 'static + Send + Sync + IntoAnyElement { pub struct GlobalElementId(SmallVec<[ElementId; 32]>); pub trait ElementInteractivity: 'static + Send + Sync { + fn as_stateless(&self) -> &StatelessInteractivity; + fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity; fn as_stateful(&self) -> Option<&StatefulInteractivity>; + fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity>; + + fn paint( + &mut self, + bounds: Bounds, + pending_click: Arc>>, + 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); + }) + } + + if let Some(stateful) = self.as_stateful() { + let click_listeners = stateful.mouse_click_listeners.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()); + } + }); + }; + } + } fn initialize( - &self, + &mut self, cx: &mut ViewContext, - f: impl FnOnce(Option, &mut ViewContext) -> R, + f: impl FnOnce(&mut ViewContext) -> R, ) -> R { - if let Some(identified) = self.as_stateful() { - cx.with_element_id(identified.id.clone(), |global_id, cx| { - f(Some(global_id), cx) + 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 { - f(None, cx) + cx.with_key_listeners(&self.as_stateless().key_listeners, f) } } } @@ -62,7 +145,8 @@ pub struct StatefulInteractivity { pub id: ElementId, #[deref] #[deref_mut] - common: StatelessInteractivity, + stateless: StatelessInteractivity, + pub mouse_click_listeners: SmallVec<[MouseClickListener; 2]>, } impl ElementInteractivity for StatefulInteractivity @@ -72,6 +156,18 @@ where fn as_stateful(&self) -> Option<&StatefulInteractivity> { Some(self) } + + fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity> { + Some(self) + } + + fn as_stateless(&self) -> &StatelessInteractivity { + &self.stateless + } + + fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity { + &mut self.stateless + } } impl From for StatefulInteractivity @@ -81,16 +177,29 @@ where fn from(id: ElementId) -> Self { Self { id, - common: StatelessInteractivity::default(), + stateless: StatelessInteractivity::default(), + mouse_click_listeners: SmallVec::new(), } } } -pub struct StatelessInteractivity(PhantomData); +pub struct StatelessInteractivity { + 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]>, +} impl Default for StatelessInteractivity { fn default() -> Self { - Self(PhantomData) + 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(), + } } } @@ -101,6 +210,18 @@ where fn as_stateful(&self) -> Option<&StatefulInteractivity> { None } + + fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity> { + None + } + + fn as_stateless(&self) -> &StatelessInteractivity { + self + } + + fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity { + self + } } pub trait ElementFocusability: 'static + Send + Sync { diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 5621ece1c565a70c8f35dcd9252580ee3ecb713b..88c1f2e8fd13fadaa008988fb009089840af6a59 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -1,16 +1,16 @@ use crate::{ - Active, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element, + Active, AnyElement, AppContext, BorrowWindow, Bounds, DispatchPhase, Element, ElementFocusability, ElementId, ElementInteractivity, Focus, FocusHandle, FocusListeners, - Focusable, GlobalElementId, Hover, Interactive, Interactivity, IntoAnyElement, KeyDownEvent, - KeyMatch, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, NonFocusable, Overflow, - ParentElement, Pixels, Point, SharedString, StatefulInteractivity, StatelessInteractivity, + Focusable, GlobalElementId, Hover, IntoAnyElement, LayoutId, MouseDownEvent, MouseMoveEvent, + MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, Point, SharedString, + StatefulInteractivity, StatefullyInteractive, StatelessInteractivity, StatelesslyInteractive, Style, StyleRefinement, Styled, ViewContext, }; use collections::HashMap; use parking_lot::Mutex; use refineable::Refineable; use smallvec::SmallVec; -use std::{any::TypeId, mem, sync::Arc}; +use std::sync::Arc; #[derive(Default)] pub struct DivState { @@ -66,9 +66,8 @@ pub struct Div< I: ElementInteractivity = StatelessInteractivity, F: ElementFocusability = NonFocusable, > { - identity: I, + interactivity: I, focusability: F, - interactivity: Interactivity, children: SmallVec<[AnyElement; 2]>, group: Option, base_style: StyleRefinement, @@ -83,9 +82,8 @@ where V: 'static + Send + Sync, { Div { - identity: StatelessInteractivity::default(), + interactivity: StatelessInteractivity::default(), focusability: NonFocusable, - interactivity: Interactivity::default(), children: SmallVec::new(), group: None, base_style: StyleRefinement::default(), @@ -108,9 +106,8 @@ where { pub fn id(self, id: impl Into) -> Div, F> { Div { - identity: id.into().into(), + interactivity: id.into().into(), focusability: self.focusability, - interactivity: self.interactivity, children: self.children, group: self.group, base_style: self.base_style, @@ -277,7 +274,7 @@ where { pub fn focusable(self, handle: &FocusHandle) -> Div> { Div { - identity: self.identity, + interactivity: self.interactivity, focusability: handle.clone().into(), children: self.children, group: self.group, @@ -286,7 +283,6 @@ where group_hover: self.group_hover, active_style: self.active_style, group_active: self.group_active, - interactivity: self.interactivity, } } } @@ -327,7 +323,7 @@ where type ElementState = DivState; fn id(&self) -> Option { - self.identity + self.interactivity .as_stateful() .map(|identified| identified.id.clone()) } @@ -338,39 +334,14 @@ where element_state: Option, cx: &mut ViewContext, ) -> Self::ElementState { - self.with_element_id(cx, |this, global_id, cx| { - let element_state = element_state.unwrap_or_default(); - - let mut key_listeners = mem::take(&mut this.interactivity.key); - if let Some(global_id) = global_id { - 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 - }), - )); - } - - cx.with_key_listeners(&key_listeners, |cx| { - this.focusability.initialize(cx, |cx| { - for child in &mut this.children { - child.initialize(view_state, cx); - } - }); + self.interactivity.initialize(cx, |cx| { + self.focusability.initialize(cx, |cx| { + for child in &mut self.children { + child.initialize(view_state, cx); + } }); - this.interactivity.key = key_listeners; - - element_state - }) + }); + element_state.unwrap_or_default() } fn layout( @@ -490,14 +461,14 @@ where } } -impl Interactive for Div +impl StatelesslyInteractive for Div where I: ElementInteractivity, F: ElementFocusability, V: 'static + Send + Sync, { - fn interactivity(&mut self) -> &mut Interactivity { - &mut self.interactivity + fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity { + self.interactivity.as_stateless_mut() } } @@ -516,11 +487,14 @@ where } } -impl Click for Div, F> +impl StatefullyInteractive for Div, F> where F: ElementFocusability, V: 'static + Send + Sync, { + fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity { + &mut self.interactivity + } } impl Active for Div, F> diff --git a/crates/gpui3/src/elements/img.rs b/crates/gpui3/src/elements/img.rs index cf3ceda6144034c2b6e5e13d4db95675a0116637..5bf46f40df8f165da8c8f4fbe6427eb52a14a1f5 100644 --- a/crates/gpui3/src/elements/img.rs +++ b/crates/gpui3/src/elements/img.rs @@ -1,8 +1,8 @@ use crate::{ - div, Active, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element, - ElementFocusability, ElementId, ElementInteractivity, Focus, FocusListeners, Focusable, Hover, - Interactive, Interactivity, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString, - StatefulInteractivity, StatelessInteractivity, StyleRefinement, Styled, ViewContext, + div, Active, AnyElement, BorrowWindow, Bounds, Div, DivState, Element, ElementFocusability, + ElementId, ElementInteractivity, Focus, FocusListeners, Focusable, Hover, IntoAnyElement, + LayoutId, NonFocusable, Pixels, SharedString, StatefulInteractivity, StatefullyInteractive, + StatelessInteractivity, StatelesslyInteractive, StyleRefinement, Styled, ViewContext, }; use futures::FutureExt; use util::ResultExt; @@ -150,14 +150,14 @@ where } } -impl Interactive for Img +impl StatelesslyInteractive for Img where V: 'static + Send + Sync, I: ElementInteractivity, F: ElementFocusability, { - fn interactivity(&mut self) -> &mut Interactivity { - self.base.interactivity() + fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity { + self.base.stateless_interactivity() } } @@ -172,11 +172,14 @@ where } } -impl Click for Img, F> +impl StatefullyInteractive for Img, F> where V: 'static + Send + Sync, F: ElementFocusability, { + fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity { + self.base.stateful_interactivity() + } } impl Active for Img, F> diff --git a/crates/gpui3/src/elements/svg.rs b/crates/gpui3/src/elements/svg.rs index d29e9fbdf3c4218edc1fd30ada4a620aeff0ccf9..eed94cf28bb43c2565520e89173cfd96acfed502 100644 --- a/crates/gpui3/src/elements/svg.rs +++ b/crates/gpui3/src/elements/svg.rs @@ -1,8 +1,8 @@ use crate::{ - div, Active, AnyElement, Bounds, Click, Div, DivState, Element, ElementFocusability, ElementId, - ElementInteractivity, Focus, FocusListeners, Focusable, Hover, Interactive, Interactivity, - IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString, StatefulInteractivity, - StatelessInteractivity, StyleRefinement, Styled, ViewContext, + div, Active, AnyElement, Bounds, Div, DivState, Element, ElementFocusability, ElementId, + ElementInteractivity, Focus, FocusListeners, Focusable, Hover, IntoAnyElement, LayoutId, + NonFocusable, Pixels, SharedString, StatefulInteractivity, StatefullyInteractive, + StatelessInteractivity, StatelesslyInteractive, StyleRefinement, Styled, ViewContext, }; use util::ResultExt; @@ -124,14 +124,14 @@ where } } -impl Interactive for Svg +impl StatelesslyInteractive for Svg where V: 'static + Send + Sync, I: ElementInteractivity, F: ElementFocusability, { - fn interactivity(&mut self) -> &mut Interactivity { - self.base.interactivity() + fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity { + self.base.stateless_interactivity() } } @@ -146,11 +146,14 @@ where } } -impl Click for Svg, F> +impl StatefullyInteractive for Svg, F> where V: 'static + Send + Sync, F: ElementFocusability, { + fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity { + self.base.stateful_interactivity() + } } impl Active for Svg, F> diff --git a/crates/gpui3/src/interactive.rs b/crates/gpui3/src/interactive.rs index 9baae220ae04cd6b56356d00c127a7d15b7b0c27..656162adf358d3e24b64e5f7144c78d741fc919c 100644 --- a/crates/gpui3/src/interactive.rs +++ b/crates/gpui3/src/interactive.rs @@ -1,19 +1,15 @@ -use parking_lot::Mutex; -use smallvec::SmallVec; - use crate::{ point, Action, Bounds, DispatchContext, DispatchPhase, Element, FocusHandle, Keystroke, - Modifiers, Pixels, Point, ViewContext, + Modifiers, Pixels, Point, StatefulInteractivity, StatelessInteractivity, ViewContext, }; use std::{ any::{Any, TypeId}, - mem, ops::Deref, sync::Arc, }; -pub trait Interactive: Element { - fn interactivity(&mut self) -> &mut Interactivity; +pub trait StatelesslyInteractive: Element { + fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity; fn on_mouse_down( mut self, @@ -26,8 +22,8 @@ pub trait Interactive: Element { where Self: Sized, { - self.interactivity() - .mouse_down + self.stateless_interactivity() + .mouse_down_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && event.button == button @@ -50,8 +46,8 @@ pub trait Interactive: Element { where Self: Sized, { - self.interactivity() - .mouse_up + self.stateless_interactivity() + .mouse_up_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && event.button == button @@ -74,8 +70,8 @@ pub trait Interactive: Element { where Self: Sized, { - self.interactivity() - .mouse_down + self.stateless_interactivity() + .mouse_down_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Capture && event.button == button @@ -98,8 +94,8 @@ pub trait Interactive: Element { where Self: Sized, { - self.interactivity() - .mouse_up + self.stateless_interactivity() + .mouse_up_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Capture && event.button == button @@ -121,8 +117,8 @@ pub trait Interactive: Element { where Self: Sized, { - self.interactivity() - .mouse_move + self.stateless_interactivity() + .mouse_move_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { handler(view, event, cx); @@ -141,8 +137,8 @@ pub trait Interactive: Element { where Self: Sized, { - self.interactivity() - .scroll_wheel + self.stateless_interactivity() + .scroll_wheel_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { handler(view, event, cx); @@ -165,7 +161,7 @@ pub trait Interactive: Element { where Self: Sized, { - self.interactivity().key.push(( + self.stateless_interactivity().key_listeners.push(( TypeId::of::(), Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); @@ -186,7 +182,7 @@ pub trait Interactive: Element { where Self: Sized, { - self.interactivity().key.push(( + self.stateless_interactivity().key_listeners.push(( TypeId::of::(), Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); @@ -207,7 +203,7 @@ pub trait Interactive: Element { where Self: Sized, { - self.interactivity().key.push(( + self.stateless_interactivity().key_listeners.push(( TypeId::of::(), Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); @@ -219,7 +215,9 @@ pub trait Interactive: Element { } } -pub trait Click: Interactive { +pub trait StatefullyInteractive: StatelesslyInteractive { + fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity; + fn on_click( mut self, handler: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext) @@ -230,8 +228,8 @@ pub trait Click: Interactive { where Self: Sized, { - self.interactivity() - .mouse_click + self.stateful_interactivity() + .mouse_click_listeners .push(Arc::new(move |view, event, cx| handler(view, event, cx))); self } @@ -495,86 +493,3 @@ pub type KeyListener = Arc< + Sync + 'static, >; - -pub struct Interactivity { - pub mouse_down: SmallVec<[MouseDownListener; 2]>, - pub mouse_up: SmallVec<[MouseUpListener; 2]>, - pub mouse_click: SmallVec<[MouseClickListener; 2]>, - pub mouse_move: SmallVec<[MouseMoveListener; 2]>, - pub scroll_wheel: SmallVec<[ScrollWheelListener; 2]>, - pub key: SmallVec<[(TypeId, KeyListener); 32]>, -} - -impl Default for Interactivity { - fn default() -> Self { - Self { - mouse_down: SmallVec::new(), - mouse_up: SmallVec::new(), - mouse_click: SmallVec::new(), - mouse_move: SmallVec::new(), - scroll_wheel: SmallVec::new(), - key: SmallVec::new(), - } - } -} - -impl Interactivity -where - V: 'static + Send + Sync, -{ - pub fn paint( - &mut self, - bounds: Bounds, - pending_click: Arc>>, - cx: &mut ViewContext, - ) { - let click_listeners = mem::take(&mut self.mouse_click); - - 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()); - } - }); - } - - for listener in mem::take(&mut self.mouse_down) { - cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - - for listener in mem::take(&mut self.mouse_up) { - cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - - for listener in mem::take(&mut self.mouse_move) { - cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - - for listener in mem::take(&mut self.scroll_wheel) { - cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - } -} diff --git a/crates/ui2/src/components/icon_button.rs b/crates/ui2/src/components/icon_button.rs index ebe7d29e58a0559c8ea1f24c4ad2bdb720758f5d..8373530a0e4905b949950a7bbdc26f4603e34bcf 100644 --- a/crates/ui2/src/components/icon_button.rs +++ b/crates/ui2/src/components/icon_button.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use std::sync::Arc; -use gpui3::{Interactive, MouseButton}; +use gpui3::{MouseButton, StatelesslyInteractive}; use crate::{h_stack, prelude::*}; use crate::{theme, ClickHandler, Icon, IconColor, IconElement}; diff --git a/crates/ui2/src/elements/button.rs b/crates/ui2/src/elements/button.rs index 7d7040e18154ad7f185140430bc8ab63dfead727..c53081de3edf7a43f53edbce697a421b1a4280e5 100644 --- a/crates/ui2/src/elements/button.rs +++ b/crates/ui2/src/elements/button.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use std::sync::Arc; -use gpui3::{DefiniteLength, Hsla, Interactive, MouseButton, WindowContext}; +use gpui3::{DefiniteLength, Hsla, MouseButton, StatelesslyInteractive, WindowContext}; use crate::settings::user_settings; use crate::{h_stack, Icon, IconColor, IconElement, Label, LabelColor}; diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index 35b0cb2808da3cd9deed0075923d8685f7f2eb2d..85ed8a3a1f98bcf3c522566698819099b6be71a4 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -1,6 +1,6 @@ pub use gpui3::{ - div, Active, Click, Element, Hover, IntoAnyElement, ParentElement, ScrollState, SharedString, - Styled, ViewContext, WindowContext, + div, Click, Element, Hover, IntoAnyElement, ParentElement, ScrollState, SharedString, + StatefullyInteractivee, Styled, ViewContext, WindowContext, }; use crate::settings::user_settings;