Compare commits

...

5 Commits

Author SHA1 Message Date
mgsloan@gmail.com
6cfd1386d9 WIP progress moving ui crate to concurrent arena gc 2024-11-21 12:53:11 -07:00
mgsloan@gmail.com
ccfa29d78b Require elements implement Send for concurrent dropping in gpui
Pairing with Antonio revealed an oversight on my part - Drop may be
unsafe if a value is not `Send`. This commit pushes the change to
require `Send` through `gpui` but not beyond
2024-11-21 11:50:24 -07:00
mgsloan@gmail.com
a76d3950f4 Implement concurrent trash dropping which seems to work!
Co-authored-by: Bennet <bennet@zed.dev>
2024-11-21 11:38:21 -07:00
mgsloan@gmail.com
3afa772a1b Initial attempt at concurrent gc for elements arena 2024-11-19 23:05:59 -07:00
mgsloan@gmail.com
5c22c8d03b Store drop metadata within arena alloc instead of separate Vec
This is in preparation for supporting concurrent drop via an
implementation using a circular queue.

On its own this isn't really a clear improvement. May improve cache
locality, saves a few intermediate Vec allocations. Probably costs 8
more bytes per element.
2024-11-19 13:10:26 -08:00
32 changed files with 702 additions and 390 deletions

1
Cargo.lock generated
View File

