Compare commits
5 Commits
fix-git-ht
...
arena-conc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cfd1386d9 | ||
|
|
ccfa29d78b | ||
|
|
a76d3950f4 | ||
|
|
3afa772a1b | ||
|
|
5c22c8d03b |
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -13108,6 +13108,7 @@ dependencies = [
|
||||
"gpui",
|
||||
"itertools 0.13.0",
|
||||
"menu",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"settings",
|
||||
"smallvec",
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.)),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user