@@ -13108,6 +13108,7 @@ dependencies = [
"gpui",
"itertools 0.13.0",
"menu",
"parking_lot",
"serde",
"settings",
"smallvec",

View File

@@ -5,7 +5,7 @@ struct SubWindow {
custom_titlebar: bool,
}
fn button(text: &str, on_click: impl Fn(&mut WindowContext) + 'static) -> impl IntoElement {
fn button(text: &str, on_click: impl Send + Fn(&mut WindowContext) + 'static) -> impl IntoElement {
div()
.id(SharedString::from(text.to_string()))
.flex_none()

View File

@@ -39,7 +39,7 @@ use std::any::{Any, TypeId};
/// }
/// register_action!(Paste);
/// ```
pub trait Action: 'static + Send {
pub trait Action: 'static + Send + Sync {
/// Clone the action into a new box
fn boxed_clone(&self) -> Box<dyn Action>;

View File

@@ -1,32 +1,64 @@
use parking_lot::Mutex;
use std::{
alloc,
cell::Cell,
ops::{Deref, DerefMut},
ptr,
rc::Rc,
sync::{
atomic::{self, AtomicBool, AtomicPtr},
Arc,
},
thread::sleep,
time::Duration,
};
struct ArenaElement {
value: *mut u8,
drop: unsafe fn(*mut u8),
/// An arena allocator with the following properties:
///
/// * Support for mixed-type values.
/// * Can only allocate from one thread.
/// * Can invalidate + trash all current values in constant time.
/// * Support for concurrently dropping trash from a thread other than the allocating thread.
///
/// The internal layout of is a circular buffer (ring buffer) that looks like this:
///
/// ```
/// < tttttttttttttvvvvvvvvvvv >
/// ^ ^ ^ ^ ^
/// start trash_start trash_end next_write end
/// ```
///
/// Illustrating the circular nature of the buffer where the values wrap around:
///
/// ```
/// <vvvvvvv tttttttttttvvvvvvvvvvvv>
/// ^ ^ ^ ^ ^
/// start next_write trash_start trash_end end
/// ```
pub struct Arena {
shared: Arc<SharedState>,
next_write: *mut u8,
valid: Arc<AtomicBool>,
}
impl Drop for ArenaElement {
#[inline(always)]
fn drop(&mut self) {
unsafe {
(self.drop)(self.value);
}
/// Mutable state shared by `Arena` and `TrashHandle`.
struct SharedState {
start: AtomicPtr<u8>,
end: AtomicPtr<u8>,
trash_start: AtomicPtr<u8>,
trash_end: AtomicPtr<u8>,
drop_trash_is_running: Mutex<()>,
}
/// `TrashHandle` is used to drop trashed values from the arena. It can be used from threads other
/// than the allocating thread.
pub struct TrashHandle(Arc<SharedState>);
impl TrashHandle {
pub fn drop_trash(&self) {
self.0.drop_trash_if_not_already_running();
}
}
pub struct Arena {
start: *mut u8,
end: *mut u8,
offset: *mut u8,
elements: Vec<ArenaElement>,
valid: Rc<Cell<bool>>,
}
// FIXME: spawn a thread and do park / unpark to control it?
impl Arena {
pub fn new(size_in_bytes: usize) -> Self {
@@ -34,79 +66,221 @@ impl Arena {
let layout = alloc::Layout::from_size_align(size_in_bytes, 1).unwrap();
let start = alloc::alloc(layout);
let end = start.add(size_in_bytes);
let shared = Arc::new(SharedState {
start: AtomicPtr::new(start),
end: AtomicPtr::new(end),
trash_start: AtomicPtr::new(start),
trash_end: AtomicPtr::new(start),
drop_trash_is_running: Mutex::new(()),
});
Self {
start,
end,
offset: start,
elements: Vec::new(),
valid: Rc::new(Cell::new(true)),
shared,
next_write: start,
valid: Arc::new(AtomicBool::new(true)),
}
}
}
pub fn len(&self) -> usize {
self.offset as usize - self.start as usize
self.next_write as usize - self.shared.start() as usize
}
pub fn capacity(&self) -> usize {
self.end as usize - self.start as usize
self.shared.end() as usize - self.shared.start() as usize
}
pub fn clear(&mut self) {
self.valid.set(false);
self.valid = Rc::new(Cell::new(true));
self.elements.clear();
self.offset = self.start;
pub fn trash_everything(&mut self) -> TrashHandle {
self.valid.store(false, atomic::Ordering::SeqCst);
self.valid = Arc::new(AtomicBool::new(true));
self.shared
.trash_end
.store(self.next_write, atomic::Ordering::Relaxed);
TrashHandle(self.shared.clone())
}
#[inline(always)]
pub fn alloc<T>(&mut self, f: impl FnOnce() -> T) -> ArenaBox<T> {
#[inline(always)]
unsafe fn inner_writer<T, F>(ptr: *mut T, f: F)
where
F: FnOnce() -> T,
{
ptr::write(ptr, f());
}
pub fn alloc<T>(&mut self, f: impl FnOnce() -> T) -> ArenaBox<T>
where
T: Send,
{
unsafe fn drop<T>(ptr: *mut u8) {
std::ptr::drop_in_place(ptr.cast::<T>());
ptr::drop_in_place(ptr.cast::<T>());
}
unsafe {
let layout = alloc::Layout::new::<T>();
let offset = self.offset.add(self.offset.align_offset(layout.align()));
let next_offset = offset.add(layout.size());
assert!(next_offset <= self.end, "not enough space in Arena");
let (header_ptr, offset) =
self.get_wrapped_allocation_ptrs::<ArenaElementHeader>(self.next_write);
let (value_ptr, offset) = self.get_wrapped_allocation_ptrs::<T>(offset);
assert!(offset <= self.shared.end(), "not enough space in Arena");
let trash_start = self.shared.trash_start();
let trash_end = self.shared.trash_end();
assert!(
// FIXME: this is broken.
trash_start == trash_end
|| ptr_is_within_circular_interval(offset, self.next_write, trash_start),
"not enough space in Arena"
);
self.shared.ensure_no_trash_before(offset);
let result = ArenaBox {
ptr: offset.cast(),
ptr::write(
header_ptr,
ArenaElementHeader {
next_element: offset,
value: AtomicPtr::new(value_ptr.cast()),
drop: drop::<T>,
},
);
ptr::write(value_ptr, f());
self.next_write = offset;
ArenaBox {
ptr: value_ptr,
valid: self.valid.clone(),
};
}
}
}
inner_writer(result.ptr, f);
self.elements.push(ArenaElement {
value: offset,
drop: drop::<T>,
});
self.offset = next_offset;
result
#[inline(always)]
unsafe fn get_wrapped_allocation_ptrs<T>(&self, offset: *mut u8) -> (*mut T, *mut u8) {
let (ptr, next_offset) = get_allocation_ptrs::<T>(offset);
if next_offset < self.shared.end() {
(ptr, next_offset)
} else if next_offset == self.shared.end() {
(ptr, self.shared.start())
} else {
get_allocation_ptrs::<T>(self.shared.start())
}
}
}
impl Drop for Arena {
impl Drop for SharedState {
fn drop(&mut self) {
self.clear();
self.drop_trash_ensure_finishes();
}
}
impl SharedState {
fn drop_trash_if_not_already_running(&self) {
self.drop_trash_impl(false);
}
fn drop_trash_ensure_finishes(&self) {
self.drop_trash_impl(true);
}
fn drop_trash_impl(&self, ensure_finishes: bool) {
if let Some(_guard) = self.drop_trash_is_running.try_lock() {
let trash_start = self.trash_start();
let mut offset = self.trash_end();
// trash_end is re-queried as this might run concurrently with multiple trash_everything calls.
//
// FIXME: what if there is nothing to trash / nothing was allocated?
while ptr_is_within_circular_interval(offset, trash_start, self.trash_end()) {
unsafe {
offset = drop_arena_element(offset);
}
self.trash_start.store(offset, atomic::Ordering::Relaxed);
}
} else if ensure_finishes {
unsafe {
// If we can't get the lock, help out with trashing.
self.ensure_no_trash_before(self.trash_end());
}
}
}
unsafe fn ensure_no_trash_before(&self, target: *mut u8) {
let mut offset = self.trash_start();
let mut trash_end = self.trash_end();
if ptr_is_within_circular_interval(target, offset, trash_end) {
log::warn!("Unexpectedly needed to clear element arena trash on main thread.");
while ptr_is_within_circular_interval(target, offset, trash_end) {
// Note that it is possible for the background thread to increment trash_start
// beyond this offset while the drop is still doing work. This is fine because this
// code is run on the same thread as allocations, so allocations will only see a valid
// trash_start.
offset = drop_arena_element(offset);
}
}
// FIXME: won't make progress if background thread is starved. If ArenaElementHeader stores
// a state indicating that the drop has happened this can possibly be fixed.
if ptr_is_within_circular_interval(target, self.trash_start(), trash_end) {
log::error!(
"Needed to spin waiting for background thread to clear element arena trash."
);
while ptr_is_within_circular_interval(target, self.trash_start(), trash_end) {
sleep(Duration::from_micros(50));
// std::hint::spin_loop();
}
}
}
fn start(&self) -> *mut u8 {
self.start.load(atomic::Ordering::Relaxed)
}
fn end(&self) -> *mut u8 {
self.end.load(atomic::Ordering::Relaxed)
}
fn trash_start(&self) -> *mut u8 {
self.trash_start.load(atomic::Ordering::Relaxed)
}
fn trash_end(&self) -> *mut u8 {
self.trash_end.load(atomic::Ordering::Relaxed)
}
}
#[inline(always)]
unsafe fn get_allocation_ptrs<T>(offset: *mut u8) -> (*mut T, *mut u8) {
let data_layout = alloc::Layout::new::<T>();
let offset = offset.add(offset.align_offset(data_layout.align()));
let data_ptr = offset.cast::<T>();
(data_ptr, offset.add(data_layout.size()))
}
// FIXME: many uses of this only actively use one of the bounds.
//
// FIXME: start == end could either mean everything is included or nothing. Here it is nothing
// included, but are there corner cases where the actual state is everything?
#[inline(always)]
fn ptr_is_within_circular_interval<T>(ptr: *const T, start: *const T, end: *const T) -> bool {
if end < start {
ptr >= start || ptr < end
} else {
ptr >= start && ptr < end
}
}
struct ArenaElementHeader {
next_element: *mut u8,
value: AtomicPtr<u8>,
drop: unsafe fn(*mut u8),
}
unsafe fn drop_arena_element(offset: *mut u8) -> *mut u8 {
let (header, _) = get_allocation_ptrs::<ArenaElementHeader>(offset);
let value = (*header)
.value
.swap(ptr::null_mut(), atomic::Ordering::AcqRel);
if value.is_null() {
return (*header).next_element;
}
let drop_fn = (*header).drop;
(drop_fn)(value);
(*header).next_element
}
pub struct ArenaBox<T: ?Sized> {
ptr: *mut T,
valid: Rc<Cell<bool>>,
valid: Arc<AtomicBool>,
}
unsafe impl<T: ?Sized + Send> Send for ArenaBox<T> {}
impl<T: ?Sized> ArenaBox<T> {
#[inline(always)]
pub fn map<U: ?Sized>(mut self, f: impl FnOnce(&mut T) -> &mut U) -> ArenaBox<U> {
@@ -118,7 +292,7 @@ impl<T: ?Sized> ArenaBox<T> {
fn validate(&self) {
assert!(
self.valid.get(),
self.valid.load(atomic::Ordering::SeqCst),
"attempted to dereference an ArenaRef after its Arena was cleared"
);
}
@@ -170,9 +344,8 @@ impl<T: ?Sized> Deref for ArenaRef<T> {
#[cfg(test)]
mod tests {
use std::{cell::Cell, rc::Rc};
use super::*;
use atomic::AtomicBool;
#[test]
fn test_arena() {
@@ -186,7 +359,7 @@ mod tests {
assert_eq!(*c, 3);
assert_eq!(*d, 4);
arena.clear();
arena.trash_everything().drop_trash();
let a = arena.alloc(|| 5u64);
let b = arena.alloc(|| 6u32);
let c = arena.alloc(|| 7u16);
@@ -197,16 +370,16 @@ mod tests {
assert_eq!(*d, 8);
// Ensure drop gets called.
let dropped = Rc::new(Cell::new(false));
struct DropGuard(Rc<Cell<bool>>);
let dropped = Arc::new(AtomicBool::new(false));
struct DropGuard(Arc<AtomicBool>);
impl Drop for DropGuard {
fn drop(&mut self) {
self.0.set(true);
self.0.store(true, atomic::Ordering::SeqCst);
}
}
arena.alloc(|| DropGuard(dropped.clone()));
arena.clear();
assert!(dropped.get());
arena.trash_everything().drop_trash();
assert!(dropped.load(atomic::Ordering::SeqCst));
}
#[test]
@@ -241,10 +414,10 @@ mod tests {
#[test]
#[should_panic(expected = "attempted to dereference an ArenaRef after its Arena was cleared")]
fn test_arena_use_after_clear() {
let mut arena = Arena::new(16);
let mut arena = Arena::new(256);
let value = arena.alloc(|| 1u64);
arena.clear();
arena.trash_everything().drop_trash();
let _read_value = *value;
}
}

View File

@@ -43,14 +43,14 @@ use std::{any::Any, fmt::Debug, mem};
/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
/// You can create custom elements by implementing this trait, see the module-level documentation
/// for more details.
pub trait Element: 'static + IntoElement {
pub trait Element: 'static + IntoElement + Send {
/// The type of state returned from [`Element::request_layout`]. A mutable reference to this state is subsequently
/// provided to [`Element::prepaint`] and [`Element::paint`].
type RequestLayoutState: 'static;
type RequestLayoutState: 'static + Send;
/// The type of state returned from [`Element::prepaint`]. A mutable reference to this state is subsequently
/// provided to [`Element::paint`].
type PrepaintState: 'static;
type PrepaintState: 'static + Send;
/// If this element has a unique identifier, return it here. This is used to track elements across frames, and
/// will cause a GlobalElementId to be passed to the request_layout, prepaint, and paint methods.
@@ -95,7 +95,7 @@ pub trait Element: 'static + IntoElement {
}
/// Implemented by any type that can be converted into an element.
pub trait IntoElement: Sized {
pub trait IntoElement: Sized + Send {
/// The specific type of element into which the implementing type is converted.
/// Useful for converting other types into elements automatically, like Strings
type Element: Element;
@@ -129,7 +129,7 @@ impl Render for Empty {
/// components as a recipe for a certain pattern of elements. RenderOnce allows
/// you to invoke this pattern, without breaking the fluent builder pattern of
/// the element APIs.
pub trait RenderOnce: 'static {
pub trait RenderOnce: 'static + Send {
/// Render this component into an element tree. Note that this method
/// takes ownership of self, as compared to [`Render::render()`] method
/// which takes a mutable reference.
@@ -225,7 +225,7 @@ impl<C: RenderOnce> IntoElement for Component<C> {
#[derive(Deref, DerefMut, Default, Debug, Eq, PartialEq, Hash)]
pub struct GlobalElementId(pub(crate) SmallVec<[ElementId; 32]>);
trait ElementObject {
trait ElementObject: Send {
fn inner_element(&mut self) -> &mut dyn Any;
fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
@@ -471,7 +471,7 @@ pub struct AnyElement(ArenaBox<dyn ElementObject>);
impl AnyElement {
pub(crate) fn new<E>(element: E) -> Self
where
E: 'static + Element,
E: 'static + Element + Send,
E::RequestLayoutState: Any,
{
let element = ELEMENT_ARENA

View File

@@ -12,7 +12,7 @@ pub struct Animation {
pub oneshot: bool,
/// A function that takes a delta between 0 and 1 and returns a new delta
/// between 0 and 1 based on the given easing function.
pub easing: Box<dyn Fn(f32) -> f32>,
pub easing: Box<dyn Send + Fn(f32) -> f32>,
}
impl Animation {
@@ -35,7 +35,7 @@ impl Animation {
/// Set the easing function to use for this animation.
/// The easing function will take a time delta between 0 and 1 and return a new delta
/// between 0 and 1
pub fn with_easing(mut self, easing: impl Fn(f32) -> f32 + 'static) -> Self {
pub fn with_easing(mut self, easing: impl Send + Fn(f32) -> f32 + 'static) -> Self {
self.easing = Box::new(easing);
self
}
@@ -48,7 +48,7 @@ pub trait AnimationExt {
self,
id: impl Into<ElementId>,
animation: Animation,
animator: impl Fn(Self, f32) -> Self + 'static,
animator: impl Send + Fn(Self, f32) -> Self + 'static,
) -> AnimationElement<Self>
where
Self: Sized,
@@ -69,7 +69,7 @@ pub struct AnimationElement<E> {
id: ElementId,
element: Option<E>,
animation: Animation,
animator: Box<dyn Fn(E, f32) -> E + 'static>,
animator: Box<dyn Send + Fn(E, f32) -> E + 'static>,
}
impl<E> AnimationElement<E> {

View File

@@ -8,8 +8,8 @@ use crate::{
/// Construct a canvas element with the given paint callback.
/// Useful for adding short term custom drawing to a view.
pub fn canvas<T>(
prepaint: impl 'static + FnOnce(Bounds<Pixels>, &mut WindowContext) -> T,
paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut WindowContext),
prepaint: impl 'static + Send + FnOnce(Bounds<Pixels>, &mut WindowContext) -> T,
paint: impl 'static + Send + FnOnce(Bounds<Pixels>, T, &mut WindowContext),
) -> Canvas<T> {
Canvas {
prepaint: Some(Box::new(prepaint)),
@@ -21,12 +21,12 @@ pub fn canvas<T>(
/// A canvas element, meant for accessing the low level paint API without defining a whole
/// custom element
pub struct Canvas<T> {
prepaint: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut WindowContext) -> T>>,
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut WindowContext)>>,
prepaint: Option<Box<dyn Send + FnOnce(Bounds<Pixels>, &mut WindowContext) -> T>>,
paint: Option<Box<dyn Send + FnOnce(Bounds<Pixels>, T, &mut WindowContext)>>,
style: StyleRefinement,
}
impl<T: 'static> IntoElement for Canvas<T> {
impl<T: 'static + Send> IntoElement for Canvas<T> {
type Element = Self;
fn into_element(self) -> Self::Element {
@@ -34,7 +34,7 @@ impl<T: 'static> IntoElement for Canvas<T> {
}
}
impl<T: 'static> Element for Canvas<T> {
impl<T: 'static + Send> Element for Canvas<T> {
type RequestLayoutState = Style;
type PrepaintState = Option<T>;

View File

@@ -24,17 +24,17 @@ use crate::{
StyleRefinement, Styled, Task, TooltipId, View, Visibility, WindowContext,
};
use collections::HashMap;
use parking_lot::Mutex;
use refineable::Refineable;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
cell::RefCell,
cmp::Ordering,
fmt::Debug,
marker::PhantomData,
mem,
ops::DerefMut,
rc::Rc,
sync::Arc,
time::Duration,
};
use taffy::style::Overflow;
@@ -81,7 +81,7 @@ impl Interactivity {
pub fn on_mouse_down(
&mut self,
button: MouseButton,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) {
self.mouse_down_listeners
.push(Box::new(move |event, phase, hitbox, cx| {
@@ -98,7 +98,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn capture_any_mouse_down(
&mut self,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) {
self.mouse_down_listeners
.push(Box::new(move |event, phase, hitbox, cx| {
@@ -114,7 +114,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_any_mouse_down(
&mut self,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) {
self.mouse_down_listeners
.push(Box::new(move |event, phase, hitbox, cx| {
@@ -131,7 +131,7 @@ impl Interactivity {
pub fn on_mouse_up(
&mut self,
button: MouseButton,
listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseUpEvent, &mut WindowContext) + 'static,
) {
self.mouse_up_listeners
.push(Box::new(move |event, phase, hitbox, cx| {
@@ -148,7 +148,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn capture_any_mouse_up(
&mut self,
listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseUpEvent, &mut WindowContext) + 'static,
) {
self.mouse_up_listeners
.push(Box::new(move |event, phase, hitbox, cx| {
@@ -164,7 +164,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_any_mouse_up(
&mut self,
listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseUpEvent, &mut WindowContext) + 'static,
) {
self.mouse_up_listeners
.push(Box::new(move |event, phase, hitbox, cx| {
@@ -181,7 +181,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_mouse_down_out(
&mut self,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) {
self.mouse_down_listeners
.push(Box::new(move |event, phase, hitbox, cx| {
@@ -199,7 +199,7 @@ impl Interactivity {
pub fn on_mouse_up_out(
&mut self,
button: MouseButton,
listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseUpEvent, &mut WindowContext) + 'static,
) {
self.mouse_up_listeners
.push(Box::new(move |event, phase, hitbox, cx| {
@@ -218,7 +218,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_mouse_move(
&mut self,
listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
) {
self.mouse_move_listeners
.push(Box::new(move |event, phase, hitbox, cx| {
@@ -237,7 +237,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_drag_move<T>(
&mut self,
listener: impl Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
listener: impl Send + Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
) where
T: 'static,
{
@@ -267,7 +267,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_scroll_wheel(
&mut self,
listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
) {
self.scroll_wheel_listeners
.push(Box::new(move |event, phase, hitbox, cx| {
@@ -283,7 +283,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn capture_action<A: Action>(
&mut self,
listener: impl Fn(&A, &mut WindowContext) + 'static,
listener: impl Send + Fn(&A, &mut WindowContext) + 'static,
) {
self.action_listeners.push((
TypeId::of::<A>(),
@@ -302,7 +302,10 @@ impl Interactivity {
/// The imperative API equivalent to [`InteractiveElement::on_action`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) {
pub fn on_action<A: Action>(
&mut self,
listener: impl Send + Fn(&A, &mut WindowContext) + 'static,
) {
self.action_listeners.push((
TypeId::of::<A>(),
Box::new(move |action, phase, cx| {
@@ -323,7 +326,7 @@ impl Interactivity {
pub fn on_boxed_action(
&mut self,
action: &dyn Action,
listener: impl Fn(&dyn Action, &mut WindowContext) + 'static,
listener: impl Send + Fn(&dyn Action, &mut WindowContext) + 'static,
) {
let action = action.boxed_clone();
self.action_listeners.push((
@@ -340,7 +343,10 @@ impl Interactivity {
/// The imperative API equivalent to [`InteractiveElement::on_key_down`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_key_down(&mut self, listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static) {
pub fn on_key_down(
&mut self,
listener: impl Send + Fn(&KeyDownEvent, &mut WindowContext) + 'static,
) {
self.key_down_listeners
.push(Box::new(move |event, phase, cx| {
if phase == DispatchPhase::Bubble {
@@ -355,7 +361,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn capture_key_down(
&mut self,
listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&KeyDownEvent, &mut WindowContext) + 'static,
) {
self.key_down_listeners
.push(Box::new(move |event, phase, cx| {
@@ -369,7 +375,10 @@ impl Interactivity {
/// The imperative API equivalent to [`InteractiveElement::on_key_up`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
pub fn on_key_up(
&mut self,
listener: impl Send + Fn(&KeyUpEvent, &mut WindowContext) + 'static,
) {
self.key_up_listeners
.push(Box::new(move |event, phase, cx| {
if phase == DispatchPhase::Bubble {
@@ -382,7 +391,10 @@ impl Interactivity {
/// The imperative API equivalent to [`InteractiveElement::on_key_up`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn capture_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
pub fn capture_key_up(
&mut self,
listener: impl Send + Fn(&KeyUpEvent, &mut WindowContext) + 'static,
) {
self.key_up_listeners
.push(Box::new(move |event, phase, cx| {
if phase == DispatchPhase::Capture {
@@ -397,7 +409,7 @@ impl Interactivity {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_modifiers_changed(
&mut self,
listener: impl Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static,
) {
self.modifiers_changed_listeners
.push(Box::new(move |event, cx| listener(event, cx)));
@@ -407,7 +419,10 @@ impl Interactivity {
/// The imperative API equivalent to [`InteractiveElement::on_drop`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) {
pub fn on_drop<T: 'static>(
&mut self,
listener: impl Send + Fn(&T, &mut WindowContext) + 'static,
) {
self.drop_listeners.push((
TypeId::of::<T>(),
Box::new(move |dragged_value, cx| {
@@ -418,7 +433,10 @@ impl Interactivity {
/// Use the given predicate to determine whether or not a drop event should be dispatched to this element
/// The imperative API equivalent to [`InteractiveElement::can_drop`]
pub fn can_drop(&mut self, predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static) {
pub fn can_drop(
&mut self,
predicate: impl Send + Fn(&dyn Any, &mut WindowContext) -> bool + 'static,
) {
self.can_drop_predicate = Some(Box::new(predicate));
}
@@ -426,7 +444,7 @@ impl Interactivity {
/// The imperative API equivalent to [`StatefulInteractiveElement::on_click`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static)
pub fn on_click(&mut self, listener: impl Send + Fn(&ClickEvent, &mut WindowContext) + 'static)
where
Self: Sized,
{
@@ -443,10 +461,10 @@ impl Interactivity {
pub fn on_drag<T, W>(
&mut self,
value: T,
constructor: impl Fn(&T, Point<Pixels>, &mut WindowContext) -> View<W> + 'static,
constructor: impl Send + Fn(&T, Point<Pixels>, &mut WindowContext) -> View<W> + 'static,
) where
Self: Sized,
T: 'static,
T: 'static + Send,
W: 'static + Render,
{
debug_assert!(
@@ -466,7 +484,7 @@ impl Interactivity {
/// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static)
pub fn on_hover(&mut self, listener: impl Send + Fn(&bool, &mut WindowContext) + 'static)
where
Self: Sized,
{
@@ -479,8 +497,10 @@ impl Interactivity {
/// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
/// The imperative API equivalent to [`InteractiveElement::tooltip`]
pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static)
where
pub fn tooltip(
&mut self,
build_tooltip: impl Send + Sync + Fn(&mut WindowContext) -> AnyView + 'static,
) where
Self: Sized,
{
debug_assert!(
@@ -488,7 +508,7 @@ impl Interactivity {
"calling tooltip more than once on the same element is not supported"
);
self.tooltip_builder = Some(TooltipBuilder {
build: Rc::new(build_tooltip),
build: Arc::new(build_tooltip),
hoverable: false,
});
}
@@ -498,7 +518,7 @@ impl Interactivity {
/// the tooltip. The imperative API equivalent to [`InteractiveElement::hoverable_tooltip`]
pub fn hoverable_tooltip(
&mut self,
build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static,
build_tooltip: impl Send + Sync + Fn(&mut WindowContext) -> AnyView + 'static,
) where
Self: Sized,
{
@@ -507,7 +527,7 @@ impl Interactivity {
"calling tooltip more than once on the same element is not supported"
);
self.tooltip_builder = Some(TooltipBuilder {
build: Rc::new(build_tooltip),
build: Arc::new(build_tooltip),
hoverable: true,
});
}
@@ -590,7 +610,7 @@ pub trait InteractiveElement: Sized {
fn on_mouse_down(
mut self,
button: MouseButton,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_mouse_down(button, listener);
self
@@ -620,7 +640,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn capture_any_mouse_down(
mut self,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().capture_any_mouse_down(listener);
self
@@ -632,7 +652,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_any_mouse_down(
mut self,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_any_mouse_down(listener);
self
@@ -645,7 +665,7 @@ pub trait InteractiveElement: Sized {
fn on_mouse_up(
mut self,
button: MouseButton,
listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseUpEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_mouse_up(button, listener);
self
@@ -657,7 +677,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn capture_any_mouse_up(
mut self,
listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseUpEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().capture_any_mouse_up(listener);
self
@@ -670,7 +690,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_mouse_down_out(
mut self,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_mouse_down_out(listener);
self
@@ -684,7 +704,7 @@ pub trait InteractiveElement: Sized {
fn on_mouse_up_out(
mut self,
button: MouseButton,
listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseUpEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_mouse_up_out(button, listener);
self
@@ -696,7 +716,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_mouse_move(
mut self,
listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_mouse_move(listener);
self
@@ -711,7 +731,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_drag_move<T: 'static>(
mut self,
listener: impl Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
listener: impl Send + Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_drag_move(listener);
self
@@ -723,7 +743,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_scroll_wheel(
mut self,
listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_scroll_wheel(listener);
self
@@ -735,7 +755,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn capture_action<A: Action>(
mut self,
listener: impl Fn(&A, &mut WindowContext) + 'static,
listener: impl Send + Fn(&A, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().capture_action(listener);
self
@@ -745,7 +765,10 @@ pub trait InteractiveElement: Sized {
/// The fluent API equivalent to [`Interactivity::on_action`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_action<A: Action>(mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) -> Self {
fn on_action<A: Action>(
mut self,
listener: impl Send + Fn(&A, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_action(listener);
self
}
@@ -759,7 +782,7 @@ pub trait InteractiveElement: Sized {
fn on_boxed_action(
mut self,
action: &dyn Action,
listener: impl Fn(&dyn Action, &mut WindowContext) + 'static,
listener: impl Send + Fn(&dyn Action, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_boxed_action(action, listener);
self
@@ -771,7 +794,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_key_down(
mut self,
listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&KeyDownEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_key_down(listener);
self
@@ -783,7 +806,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn capture_key_down(
mut self,
listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&KeyDownEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().capture_key_down(listener);
self
@@ -793,7 +816,10 @@ pub trait InteractiveElement: Sized {
/// The fluent API equivalent to [`Interactivity::on_key_up`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_key_up(mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) -> Self {
fn on_key_up(
mut self,
listener: impl Send + Fn(&KeyUpEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_key_up(listener);
self
}
@@ -804,7 +830,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn capture_key_up(
mut self,
listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&KeyUpEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().capture_key_up(listener);
self
@@ -816,7 +842,7 @@ pub trait InteractiveElement: Sized {
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_modifiers_changed(
mut self,
listener: impl Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static,
listener: impl Send + Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_modifiers_changed(listener);
self
@@ -825,7 +851,7 @@ pub trait InteractiveElement: Sized {
/// Apply the given style when the given data type is dragged over this element
fn drag_over<S: 'static>(
mut self,
f: impl 'static + Fn(StyleRefinement, &S, &WindowContext) -> StyleRefinement,
f: impl 'static + Send + Fn(StyleRefinement, &S, &WindowContext) -> StyleRefinement,
) -> Self {
self.interactivity().drag_over_styles.push((
TypeId::of::<S>(),
@@ -844,7 +870,7 @@ pub trait InteractiveElement: Sized {
fn group_drag_over<S: 'static>(
mut self,
group_name: impl Into<SharedString>,
f: impl FnOnce(StyleRefinement) -> StyleRefinement,
f: impl Send + FnOnce(StyleRefinement) -> StyleRefinement,
) -> Self {
self.interactivity().group_drag_over_styles.push((
TypeId::of::<S>(),
@@ -860,7 +886,10 @@ pub trait InteractiveElement: Sized {
/// The fluent API equivalent to [`Interactivity::on_drop`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_drop<T: 'static>(mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) -> Self {
fn on_drop<T: 'static>(
mut self,
listener: impl Send + Fn(&T, &mut WindowContext) + 'static,
) -> Self {
self.interactivity().on_drop(listener);
self
}
@@ -869,7 +898,7 @@ pub trait InteractiveElement: Sized {
/// The fluent API equivalent to [`Interactivity::can_drop`]
fn can_drop(
mut self,
predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static,
predicate: impl Send + Fn(&dyn Any, &mut WindowContext) -> bool + 'static,
) -> Self {
self.interactivity().can_drop(predicate);
self
@@ -958,7 +987,10 @@ pub trait StatefulInteractiveElement: InteractiveElement {
/// The fluent API equivalent to [`Interactivity::on_click`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self
fn on_click(
mut self,
listener: impl Send + Fn(&ClickEvent, &mut WindowContext) + 'static,
) -> Self
where
Self: Sized,
{
@@ -976,11 +1008,11 @@ pub trait StatefulInteractiveElement: InteractiveElement {
fn on_drag<T, W>(
mut self,
value: T,
constructor: impl Fn(&T, Point<Pixels>, &mut WindowContext) -> View<W> + 'static,
constructor: impl Send + Fn(&T, Point<Pixels>, &mut WindowContext) -> View<W> + 'static,
) -> Self
where
Self: Sized,
T: 'static,
T: 'static + Send,
W: 'static + Render,
{
self.interactivity().on_drag(value, constructor);
@@ -992,7 +1024,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
/// The fluent API equivalent to [`Interactivity::on_hover`]
///
/// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_hover(mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static) -> Self
fn on_hover(mut self, listener: impl Send + Fn(&bool, &mut WindowContext) + 'static) -> Self
where
Self: Sized,
{
@@ -1002,7 +1034,10 @@ pub trait StatefulInteractiveElement: InteractiveElement {
/// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
/// The fluent API equivalent to [`Interactivity::tooltip`]
fn tooltip(mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self
fn tooltip(
mut self,
build_tooltip: impl Send + Sync + Fn(&mut WindowContext) -> AnyView + 'static,
) -> Self
where
Self: Sized,
{
@@ -1015,7 +1050,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
/// the tooltip. The fluent API equivalent to [`Interactivity::hoverable_tooltip`]
fn hoverable_tooltip(
mut self,
build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static,
build_tooltip: impl Send + Sync + Fn(&mut WindowContext) -> AnyView + 'static,
) -> Self
where
Self: Sized,
@@ -1047,40 +1082,41 @@ pub trait FocusableElement: InteractiveElement {
}
pub(crate) type MouseDownListener =
Box<dyn Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
Box<dyn Send + Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
pub(crate) type MouseUpListener =
Box<dyn Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
Box<dyn Send + Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
pub(crate) type MouseMoveListener =
Box<dyn Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
Box<dyn Send + Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
pub(crate) type ScrollWheelListener =
Box<dyn Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
Box<dyn Send + Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
pub(crate) type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
pub(crate) type ClickListener = Box<dyn Send + Fn(&ClickEvent, &mut WindowContext) + 'static>;
pub(crate) type DragListener =
Box<dyn Fn(&dyn Any, Point<Pixels>, &mut WindowContext) -> AnyView + 'static>;
Box<dyn Send + Fn(&dyn Any, Point<Pixels>, &mut WindowContext) -> AnyView + 'static>;
type DropListener = Box<dyn Fn(&dyn Any, &mut WindowContext) + 'static>;
type DropListener = Box<dyn Send + Fn(&dyn Any, &mut WindowContext) + 'static>;
type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut WindowContext) -> bool + 'static>;
type CanDropPredicate = Box<dyn Send + Fn(&dyn Any, &mut WindowContext) -> bool + 'static>;
pub(crate) struct TooltipBuilder {
build: Rc<dyn Fn(&mut WindowContext) -> AnyView + 'static>,
build: Arc<dyn Send + Sync + Fn(&mut WindowContext) -> AnyView + 'static>,
hoverable: bool,
}
pub(crate) type KeyDownListener =
Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + 'static>;
Box<dyn Send + Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + 'static>;
pub(crate) type KeyUpListener =
Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + 'static>;
Box<dyn Send + Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + 'static>;
pub(crate) type ModifiersChangedListener =
Box<dyn Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static>;
Box<dyn Send + Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static>;
pub(crate) type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
pub(crate) type ActionListener =
Box<dyn Send + Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
/// Construct a new [`Div`] element
#[track_caller]
@@ -1173,12 +1209,12 @@ impl Element for Div {
let mut child_min = point(Pixels::MAX, Pixels::MAX);
let mut child_max = Point::default();
if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
*handle.last_origin.borrow_mut() = bounds.origin - cx.element_offset();
*handle.last_origin.lock() = bounds.origin - cx.element_offset();
}
let content_size = if request_layout.child_layout_ids.is_empty() {
bounds.size
} else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
let mut state = scroll_handle.0.borrow_mut();
let mut state = scroll_handle.0.lock();
state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
state.bounds = bounds;
let requested = state.requested_scroll_top.take();
@@ -1191,7 +1227,7 @@ impl Element for Div {
if let Some(requested) = requested.as_ref() {
if requested.0 == ix {
*state.offset.borrow_mut() =
*state.offset.lock() =
bounds.origin - (child_bounds.origin - point(px(0.), requested.1));
}
}
@@ -1265,7 +1301,7 @@ pub struct Interactivity {
pub(crate) tracked_focus_handle: Option<FocusHandle>,
pub(crate) tracked_scroll_handle: Option<ScrollHandle>,
pub(crate) scroll_anchor: Option<ScrollAnchor>,
pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
pub(crate) scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
pub(crate) group: Option<SharedString>,
/// The base style of the element, before any modifications are applied
/// by focus, active, etc.
@@ -1278,7 +1314,7 @@ pub struct Interactivity {
pub(crate) group_active_style: Option<GroupStyle>,
pub(crate) drag_over_styles: Vec<(
TypeId,
Box<dyn Fn(&dyn Any, &mut WindowContext) -> StyleRefinement>,
Box<dyn Send + Fn(&dyn Any, &mut WindowContext) -> StyleRefinement>,
)>,
pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
@@ -1292,8 +1328,8 @@ pub struct Interactivity {
pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
pub(crate) can_drop_predicate: Option<CanDropPredicate>,
pub(crate) click_listeners: Vec<ClickListener>,
pub(crate) drag_listener: Option<(Box<dyn Any>, DragListener)>,
pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
pub(crate) drag_listener: Option<(Box<dyn Any + Send>, DragListener)>,
pub(crate) hover_listener: Option<Box<dyn Send + Fn(&bool, &mut WindowContext)>>,
pub(crate) tooltip_builder: Option<TooltipBuilder>,
pub(crate) occlude_mouse: bool,
@@ -1322,10 +1358,10 @@ impl Interactivity {
if cx.has_active_drag() {
if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref()
{
*pending_mouse_down.borrow_mut() = None;
*pending_mouse_down.lock() = None;
}
if let Some(clicked_state) = element_state.clicked_state.as_ref() {
*clicked_state.borrow_mut() = ElementClickedState::default();
*clicked_state.lock() = ElementClickedState::default();
}
}
}
@@ -1346,7 +1382,7 @@ impl Interactivity {
}
if let Some(scroll_handle) = self.tracked_scroll_handle.as_ref() {
self.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
self.scroll_offset = Some(scroll_handle.0.lock().offset.clone());
} else if self.base_style.overflow.x == Some(Overflow::Scroll)
|| self.base_style.overflow.y == Some(Overflow::Scroll)
{
@@ -1354,7 +1390,7 @@ impl Interactivity {
self.scroll_offset = Some(
element_state
.scroll_offset
.get_or_insert_with(Rc::default)
.get_or_insert_with(Arc::default)
.clone(),
);
}
@@ -1389,12 +1425,12 @@ impl Interactivity {
if let Some(element_state) = element_state.as_ref() {
if let Some(clicked_state) = element_state.clicked_state.as_ref() {
let clicked_state = clicked_state.borrow();
let clicked_state = clicked_state.lock();
self.active = Some(clicked_state.element);
}
if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
if let Some(active_tooltip) = active_tooltip.borrow().as_ref() {
if let Some(active_tooltip) = active_tooltip.lock().as_ref() {
if let Some(tooltip) = active_tooltip.tooltip.clone() {
self.tooltip_id = Some(cx.set_tooltip(tooltip));
}
@@ -1445,7 +1481,7 @@ impl Interactivity {
) -> Point<Pixels> {
if let Some(scroll_offset) = self.scroll_offset.as_ref() {
if let Some(scroll_handle) = &self.tracked_scroll_handle {
scroll_handle.0.borrow_mut().overflow = style.overflow;
scroll_handle.0.lock().overflow = style.overflow;
}
let rem_size = cx.rem_size();
@@ -1470,7 +1506,7 @@ impl Interactivity {
let scroll_max = (self.content_size + padding_size - bounds.size).max(&Size::default());
// Clamp scroll offset in case scroll max is smaller now (e.g., if children
// were removed or the bounds became larger).
let mut scroll_offset = scroll_offset.borrow_mut();
let mut scroll_offset = scroll_offset.lock();
scroll_offset.x = scroll_offset.x.clamp(-scroll_max.width, px(0.));
scroll_offset.y = scroll_offset.y.clamp(-scroll_max.height, px(0.));
*scroll_offset
@@ -1799,7 +1835,7 @@ impl Interactivity {
&& event.button == MouseButton::Left
&& hitbox.is_hovered(cx)
{
*pending_mouse_down.borrow_mut() = Some(event.clone());
*pending_mouse_down.lock() = Some(event.clone());
cx.refresh();
}
}
@@ -1813,14 +1849,14 @@ impl Interactivity {
return;
}
let mut pending_mouse_down = pending_mouse_down.borrow_mut();
let mut pending_mouse_down = pending_mouse_down.lock();
if let Some(mouse_down) = pending_mouse_down.clone() {
if !cx.has_active_drag()
&& (event.position - mouse_down.position).magnitude()
> DRAG_THRESHOLD
{
if let Some((drag_value, drag_listener)) = drag_listener.take() {
*clicked_state.borrow_mut() = ElementClickedState::default();
*clicked_state.lock() = ElementClickedState::default();
let cursor_offset = event.position - hitbox.origin;
let drag =
(drag_listener)(drag_value.as_ref(), cursor_offset, cx);
@@ -1846,7 +1882,7 @@ impl Interactivity {
// so that it happens even if another event handler stops
// propagation.
DispatchPhase::Capture => {
let mut pending_mouse_down = pending_mouse_down.borrow_mut();
let mut pending_mouse_down = pending_mouse_down.lock();
if pending_mouse_down.is_some() && hitbox.is_hovered(cx) {
captured_mouse_down = pending_mouse_down.take();
cx.refresh();
@@ -1883,10 +1919,10 @@ impl Interactivity {
if phase != DispatchPhase::Bubble {
return;
}
let is_hovered = has_mouse_down.borrow().is_none()
let is_hovered = has_mouse_down.lock().is_none()
&& !cx.has_active_drag()
&& hitbox.is_hovered(cx);
let mut was_hovered = was_hovered.borrow_mut();
let mut was_hovered = was_hovered.lock();
if is_hovered != *was_hovered {
*was_hovered = is_hovered;
@@ -1919,11 +1955,11 @@ impl Interactivity {
let tooltip_id = self.tooltip_id;
move |_: &MouseMoveEvent, phase, cx| {
let is_hovered =
pending_mouse_down.borrow().is_none() && hitbox.is_hovered(cx);
pending_mouse_down.lock().is_none() && hitbox.is_hovered(cx);
let tooltip_is_hovered =
tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(cx));
if !is_hovered && (!tooltip_is_hoverable || !tooltip_is_hovered) {
if active_tooltip.borrow_mut().take().is_some() {
if active_tooltip.lock().take().is_some() {
cx.refresh();
}
@@ -1934,14 +1970,14 @@ impl Interactivity {
return;
}
if active_tooltip.borrow().is_none() {
if active_tooltip.lock().is_none() {
let task = cx.spawn({
let active_tooltip = active_tooltip.clone();
let build_tooltip = tooltip_builder.build.clone();
move |mut cx| async move {
cx.background_executor().timer(TOOLTIP_DELAY).await;
cx.update(|cx| {
active_tooltip.borrow_mut().replace(ActiveTooltip {
active_tooltip.lock().replace(ActiveTooltip {
tooltip: Some(AnyTooltip {
view: build_tooltip(cx),
mouse_position: cx.mouse_position(),
@@ -1953,7 +1989,7 @@ impl Interactivity {
.ok();
}
});
active_tooltip.borrow_mut().replace(ActiveTooltip {
active_tooltip.lock().replace(ActiveTooltip {
tooltip: None,
_task: Some(task),
});
@@ -1969,7 +2005,7 @@ impl Interactivity {
tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(cx));
if (!tooltip_is_hoverable || !tooltip_is_hovered)
&& active_tooltip.borrow_mut().take().is_some()
&& active_tooltip.lock().take().is_some()
{
cx.refresh();
}
@@ -1983,7 +2019,7 @@ impl Interactivity {
let tooltip_is_hovered =
tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(cx));
if (!tooltip_is_hoverable || !tooltip_is_hovered)
&& active_tooltip.borrow_mut().take().is_some()
&& active_tooltip.lock().take().is_some()
{
cx.refresh();
}
@@ -1995,10 +2031,10 @@ impl Interactivity {
.clicked_state
.get_or_insert_with(Default::default)
.clone();
if active_state.borrow().is_clicked() {
if active_state.lock().is_clicked() {
cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Capture {
*active_state.borrow_mut() = ElementClickedState::default();
*active_state.lock() = ElementClickedState::default();
cx.refresh();
}
});
@@ -2014,7 +2050,7 @@ impl Interactivity {
.map_or(false, |group_hitbox_id| group_hitbox_id.is_hovered(cx));
let element_hovered = hitbox.is_hovered(cx);
if group_hovered || element_hovered {
*active_state.borrow_mut() = ElementClickedState {
*active_state.lock() = ElementClickedState {
group: group_hovered,
element: element_hovered,
};
@@ -2083,7 +2119,7 @@ impl Interactivity {
let hitbox = hitbox.clone();
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
let mut scroll_offset = scroll_offset.borrow_mut();
let mut scroll_offset = scroll_offset.lock();
let old_scroll_offset = *scroll_offset;
let delta = event.delta.pixel_delta(line_height);
@@ -2213,7 +2249,7 @@ impl Interactivity {
let clicked_state = element_state
.clicked_state
.get_or_insert_with(Default::default)
.borrow();
.lock();
if clicked_state.group {
if let Some(group) = self.group_active_style.as_ref() {
style.refine(&group.style)
@@ -2236,11 +2272,11 @@ impl Interactivity {
#[derive(Default)]
pub struct InteractiveElementState {
pub(crate) focus_handle: Option<FocusHandle>,
pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
pub(crate) hover_state: Option<Rc<RefCell<bool>>>,
pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
pub(crate) clicked_state: Option<Arc<Mutex<ElementClickedState>>>,
pub(crate) hover_state: Option<Arc<Mutex<bool>>>,
pub(crate) pending_mouse_down: Option<Arc<Mutex<Option<MouseDownEvent>>>>,
pub(crate) scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
pub(crate) active_tooltip: Option<Arc<Mutex<Option<ActiveTooltip>>>>,
}
/// The current active tooltip
@@ -2480,7 +2516,7 @@ where
#[derive(Clone)]
pub struct ScrollAnchor {
handle: ScrollHandle,
last_origin: Rc<RefCell<Point<Pixels>>>,
last_origin: Arc<Mutex<Point<Pixels>>>,
}
impl ScrollAnchor {
@@ -2497,14 +2533,14 @@ impl ScrollAnchor {
cx.on_next_frame(move |_| {
let viewport_bounds = this.handle.bounds();
let self_bounds = *this.last_origin.borrow();
let self_bounds = *this.last_origin.lock();
this.handle.set_offset(viewport_bounds.origin - self_bounds);
});
}
}
#[derive(Default, Debug)]
struct ScrollHandleState {
offset: Rc<RefCell<Point<Pixels>>>,
offset: Arc<Mutex<Point<Pixels>>>,
bounds: Bounds<Pixels>,
child_bounds: Vec<Bounds<Pixels>>,
requested_scroll_top: Option<(usize, Pixels)>,
@@ -2515,7 +2551,7 @@ struct ScrollHandleState {
/// Used for accessing scroll state, like the current scroll offset,
/// and for mutating the scroll state, like scrolling to a specific child.
#[derive(Clone, Debug)]
pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
pub struct ScrollHandle(Arc<Mutex<ScrollHandleState>>);
impl Default for ScrollHandle {
fn default() -> Self {
@@ -2526,18 +2562,18 @@ impl Default for ScrollHandle {
impl ScrollHandle {
/// Construct a new scroll handle.
pub fn new() -> Self {
Self(Rc::default())
Self(Arc::default())
}
/// Get the current scroll offset.
pub fn offset(&self) -> Point<Pixels> {
*self.0.borrow().offset.borrow()
*self.0.lock().offset.lock()
}
/// Get the top child that's scrolled into view.
pub fn top_item(&self) -> usize {
let state = self.0.borrow();
let top = state.bounds.top() - state.offset.borrow().y;
let state = self.0.lock();
let top = state.bounds.top() - state.offset.lock().y;
match state.child_bounds.binary_search_by(|bounds| {
if top < bounds.top() {
@@ -2555,29 +2591,29 @@ impl ScrollHandle {
/// Return the bounds into which this child is painted
pub fn bounds(&self) -> Bounds<Pixels> {
self.0.borrow().bounds
self.0.lock().bounds
}
/// Set the bounds into which this child is painted
pub(super) fn set_bounds(&self, bounds: Bounds<Pixels>) {
self.0.borrow_mut().bounds = bounds;
self.0.lock().bounds = bounds;
}
/// Get the bounds for a specific child.
pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
self.0.borrow().child_bounds.get(ix).cloned()
self.0.lock().child_bounds.get(ix).cloned()
}
/// scroll_to_item scrolls the minimal amount to ensure that the child is
/// fully visible
pub fn scroll_to_item(&self, ix: usize) {
let state = self.0.borrow();
let state = self.0.lock();
let Some(bounds) = state.child_bounds.get(ix) else {
return;
};
let mut scroll_offset = state.offset.borrow_mut();
let mut scroll_offset = state.offset.lock();
if state.overflow.y == Overflow::Scroll {
if bounds.top() + scroll_offset.y < state.bounds.top() {
@@ -2600,19 +2636,19 @@ impl ScrollHandle {
/// parent container to the top left of the first child.
/// As you scroll further down the offset becomes more negative.
pub fn set_offset(&self, mut position: Point<Pixels>) {
let state = self.0.borrow();
*state.offset.borrow_mut() = position;
let state = self.0.lock();
*state.offset.lock() = position;
}
/// Get the logical scroll top, based on a child index and a pixel offset.
pub fn logical_scroll_top(&self) -> (usize, Pixels) {
let ix = self.top_item();
let state = self.0.borrow();
let state = self.0.lock();
if let Some(child_bounds) = state.child_bounds.get(ix) {
(
ix,
child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
child_bounds.top() + state.offset.lock().y - state.bounds.top(),
)
} else {
(ix, px(0.))
@@ -2621,11 +2657,11 @@ impl ScrollHandle {
/// Set the logical scroll top, based on a child index and a pixel offset.
pub fn set_logical_scroll_top(&self, ix: usize, px: Pixels) {
self.0.borrow_mut().requested_scroll_top = Some((ix, px));
self.0.lock().requested_scroll_top = Some((ix, px));
}
/// Get the count of children for scrollable item.
pub fn children_count(&self) -> usize {
self.0.borrow().child_bounds.len()
self.0.lock().child_bounds.len()
}
}

View File

@@ -37,14 +37,20 @@ pub type ImgResourceLoader = AssetLogger<ImageAssetLoader>;
/// A source of image content.
#[derive(Clone)]
pub enum ImageSource {
/// The image content will be loaded from some resource location
/// Th)e image content will be loaded from some resource location
Resource(Resource),
/// Cached image data
Render(Arc<RenderImage>),
/// Cached image data
Image(Arc<Image>),
/// A custom loading function to use
Custom(Arc<dyn Fn(&mut WindowContext) -> Option<Result<Arc<RenderImage>, ImageCacheError>>>),
Custom(
Arc<
dyn Send
+ Sync
+ Fn(&mut WindowContext) -> Option<Result<Arc<RenderImage>, ImageCacheError>>,
>,
),
}
fn is_uri(uri: &str) -> bool {
@@ -113,8 +119,12 @@ impl From<Arc<Image>> for ImageSource {
}
}
impl<F: Fn(&mut WindowContext) -> Option<Result<Arc<RenderImage>, ImageCacheError>> + 'static>
From<F> for ImageSource
impl<
F: Send
+ Sync
+ Fn(&mut WindowContext) -> Option<Result<Arc<RenderImage>, ImageCacheError>>
+ 'static,
> From<F> for ImageSource
{
fn from(value: F) -> Self {
Self::Custom(Arc::new(value))
@@ -125,8 +135,8 @@ impl<F: Fn(&mut WindowContext) -> Option<Result<Arc<RenderImage>, ImageCacheErro
pub struct ImageStyle {
grayscale: bool,
object_fit: ObjectFit,
loading: Option<Box<dyn Fn() -> AnyElement>>,
fallback: Option<Box<dyn Fn() -> AnyElement>>,
loading: Option<Box<dyn Send + Fn() -> AnyElement>>,
fallback: Option<Box<dyn Send + Fn() -> AnyElement>>,
}
impl Default for ImageStyle {
@@ -158,13 +168,13 @@ pub trait StyledImage: Sized {
}
/// Set the object fit for the image.
fn with_fallback(mut self, fallback: impl Fn() -> AnyElement + 'static) -> Self {
fn with_fallback(mut self, fallback: impl Send + Fn() -> AnyElement + 'static) -> Self {
self.image_style().fallback = Some(Box::new(fallback));
self
}
/// Set the object fit for the image.
fn with_loading(mut self, loading: impl Fn() -> AnyElement + 'static) -> Self {
fn with_loading(mut self, loading: impl Send + Fn() -> AnyElement + 'static) -> Self {
self.image_style().loading = Some(Box::new(loading));
self
}

View File

@@ -13,8 +13,9 @@ use crate::{
Size, Style, StyleRefinement, Styled, WindowContext,
};
use collections::VecDeque;
use parking_lot::Mutex;
use refineable::Refineable as _;
use std::{cell::RefCell, ops::Range, rc::Rc};
use std::{ops::Range, sync::Arc};
use sum_tree::{Bias, SumTree};
use taffy::style::Overflow;
@@ -44,19 +45,19 @@ impl List {
/// The list state that views must hold on behalf of the list element.
#[derive(Clone)]
pub struct ListState(Rc<RefCell<StateInner>>);
pub struct ListState(Arc<Mutex<StateInner>>);
struct StateInner {
last_layout_bounds: Option<Bounds<Pixels>>,
last_padding: Option<Edges<Pixels>>,
render_item: Box<dyn FnMut(usize, &mut WindowContext) -> AnyElement>,
render_item: Box<dyn Send + FnMut(usize, &mut WindowContext) -> AnyElement>,
items: SumTree<ListItem>,
logical_scroll_top: Option<ListOffset>,
alignment: ListAlignment,
overdraw: Pixels,
reset: bool,
#[allow(clippy::type_complexity)]
scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
scroll_handler: Option<Box<dyn Send + FnMut(&ListScrollEvent, &mut WindowContext)>>,
}
/// Whether the list is scrolling from top to bottom or bottom to top.
@@ -186,9 +187,9 @@ impl ListState {
render_item: R,
) -> Self
where
R: 'static + FnMut(usize, &mut WindowContext) -> AnyElement,
R: 'static + Send + FnMut(usize, &mut WindowContext) -> AnyElement,
{
let this = Self(Rc::new(RefCell::new(StateInner {
let this = Self(Arc::new(Mutex::new(StateInner {
last_layout_bounds: None,
last_padding: None,
render_item: Box::new(render_item),
@@ -208,7 +209,7 @@ impl ListState {
/// Note that this will cause scroll events to be dropped until the next paint.
pub fn reset(&self, element_count: usize) {
let old_count = {
let state = &mut *self.0.borrow_mut();
let state = &mut *self.0.lock();
state.reset = true;
state.logical_scroll_top = None;
state.items.summary().count
@@ -219,7 +220,7 @@ impl ListState {
/// The number of items in this list.
pub fn item_count(&self) -> usize {
self.0.borrow().items.summary().count
self.0.lock().items.summary().count
}
/// Inform the list state that the items in `old_range` have been replaced
@@ -237,7 +238,7 @@ impl ListState {
old_range: Range<usize>,
focus_handles: impl IntoIterator<Item = Option<FocusHandle>>,
) {
let state = &mut *self.0.borrow_mut();
let state = &mut *self.0.lock();
let mut old_items = state.items.cursor::<Count>(&());
let mut new_items = old_items.slice(&Count(old_range.start), Bias::Right, &());
@@ -272,19 +273,19 @@ impl ListState {
/// Set a handler that will be called when the list is scrolled.
pub fn set_scroll_handler(
&self,
handler: impl FnMut(&ListScrollEvent, &mut WindowContext) + 'static,
handler: impl Send + FnMut(&ListScrollEvent, &mut WindowContext) + 'static,
) {
self.0.borrow_mut().scroll_handler = Some(Box::new(handler))
self.0.lock().scroll_handler = Some(Box::new(handler))
}
/// Get the current scroll offset, in terms of the list's items.
pub fn logical_scroll_top(&self) -> ListOffset {
self.0.borrow().logical_scroll_top()
self.0.lock().logical_scroll_top()
}
/// Scroll the list to the given offset
pub fn scroll_to(&self, mut scroll_top: ListOffset) {
let state = &mut *self.0.borrow_mut();
let state = &mut *self.0.lock();
let item_count = state.items.summary().count;
if scroll_top.item_ix >= item_count {
scroll_top.item_ix = item_count;
@@ -296,7 +297,7 @@ impl ListState {
/// Scroll the list to the given item, such that the item is fully visible.
pub fn scroll_to_reveal_item(&self, ix: usize) {
let state = &mut *self.0.borrow_mut();
let state = &mut *self.0.lock();
let mut scroll_top = state.logical_scroll_top();
let height = state
@@ -329,7 +330,7 @@ impl ListState {
/// Get the bounds for the given item in window coordinates, if it's
/// been rendered.
pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
let state = &*self.0.borrow();
let state = &*self.0.lock();
let bounds = state.last_layout_bounds.unwrap_or_default();
let scroll_top = state.logical_scroll_top();
@@ -724,7 +725,7 @@ impl Element for List {
style.overflow.y = Overflow::Scroll;
style.refine(&self.style);
cx.with_text_style(style.text_style().cloned(), |cx| {
let state = &mut *self.state.0.borrow_mut();
let state = &mut *self.state.0.lock();
let available_height = if let Some(last_bounds) = state.last_layout_bounds {
last_bounds.size.height
@@ -785,7 +786,7 @@ impl Element for List {
_: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> ListPrepaintState {
let state = &mut *self.state.0.borrow_mut();
let state = &mut *self.state.0.lock();
state.reset = false;
let mut style = Style::default();
@@ -841,7 +842,7 @@ impl Element for List {
let hitbox_id = prepaint.hitbox.id;
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
if phase == DispatchPhase::Bubble && hitbox_id.is_hovered(cx) {
list_state.0.borrow_mut().scroll(
list_state.0.lock().scroll(
&scroll_top,
height,
event.delta.pixel_delta(px(20.)),

View File

@@ -471,10 +471,13 @@ impl TextLayout {
pub struct InteractiveText {
element_id: ElementId,
text: StyledText,
click_listener:
Option<Box<dyn Fn(&[Range<usize>], InteractiveTextClickEvent, &mut WindowContext<'_>)>>,
hover_listener: Option<Box<dyn Fn(Option<usize>, MouseMoveEvent, &mut WindowContext<'_>)>>,
tooltip_builder: Option<Rc<dyn Fn(usize, &mut WindowContext<'_>) -> Option<AnyView>>>,
click_listener: Option<
Box<dyn Send + Fn(&[Range<usize>], InteractiveTextClickEvent, &mut WindowContext<'_>)>,
>,
hover_listener:
Option<Box<dyn Send + Fn(Option<usize>, MouseMoveEvent, &mut WindowContext<'_>)>>,
tooltip_builder:
Option<Arc<dyn Send + Sync + Fn(usize, &mut WindowContext<'_>) -> Option<AnyView>>>,
clickable_ranges: Vec<Range<usize>>,
}
@@ -510,7 +513,7 @@ impl InteractiveText {
pub fn on_click(
mut self,
ranges: Vec<Range<usize>>,
listener: impl Fn(usize, &mut WindowContext<'_>) + 'static,
listener: impl Send + Fn(usize, &mut WindowContext<'_>) + 'static,
) -> Self {
self.click_listener = Some(Box::new(move |ranges, event, cx| {
for (range_ix, range) in ranges.iter().enumerate() {
@@ -528,7 +531,7 @@ impl InteractiveText {
/// index of the hovered character, or None if the mouse leaves the text.
pub fn on_hover(
mut self,
listener: impl Fn(Option<usize>, MouseMoveEvent, &mut WindowContext<'_>) + 'static,
listener: impl Send + Fn(Option<usize>, MouseMoveEvent, &mut WindowContext<'_>) + 'static,
) -> Self {
self.hover_listener = Some(Box::new(listener));
self
@@ -537,9 +540,9 @@ impl InteractiveText {
/// tooltip lets you specify a tooltip for a given character index in the string.
pub fn tooltip(
mut self,
builder: impl Fn(usize, &mut WindowContext<'_>) -> Option<AnyView> + 'static,
builder: impl Send + Sync + Fn(usize, &mut WindowContext<'_>) -> Option<AnyView> + 'static,
) -> Self {
self.tooltip_builder = Some(Rc::new(builder));
self.tooltip_builder = Some(Arc::new(builder));
self
}
}

View File

@@ -10,8 +10,9 @@ use crate::{
ListSizingBehavior, Pixels, Render, ScrollHandle, Size, StyleRefinement, Styled, View,
ViewContext, WindowContext,
};
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
use std::{cmp, ops::Range, sync::Arc};
use taffy::style::Overflow;
use super::ListHorizontalSizingBehavior;
@@ -24,7 +25,7 @@ pub fn uniform_list<I, R, V>(
view: View<V>,
id: I,
item_count: usize,
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<R>,
f: impl 'static + Send + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<R>,
) -> UniformList
where
I: Into<ElementId>,
@@ -68,8 +69,9 @@ where
pub struct UniformList {
item_count: usize,
item_to_measure_index: usize,
render_items:
Box<dyn for<'a> Fn(Range<usize>, &'a mut WindowContext) -> SmallVec<[AnyElement; 64]>>,
render_items: Box<
dyn for<'a> Fn(Range<usize>, &'a mut WindowContext) -> SmallVec<[AnyElement; 64]> + Send,
>,
decorations: Vec<Box<dyn UniformListDecoration>>,
interactivity: Interactivity,
scroll_handle: Option<UniformListScrollHandle>,
@@ -86,7 +88,7 @@ pub struct UniformListFrameState {
/// A handle for controlling the scroll position of a uniform list.
/// This should be stored in your view and passed to the uniform_list on each frame.
#[derive(Clone, Debug, Default)]
pub struct UniformListScrollHandle(pub Rc<RefCell<UniformListScrollState>>);
pub struct UniformListScrollHandle(pub Arc<Mutex<UniformListScrollState>>);
/// Where to place the element scrolled to.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -121,7 +123,7 @@ pub struct ItemSize {
impl UniformListScrollHandle {
/// Create a new scroll handle to bind to a uniform list.
pub fn new() -> Self {
Self(Rc::new(RefCell::new(UniformListScrollState {
Self(Arc::new(Mutex::new(UniformListScrollState {
base_handle: ScrollHandle::new(),
deferred_scroll_to_item: None,
last_item_size: None,
@@ -130,13 +132,13 @@ impl UniformListScrollHandle {
/// Scroll the list to the given item index.
pub fn scroll_to_item(&self, ix: usize, strategy: ScrollStrategy) {
self.0.borrow_mut().deferred_scroll_to_item = Some((ix, strategy));
self.0.lock().deferred_scroll_to_item = Some((ix, strategy));
}
/// Get the index of the topmost visible child.
#[cfg(any(test, feature = "test-support"))]
pub fn logical_scroll_top_index(&self) -> usize {
let this = self.0.borrow();
let this = self.0.lock();
this.deferred_scroll_to_item
.map(|(ix, _)| ix)
.unwrap_or_else(|| this.base_handle.logical_scroll_top().0)
@@ -242,7 +244,7 @@ impl Element for UniformList {
let shared_scroll_offset = self.interactivity.scroll_offset.clone().unwrap();
let item_height = longest_item_size.height;
let shared_scroll_to_item = self.scroll_handle.as_mut().and_then(|handle| {
let mut handle = handle.0.borrow_mut();
let mut handle = handle.0.lock();
handle.last_item_size = Some(ItemSize {
item: padded_bounds.size,
contents: content_size,
@@ -265,7 +267,7 @@ impl Element for UniformList {
);
if let Some(handle) = self.scroll_handle.as_mut() {
handle.0.borrow_mut().base_handle.set_bounds(bounds);
handle.0.lock().base_handle.set_bounds(bounds);
}
if self.item_count > 0 {
@@ -274,7 +276,7 @@ impl Element for UniformList {
let is_scrolled_vertically = !scroll_offset.y.is_zero();
let min_vertical_scroll_offset = padded_bounds.size.height - content_height;
if is_scrolled_vertically && scroll_offset.y < min_vertical_scroll_offset {
shared_scroll_offset.borrow_mut().y = min_vertical_scroll_offset;
shared_scroll_offset.lock().y = min_vertical_scroll_offset;
scroll_offset.y = min_vertical_scroll_offset;
}
@@ -282,13 +284,13 @@ impl Element for UniformList {
let is_scrolled_horizontally =
can_scroll_horizontally && !scroll_offset.x.is_zero();
if is_scrolled_horizontally && content_width <= padded_bounds.size.width {
shared_scroll_offset.borrow_mut().x = Pixels::ZERO;
shared_scroll_offset.lock().x = Pixels::ZERO;
scroll_offset.x = Pixels::ZERO;
}
if let Some((ix, scroll_strategy)) = shared_scroll_to_item {
let list_height = padded_bounds.size.height;
let mut updated_scroll_offset = shared_scroll_offset.borrow_mut();
let mut updated_scroll_offset = shared_scroll_offset.lock();
let item_top = item_height * ix + padding.top;
let item_bottom = item_top + item_height;
let scroll_top = -updated_scroll_offset.y;
@@ -424,7 +426,7 @@ impl IntoElement for UniformList {
/// A decoration for a [`UniformList`]. This can be used for various things,
/// such as rendering indent guides, or other visual effects.
pub trait UniformListDecoration {
pub trait UniformListDecoration: Send {
/// Compute the decoration element, given the visible range of list items,
/// the bounds of the list, and the height of each item.
fn compute(
@@ -496,7 +498,7 @@ impl UniformList {
/// Track and render scroll state of this list with reference to the given scroll handle.
pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
self.interactivity.tracked_scroll_handle = Some(handle.0.borrow().base_handle.clone());
self.interactivity.tracked_scroll_handle = Some(handle.0.lock().base_handle.clone());
self.scroll_handle = Some(handle);
self
}

View File

@@ -43,7 +43,7 @@ use std::{
atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Weak,
},
time::{Duration, Instant},
time::{Duration, Instant, SystemTime},
};
use util::post_inc;
use util::{measure, ResultExt};
@@ -117,6 +117,7 @@ slotmap::new_key_type! {
thread_local! {
/// 8MB wasn't quite enough...
/// TODO: make this blow up when called from other threads
pub(crate) static ELEMENT_ARENA: RefCell<Arena> = RefCell::new(Arena::new(32 * 1024 * 1024));
}
@@ -1441,13 +1442,30 @@ impl<'a> WindowContext<'a> {
self.window
.next_frame
.finish(&mut self.window.rendered_frame);
ELEMENT_ARENA.with_borrow_mut(|element_arena| {
let trash_handle = ELEMENT_ARENA.with_borrow_mut(|element_arena| {
let percentage = (element_arena.len() as f32 / element_arena.capacity() as f32) * 100.;
if percentage >= 80. {
log::warn!("elevated element arena occupation: {}.", percentage);
}
element_arena.clear();
let start = SystemTime::now();
let trash_handle = element_arena.trash_everything();
log::error!(
"trash_everything elapsed time = {:?}",
SystemTime::now().duration_since(start).unwrap()
);
trash_handle
});
self.app
.background_executor()
.spawn(async move {
let start = SystemTime::now();
trash_handle.drop_trash();
log::error!(
"drop_trash elapsed time = {:?}",
SystemTime::now().duration_since(start).unwrap()
);
})
.detach();
self.window.draw_phase = DrawPhase::Focus;
let previous_focus_path = self.window.rendered_frame.focus_path();

View File

@@ -17,6 +17,7 @@ chrono.workspace = true
gpui.workspace = true
itertools = { workspace = true, optional = true }
menu.workspace = true
parking_lot.workspace = true
serde.workspace = true
settings.workspace = true
smallvec.workspace = true

View File

@@ -16,7 +16,7 @@ pub enum AudioStatus {
#[derive(IntoElement)]
pub struct AvatarAudioStatusIndicator {
audio_status: AudioStatus,
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + Send + Sync>>,
}
impl AvatarAudioStatusIndicator {
@@ -29,7 +29,10 @@ impl AvatarAudioStatusIndicator {
}
/// Sets the tooltip for the indicator.
pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
pub fn tooltip(
mut self,
tooltip: impl Fn(&mut WindowContext) -> AnyView + Send + Sync + 'static,
) -> Self {
self.tooltip = Some(Box::new(tooltip));
self
}

View File

@@ -271,7 +271,7 @@ impl Clickable for Button {
/// Sets the click event handler for the button.
fn on_click(
mut self,
handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + Send + Sync + 'static,
) -> Self {
self.base = self.base.on_click(handler);
self
@@ -368,7 +368,10 @@ impl ButtonCommon for Button {
/// ```
///
/// This will create a button with a tooltip that displays "This is a tooltip" when hovered over.
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
fn tooltip(
mut self,
tooltip: impl Fn(&mut WindowContext) -> AnyView + Send + Sync + 'static,
) -> Self {
self.base = self.base.tooltip(tooltip);
self
}

View File

@@ -33,7 +33,10 @@ pub trait ButtonCommon: Clickable + Disableable {
///
/// Nearly all interactable elements should have a tooltip. Some example
/// exceptions might a scroll bar, or a slider.
fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
fn tooltip(
self,
tooltip: impl Fn(&mut WindowContext) -> AnyView + Send + Sync + 'static,
) -> Self;
fn layer(self, elevation: ElevationIndex) -> Self;
}
@@ -347,9 +350,9 @@ pub struct ButtonLike {
pub(super) layer: Option<ElevationIndex>,
size: ButtonSize,
rounding: Option<ButtonLikeRounding>,
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + Send + Sync>>,
cursor_style: CursorStyle,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + Send + Sync + 'static>>,
children: SmallVec<[AnyElement; 2]>,
}
@@ -415,7 +418,10 @@ impl SelectableButton for ButtonLike {
}
impl Clickable for ButtonLike {
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
fn on_click(
mut self,
handler: impl Fn(&ClickEvent, &mut WindowContext) + Send + Sync + 'static,
) -> Self {
self.on_click = Some(Box::new(handler));
self
}
@@ -453,7 +459,10 @@ impl ButtonCommon for ButtonLike {
self
}
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
fn tooltip(
mut self,
tooltip: impl Fn(&mut WindowContext) -> AnyView + Send + Sync + 'static,
) -> Self {
self.tooltip = Some(Box::new(tooltip));
self
}

View File

@@ -83,7 +83,7 @@ impl SelectableButton for IconButton {
impl Clickable for IconButton {
fn on_click(
mut self,
handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static + Send + Sync,
) -> Self {
self.base = self.base.on_click(handler);
self
@@ -122,7 +122,10 @@ impl ButtonCommon for IconButton {
self
}
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
fn tooltip(
mut self,
tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static + Send + Sync,
) -> Self {
self.base = self.base.tooltip(tooltip);
self
}

View File

@@ -79,7 +79,10 @@ impl Disableable for ToggleButton {
}
impl Clickable for ToggleButton {
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
fn on_click(
mut self,
handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync,
) -> Self {
self.base = self.base.on_click(handler);
self
}
@@ -105,7 +108,10 @@ impl ButtonCommon for ToggleButton {
self
}
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
fn tooltip(
mut self,
tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static + Send + Sync,
) -> Self {
self.base = self.base.tooltip(tooltip);
self
}

View File

@@ -15,7 +15,7 @@ pub struct Checkbox {
id: ElementId,
checked: Selection,
disabled: bool,
on_click: Option<Box<dyn Fn(&Selection, &mut WindowContext) + 'static>>,
on_click: Option<Box<dyn Fn(&Selection, &mut WindowContext) + Send + Sync + 'static>>,
}
impl Checkbox {
@@ -33,7 +33,10 @@ impl Checkbox {
self
}
pub fn on_click(mut self, handler: impl Fn(&Selection, &mut WindowContext) + 'static) -> Self {
pub fn on_click(
mut self,
handler: impl Fn(&Selection, &mut WindowContext) + Send + Sync + 'static,
) -> Self {
self.on_click = Some(Box::new(handler));
self
}
@@ -169,7 +172,7 @@ pub struct CheckboxWithLabel {
id: ElementId,
label: Label,
checked: Selection,
on_click: Arc<dyn Fn(&Selection, &mut WindowContext) + 'static>,
on_click: Arc<dyn Fn(&Selection, &mut WindowContext) + Send + Sync + 'static>,
}
impl CheckboxWithLabel {
@@ -177,7 +180,7 @@ impl CheckboxWithLabel {
id: impl Into<ElementId>,
label: Label,
checked: Selection,
on_click: impl Fn(&Selection, &mut WindowContext) + 'static,
on_click: impl Fn(&Selection, &mut WindowContext) + Send + Sync + 'static,
) -> Self {
Self {
id: id.into(),

View File

@@ -9,7 +9,7 @@ use gpui::{
};
use menu::{SelectFirst, SelectLast, SelectNext, SelectPrev};
use settings::Settings;
use std::{rc::Rc, time::Duration};
use std::{sync::Arc, time::Duration};
use theme::ThemeSettings;
enum ContextMenuItem {
@@ -21,13 +21,13 @@ enum ContextMenuItem {
label: SharedString,
icon: Option<IconName>,
icon_size: IconSize,
handler: Rc<dyn Fn(Option<&FocusHandle>, &mut WindowContext)>,
handler: Arc<dyn Fn(Option<&FocusHandle>, &mut WindowContext) + Send + Sync>,
action: Option<Box<dyn Action>>,
disabled: bool,
},
CustomEntry {
entry_render: Box<dyn Fn(&mut WindowContext) -> AnyElement>,
handler: Rc<dyn Fn(Option<&FocusHandle>, &mut WindowContext)>,
handler: Arc<dyn Fn(Option<&FocusHandle>, &mut WindowContext) + Send + Sync>,
selectable: bool,
},
}
@@ -97,12 +97,12 @@ impl ContextMenu {
mut self,
label: impl Into<SharedString>,
action: Option<Box<dyn Action>>,
handler: impl Fn(&mut WindowContext) + 'static,
handler: impl Fn(&mut WindowContext) + Send + Sync + 'static,
) -> Self {
self.items.push(ContextMenuItem::Entry {
toggle: None,
label: label.into(),
handler: Rc::new(move |_, cx| handler(cx)),
handler: Arc::new(move |_, cx| handler(cx)),
icon: None,
icon_size: IconSize::Small,
action,
@@ -117,12 +117,12 @@ impl ContextMenu {
toggled: bool,
position: IconPosition,
action: Option<Box<dyn Action>>,
handler: impl Fn(&mut WindowContext) + 'static,
handler: impl Fn(&mut WindowContext) + Send + Sync + 'static,
) -> Self {
self.items.push(ContextMenuItem::Entry {
toggle: Some((position, toggled)),
label: label.into(),
handler: Rc::new(move |_, cx| handler(cx)),
handler: Arc::new(move |_, cx| handler(cx)),
icon: None,
icon_size: IconSize::Small,
action,
@@ -137,7 +137,7 @@ impl ContextMenu {
) -> Self {
self.items.push(ContextMenuItem::CustomEntry {
entry_render: Box::new(entry_render),
handler: Rc::new(|_, _| {}),
handler: Arc::new(|_, _| {}),
selectable: false,
});
self
@@ -145,12 +145,12 @@ impl ContextMenu {
pub fn custom_entry(
mut self,
entry_render: impl Fn(&mut WindowContext) -> AnyElement + 'static,
handler: impl Fn(&mut WindowContext) + 'static,
entry_render: impl Fn(&mut WindowContext) -> AnyElement + Send + Sync + 'static,
handler: impl Fn(&mut WindowContext) + Send + Sync + 'static,
) -> Self {
self.items.push(ContextMenuItem::CustomEntry {
entry_render: Box::new(entry_render),
handler: Rc::new(move |_, cx| handler(cx)),
handler: Arc::new(move |_, cx| handler(cx)),
selectable: true,
});
self
@@ -167,7 +167,7 @@ impl ContextMenu {
label: label.into(),
action: Some(action.boxed_clone()),
handler: Rc::new(move |context, cx| {
handler: Arc::new(move |context, cx| {
if let Some(context) = &context {
cx.focus(context);
}
@@ -190,7 +190,7 @@ impl ContextMenu {
label: label.into(),
action: Some(action.boxed_clone()),
handler: Rc::new(move |context, cx| {
handler: Arc::new(move |context, cx| {
if let Some(context) = &context {
cx.focus(context);
}
@@ -209,7 +209,7 @@ impl ContextMenu {
label: label.into(),
action: Some(action.boxed_clone()),
handler: Rc::new(move |_, cx| cx.dispatch_action(action.boxed_clone())),
handler: Arc::new(move |_, cx| cx.dispatch_action(action.boxed_clone())),
icon: Some(IconName::ArrowUpRight),
icon_size: IconSize::XSmall,
disabled: false,

View File

@@ -10,7 +10,7 @@ pub struct Disclosure {
id: ElementId,
is_open: bool,
selected: bool,
on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync>>,
cursor_style: CursorStyle,
}
@@ -27,7 +27,7 @@ impl Disclosure {
pub fn on_toggle(
mut self,
handler: impl Into<Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>>,
handler: impl Into<Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync>>>,
) -> Self {
self.on_toggle = handler.into();
self
@@ -42,7 +42,10 @@ impl Selectable for Disclosure {
}
impl Clickable for Disclosure {
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
fn on_click(
mut self,
handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync,
) -> Self {
self.on_toggle = Some(Arc::new(handler));
self
}

View File

@@ -57,7 +57,7 @@ struct DropdownMenuTrigger {
selected: bool,
disabled: bool,
cursor_style: CursorStyle,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync>>,
}
impl DropdownMenuTrigger {
@@ -93,7 +93,10 @@ impl Selectable for DropdownMenuTrigger {
}
impl Clickable for DropdownMenuTrigger {
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
fn on_click(
mut self,
handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync,
) -> Self {
self.on_click = Some(Box::new(handler));
self
}

View File

@@ -1,5 +1,5 @@
#![allow(missing_docs)]
use std::{cmp::Ordering, ops::Range, rc::Rc};
use std::{cmp::Ordering, ops::Range, sync::Arc};
use gpui::{
fill, point, size, AnyElement, AppContext, Bounds, Hsla, Point, UniformListDecoration, View,
@@ -33,16 +33,19 @@ impl IndentGuideColors {
pub struct IndentGuides {
colors: IndentGuideColors,
indent_size: Pixels,
compute_indents_fn: Box<dyn Fn(Range<usize>, &mut WindowContext) -> SmallVec<[usize; 64]>>,
compute_indents_fn:
Box<dyn Fn(Range<usize>, &mut WindowContext) -> SmallVec<[usize; 64]> + Send + Sync>,
render_fn: Option<
Box<
dyn Fn(
RenderIndentGuideParams,
&mut WindowContext,
) -> SmallVec<[RenderedIndentGuide; 12]>,
RenderIndentGuideParams,
&mut WindowContext,
) -> SmallVec<[RenderedIndentGuide; 12]>
+ Send
+ Sync,
>,
>,
on_click: Option<Rc<dyn Fn(&IndentGuideLayout, &mut WindowContext)>>,
on_click: Option<Arc<dyn Fn(&IndentGuideLayout, &mut WindowContext) + Send + Sync>>,
}
pub fn indent_guides<V: Render>(
@@ -50,6 +53,8 @@ pub fn indent_guides<V: Render>(
indent_size: Pixels,
colors: IndentGuideColors,
compute_indents_fn: impl Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> SmallVec<[usize; 64]>
+ Send
+ Sync
+ 'static,
) -> IndentGuides {
let compute_indents_fn = Box::new(move |range, cx: &mut WindowContext| {
@@ -68,9 +73,9 @@ impl IndentGuides {
/// Sets the callback that will be called when the user clicks on an indent guide.
pub fn on_click(
mut self,
on_click: impl Fn(&IndentGuideLayout, &mut WindowContext) + 'static,
on_click: impl Fn(&IndentGuideLayout, &mut WindowContext) + Send + Sync + 'static,
) -> Self {
self.on_click = Some(Rc::new(on_click));
self.on_click = Some(Arc::new(on_click));
self
}
@@ -83,7 +88,9 @@ impl IndentGuides {
RenderIndentGuideParams,
&mut WindowContext,
) -> SmallVec<[RenderedIndentGuide; 12]>
+ 'static,
+ 'static
+ Send
+ Sync,
) -> Self {
let render_fn = move |params, cx: &mut WindowContext| {
view.update(cx, |this, cx| render_fn(this, params, cx))
@@ -130,6 +137,8 @@ pub struct IndentGuideLayout {
/// Implements the necessary functionality for rendering indent guides inside a uniform list.
mod uniform_list {
use std::sync::Arc;
use gpui::{DispatchPhase, Hitbox, MouseButton, MouseDownEvent, MouseMoveEvent};
use super::*;
@@ -189,7 +198,7 @@ mod uniform_list {
}
let indent_guides = IndentGuidesElement {
indent_guides: Rc::new(indent_guides),
indent_guides: Arc::new(indent_guides),
colors: self.colors.clone(),
on_hovered_indent_guide_click: self.on_click.clone(),
};
@@ -199,15 +208,17 @@ mod uniform_list {
struct IndentGuidesElement {
colors: IndentGuideColors,
indent_guides: Rc<SmallVec<[RenderedIndentGuide; 12]>>,
on_hovered_indent_guide_click: Option<Rc<dyn Fn(&IndentGuideLayout, &mut WindowContext)>>,
indent_guides: Arc<SmallVec<[RenderedIndentGuide; 12]>>,
on_hovered_indent_guide_click:
Option<Arc<dyn Fn(&IndentGuideLayout, &mut WindowContext) + Send + Sync>>,
}
enum IndentGuidesElementPrepaintState {
Static,
Interactive {
hitboxes: Rc<SmallVec<[Hitbox; 12]>>,
on_hovered_indent_guide_click: Rc<dyn Fn(&IndentGuideLayout, &mut WindowContext)>,
hitboxes: Arc<SmallVec<[Hitbox; 12]>>,
on_hovered_indent_guide_click:
Arc<dyn Fn(&IndentGuideLayout, &mut WindowContext) + Send + Sync>,
},
}
@@ -243,7 +254,7 @@ mod uniform_list {
.map(|guide| cx.insert_hitbox(guide.hitbox.unwrap_or(guide.bounds), false))
.collect();
Self::PrepaintState::Interactive {
hitboxes: Rc::new(hitboxes),
hitboxes: Arc::new(hitboxes),
on_hovered_indent_guide_click,
}
} else {

View File

@@ -20,7 +20,7 @@ pub struct ListHeader {
/// It will obscure the `end_slot` when visible.
end_hover_slot: Option<AnyElement>,
toggle: Option<bool>,
on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + Send + Sync + 'static>>,
inset: bool,
selected: bool,
}
@@ -46,7 +46,7 @@ impl ListHeader {
pub fn on_toggle(
mut self,
on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + Send + Sync + 'static,
) -> Self {
self.on_toggle = Some(Arc::new(on_toggle));
self

View File

@@ -32,10 +32,11 @@ pub struct ListItem {
end_hover_slot: Option<AnyElement>,
toggle: Option<bool>,
inset: bool,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
on_secondary_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync>>,
on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync>>,
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static + Send + Sync>>,
on_secondary_mouse_down:
Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static + Send + Sync>>,
children: SmallVec<[AnyElement; 2]>,
selectable: bool,
overflow_x: bool,
@@ -75,20 +76,26 @@ impl ListItem {
self
}
pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
pub fn on_click(
mut self,
handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync,
) -> Self {
self.on_click = Some(Box::new(handler));
self
}
pub fn on_secondary_mouse_down(
mut self,
handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static + Send + Sync,
) -> Self {
self.on_secondary_mouse_down = Some(Box::new(handler));
self
}
pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
pub fn tooltip(
mut self,
tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static + Send + Sync,
) -> Self {
self.tooltip = Some(Box::new(tooltip));
self
}
@@ -115,7 +122,7 @@ impl ListItem {
pub fn on_toggle(
mut self,
on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync,
) -> Self {
self.on_toggle = Some(Arc::new(on_toggle));
self

View File

@@ -8,19 +8,19 @@ use crate::{prelude::*, IconButtonShape};
pub struct NumericStepper {
id: ElementId,
value: SharedString,
on_decrement: Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
on_increment: Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
on_decrement: Box<dyn Fn(&ClickEvent, &mut WindowContext) + Send + Sync + 'static>,
on_increment: Box<dyn Fn(&ClickEvent, &mut WindowContext) + Send + Sync + 'static>,
/// Whether to reserve space for the reset button.
reserve_space_for_reset: bool,
on_reset: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
on_reset: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + Send + Sync + 'static>>,
}
impl NumericStepper {
pub fn new(
id: impl Into<ElementId>,
value: impl Into<SharedString>,
on_decrement: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
on_increment: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
on_decrement: impl Fn(&ClickEvent, &mut WindowContext) + Send + Sync + 'static,
on_increment: impl Fn(&ClickEvent, &mut WindowContext) + Send + Sync + 'static,
) -> Self {
Self {
id: id.into(),
@@ -39,7 +39,7 @@ impl NumericStepper {
pub fn on_reset(
mut self,
on_reset: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
on_reset: impl Fn(&ClickEvent, &mut WindowContext) + Send + Sync + 'static,
) -> Self {
self.on_reset = Some(Box::new(on_reset));
self

View File

@@ -1,6 +1,6 @@
#![allow(missing_docs)]
use std::{cell::RefCell, rc::Rc};
use std::sync::Arc;
use gpui::{
anchored, deferred, div, point, prelude::FluentBuilder, px, size, AnchorCorner, AnyElement,
@@ -8,14 +8,15 @@ use gpui::{
InteractiveElement, IntoElement, LayoutId, Length, ManagedView, MouseDownEvent, ParentElement,
Pixels, Point, Style, View, VisualContext, WindowContext,
};
use parking_lot::Mutex;
use crate::prelude::*;
pub trait PopoverTrigger: IntoElement + Clickable + Selectable + 'static {}
pub trait PopoverTrigger: IntoElement + Clickable + Selectable + Send + Sync + 'static {}
impl<T: IntoElement + Clickable + Selectable + 'static> PopoverTrigger for T {}
impl<T: IntoElement + Clickable + Selectable + Send + Sync + 'static> PopoverTrigger for T {}
pub struct PopoverMenuHandle<M>(Rc<RefCell<Option<PopoverMenuHandleState<M>>>>);
pub struct PopoverMenuHandle<M>(Arc<Mutex<Option<PopoverMenuHandleState<M>>>>);
impl<M> Clone for PopoverMenuHandle<M> {
fn clone(&self) -> Self {
@@ -25,33 +26,33 @@ impl<M> Clone for PopoverMenuHandle<M> {
impl<M> Default for PopoverMenuHandle<M> {
fn default() -> Self {
Self(Rc::default())
Self(Arc::default())
}
}
struct PopoverMenuHandleState<M> {
menu_builder: Rc<dyn Fn(&mut WindowContext) -> Option<View<M>>>,
menu: Rc<RefCell<Option<View<M>>>>,
menu_builder: Arc<dyn Fn(&mut WindowContext) -> Option<View<M>> + Send + Sync>,
menu: Arc<Mutex<Option<View<M>>>>,
}
impl<M: ManagedView> PopoverMenuHandle<M> {
pub fn show(&self, cx: &mut WindowContext) {
if let Some(state) = self.0.borrow().as_ref() {
if let Some(state) = self.0.lock().as_ref() {
show_menu(&state.menu_builder, &state.menu, cx);
}
}
pub fn hide(&self, cx: &mut WindowContext) {
if let Some(state) = self.0.borrow().as_ref() {
if let Some(menu) = state.menu.borrow().as_ref() {
if let Some(state) = self.0.lock().as_ref() {
if let Some(menu) = state.menu.lock().as_ref() {
menu.update(cx, |_, cx| cx.emit(DismissEvent));
}
}
}
pub fn toggle(&self, cx: &mut WindowContext) {
if let Some(state) = self.0.borrow().as_ref() {
if state.menu.borrow().is_some() {
if let Some(state) = self.0.lock().as_ref() {
if state.menu.lock().is_some() {
self.hide(cx);
} else {
self.show(cx);
@@ -61,16 +62,16 @@ impl<M: ManagedView> PopoverMenuHandle<M> {
pub fn is_deployed(&self) -> bool {
self.0
.borrow()
.lock()
.as_ref()
.map_or(false, |state| state.menu.borrow().as_ref().is_some())
.map_or(false, |state| state.menu.lock().as_ref().is_some())
}
pub fn is_focused(&self, cx: &WindowContext) -> bool {
self.0.borrow().as_ref().map_or(false, |state| {
self.0.lock().as_ref().map_or(false, |state| {
state
.menu
.borrow()
.lock()
.as_ref()
.map_or(false, |view| view.focus_handle(cx).is_focused(cx))
})
@@ -82,13 +83,18 @@ pub struct PopoverMenu<M: ManagedView> {
child_builder: Option<
Box<
dyn FnOnce(
Rc<RefCell<Option<View<M>>>>,
Option<Rc<dyn Fn(&mut WindowContext) -> Option<View<M>> + 'static>>,
Arc<Mutex<Option<View<M>>>>,
Option<
Arc<dyn Fn(&mut WindowContext) -> Option<View<M>> + Send + Sync + 'static>,
>,
) -> AnyElement
+ 'static,
+ 'static
+ Send
+ Sync,
>,
>,
menu_builder: Option<Rc<dyn Fn(&mut WindowContext) -> Option<View<M>> + 'static>>,
menu_builder:
Option<Arc<dyn Fn(&mut WindowContext) -> Option<View<M>> + Send + Sync + 'static>>,
anchor: AnchorCorner,
attach: Option<AnchorCorner>,
offset: Option<Point<Pixels>>,
@@ -116,8 +122,11 @@ impl<M: ManagedView> PopoverMenu<M> {
self
}
pub fn menu(mut self, f: impl Fn(&mut WindowContext) -> Option<View<M>> + 'static) -> Self {
self.menu_builder = Some(Rc::new(f));
pub fn menu(
mut self,
f: impl Fn(&mut WindowContext) -> Option<View<M>> + Send + Sync + 'static,
) -> Self {
self.menu_builder = Some(Arc::new(f));
self
}
@@ -128,7 +137,7 @@ impl<M: ManagedView> PopoverMenu<M> {
pub fn trigger<T: PopoverTrigger>(mut self, t: T) -> Self {
self.child_builder = Some(Box::new(|menu, builder| {
let open = menu.borrow().is_some();
let open = menu.lock().is_some();
t.selected(open)
.when_some(builder, |el, builder| {
el.on_click(move |_, cx| show_menu(&builder, &menu, cx))
@@ -179,8 +188,8 @@ impl<M: ManagedView> PopoverMenu<M> {
}
fn show_menu<M: ManagedView>(
builder: &Rc<dyn Fn(&mut WindowContext) -> Option<View<M>>>,
menu: &Rc<RefCell<Option<View<M>>>>,
builder: &Arc<dyn Fn(&mut WindowContext) -> Option<View<M>> + Send + Sync>,
menu: &Arc<Mutex<Option<View<M>>>>,
cx: &mut WindowContext,
) {
let Some(new_menu) = (builder)(cx) else {
@@ -195,24 +204,24 @@ fn show_menu<M: ManagedView>(
cx.focus(previous_focus_handle);
}
}
*menu2.borrow_mut() = None;
*menu2.lock() = None;
cx.refresh();
})
.detach();
cx.focus_view(&new_menu);
*menu.borrow_mut() = Some(new_menu);
*menu.lock() = Some(new_menu);
cx.refresh();
}
pub struct PopoverMenuElementState<M> {
menu: Rc<RefCell<Option<View<M>>>>,
menu: Arc<Mutex<Option<View<M>>>>,
child_bounds: Option<Bounds<Pixels>>,
}
impl<M> Clone for PopoverMenuElementState<M> {
fn clone(&self) -> Self {
Self {
menu: Rc::clone(&self.menu),
menu: Arc::clone(&self.menu),
child_bounds: self.child_bounds,
}
}
@@ -221,7 +230,7 @@ impl<M> Clone for PopoverMenuElementState<M> {
impl<M> Default for PopoverMenuElementState<M> {
fn default() -> Self {
Self {
menu: Rc::default(),
menu: Arc::default(),
child_bounds: None,
}
}
@@ -231,7 +240,7 @@ pub struct PopoverMenuFrameState<M: ManagedView> {
child_layout_id: Option<LayoutId>,
child_element: Option<AnyElement>,
menu_element: Option<AnyElement>,
menu_handle: Rc<RefCell<Option<View<M>>>>,
menu_handle: Arc<Mutex<Option<View<M>>>>,
}
impl<M: ManagedView> Element for PopoverMenu<M> {
@@ -253,7 +262,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
let element_state = element_state.unwrap_or_default();
let mut menu_layout_id = None;
let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
let menu_element = element_state.menu.lock().as_mut().map(|menu| {
let mut anchored = anchored()
.snap_to_window_with_margin(px(8.))
.anchor(self.anchor);
@@ -276,7 +285,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
if let Some(trigger_handle) = self.trigger_handle.take() {
if let Some(menu_builder) = self.menu_builder.clone() {
*trigger_handle.0.borrow_mut() = Some(PopoverMenuHandleState {
*trigger_handle.0.lock() = Some(PopoverMenuHandleState {
menu_builder,
menu: element_state.menu.clone(),
});
@@ -359,7 +368,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
// want a click on the toggle to re-open it.
cx.on_mouse_event(move |_: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble && child_hitbox.is_hovered(cx) {
if let Some(menu) = menu_handle.borrow().as_ref() {
if let Some(menu) = menu_handle.lock().as_ref() {
menu.update(cx, |_, cx| {
cx.emit(DismissEvent);
});

View File

@@ -10,7 +10,7 @@ pub struct RadioWithLabel {
id: ElementId,
label: Label,
selected: bool,
on_click: Arc<dyn Fn(&bool, &mut WindowContext) + 'static>,
on_click: Arc<dyn Fn(&bool, &mut WindowContext) + Send + Sync + 'static>,
}
impl RadioWithLabel {
@@ -18,7 +18,7 @@ impl RadioWithLabel {
id: impl Into<ElementId>,
label: Label,
selected: bool,
on_click: impl Fn(&bool, &mut WindowContext) + 'static,
on_click: impl Fn(&bool, &mut WindowContext) + Send + Sync + 'static,
) -> Self {
Self {
id: id.into(),

View File

@@ -1,6 +1,6 @@
#![allow(missing_docs)]
use std::{cell::RefCell, rc::Rc};
use std::{cell::RefCell, rc::Rc, sync::Arc};
use gpui::{
anchored, deferred, div, px, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase,
@@ -11,19 +11,22 @@ use gpui::{
pub struct RightClickMenu<M: ManagedView> {
id: ElementId,
child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement + 'static>>,
menu_builder: Option<Rc<dyn Fn(&mut WindowContext) -> View<M> + 'static>>,
child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement + Send + Sync + 'static>>,
menu_builder: Option<Arc<dyn Fn(&mut WindowContext) -> View<M> + Send + Sync + 'static>>,
anchor: Option<AnchorCorner>,
attach: Option<AnchorCorner>,
}
impl<M: ManagedView> RightClickMenu<M> {
pub fn menu(mut self, f: impl Fn(&mut WindowContext) -> View<M> + 'static) -> Self {
self.menu_builder = Some(Rc::new(f));
pub fn menu(
mut self,
f: impl Fn(&mut WindowContext) -> View<M> + Send + Sync + 'static,
) -> Self {
self.menu_builder = Some(Arc::new(f));
self
}
pub fn trigger<E: IntoElement + 'static>(mut self, e: E) -> Self {
pub fn trigger<E: IntoElement + Sync + 'static>(mut self, e: E) -> Self {
self.child_builder = Some(Box::new(move |_| e.into_any_element()));
self
}

View File

@@ -1,5 +1,6 @@
#![allow(missing_docs)]
use std::{cell::Cell, ops::Range, rc::Rc};
use parking_lot::Mutex;
use std::{ops::Range, sync::Arc};
use crate::{prelude::*, px, relative, IntoElement};
use gpui::{
@@ -32,7 +33,7 @@ impl ScrollableHandle {
fn content_size(&self) -> Option<ContentSize> {
match self {
ScrollableHandle::Uniform(handle) => Some(ContentSize {
size: handle.0.borrow().last_item_size.map(|size| size.contents)?,
size: handle.0.lock().last_item_size.map(|size| size.contents)?,
scroll_adjustment: None,
}),
ScrollableHandle::NonUniform(handle) => {
@@ -59,21 +60,21 @@ impl ScrollableHandle {
}
fn set_offset(&self, point: Point<Pixels>) {
let base_handle = match self {
ScrollableHandle::Uniform(handle) => &handle.0.borrow().base_handle,
ScrollableHandle::Uniform(handle) => &handle.0.lock().base_handle,
ScrollableHandle::NonUniform(handle) => &handle,
};
base_handle.set_offset(point);
}
fn offset(&self) -> Point<Pixels> {
let base_handle = match self {
ScrollableHandle::Uniform(handle) => &handle.0.borrow().base_handle,
ScrollableHandle::Uniform(handle) => &handle.0.lock().base_handle,
ScrollableHandle::NonUniform(handle) => &handle,
};
base_handle.offset()
}
fn viewport(&self) -> Bounds<Pixels> {
let base_handle = match self {
ScrollableHandle::Uniform(handle) => &handle.0.borrow().base_handle,
ScrollableHandle::Uniform(handle) => &handle.0.lock().base_handle,
ScrollableHandle::NonUniform(handle) => &handle,
};
base_handle.bounds()
@@ -95,7 +96,7 @@ impl From<ScrollHandle> for ScrollableHandle {
#[derive(Clone, Debug)]
pub struct ScrollbarState {
// If Some(), there's an active drag, offset by percentage from the origin of a thumb.
drag: Rc<Cell<Option<f32>>>,
drag: Arc<Mutex<Option<f32>>>,
parent_id: Option<EntityId>,
scroll_handle: ScrollableHandle,
}
@@ -120,7 +121,7 @@ impl ScrollbarState {
}
pub fn is_dragging(&self) -> bool {
self.drag.get().is_some()
self.drag.lock().is_some()
}
fn thumb_range(&self, axis: ScrollbarAxis) -> Option<Range<f32>> {
@@ -302,7 +303,7 @@ impl Element for Scrollbar {
let thumb_offset = (event.position.along(axis)
- thumb_bounds.origin.along(axis))
/ bounds.size.along(axis);
state.drag.set(Some(thumb_offset));
state.drag.lock() = Some(thumb_offset);
} else if let Some(ContentSize {
size: item_size, ..
}) = scroll.content_size()
@@ -341,7 +342,7 @@ impl Element for Scrollbar {
let state = self.state.clone();
let kind = self.kind;
cx.on_mouse_event(move |event: &MouseMoveEvent, _, cx| {
if let Some(drag_state) = state.drag.get().filter(|_| event.dragging()) {
if let Some(drag_state) = state.drag.lock().filter(|_| event.dragging()) {
if let Some(ContentSize {
size: item_size, ..
}) = scroll.content_size()
@@ -374,7 +375,7 @@ impl Element for Scrollbar {
}
}
} else {
state.drag.set(None);
state.drag.lock().state.drag.set(None);
}
});
let state = self.state.clone();

View File

@@ -3,7 +3,10 @@ use gpui::{ClickEvent, CursorStyle, WindowContext};
/// A trait for elements that can be clicked. Enables the use of the `on_click` method.
pub trait Clickable {
/// Sets the click handler that will fire whenever the element is clicked.
fn on_click(self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self;
fn on_click(
self,
handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static + Send + Sync,
) -> Self;
/// Sets the cursor style when hovering over the element.
fn cursor_style(self, cursor_style: CursorStyle) -> Self;
}