mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-20 02:47:34 +03:00
Refactor out element context from GPUI
This commit is contained in:
parent
2f9958621b
commit
df4566fd1e
@ -640,8 +640,11 @@ impl<'a> VisualTestContext {
|
||||
.as_ref()
|
||||
.expect("Can't draw to this window without a root view")
|
||||
.entity_id();
|
||||
cx.with_view_id(entity_id, |cx| {
|
||||
f(cx).draw(origin, space, cx);
|
||||
|
||||
cx.with_element_context(|cx| {
|
||||
cx.with_view_id(entity_id, |cx| {
|
||||
f(cx).draw(origin, space, cx);
|
||||
})
|
||||
});
|
||||
|
||||
cx.refresh();
|
||||
|
@ -35,56 +35,12 @@
|
||||
//! your own custom layout algorithm or rendering a code editor.
|
||||
|
||||
use crate::{
|
||||
util::FluentBuilder, AppContext, ArenaBox, AvailableSpace, BorrowWindow, Bounds, ContentMask,
|
||||
ElementId, ElementStateBox, EntityId, IsZero, LayoutId, Pixels, Point, Size, ViewContext,
|
||||
Window, WindowContext, ELEMENT_ARENA,
|
||||
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, ElementContext, ElementId, LayoutId,
|
||||
Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
pub(crate) use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::{Borrow, BorrowMut},
|
||||
fmt::Debug,
|
||||
mem,
|
||||
ops::DerefMut,
|
||||
};
|
||||
use util::post_inc;
|
||||
|
||||
/// This context is used for assisting in the implementation of the element trait
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct ElementContext<'a> {
|
||||
pub(crate) cx: WindowContext<'a>,
|
||||
}
|
||||
|
||||
impl<'a> WindowContext<'a> {
|
||||
pub(crate) fn into_element_cx(self) -> ElementContext<'a> {
|
||||
ElementContext { cx: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Borrow<AppContext> for ElementContext<'a> {
|
||||
fn borrow(&self) -> &AppContext {
|
||||
self.cx.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BorrowMut<AppContext> for ElementContext<'a> {
|
||||
fn borrow_mut(&mut self) -> &mut AppContext {
|
||||
self.cx.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Borrow<Window> for ElementContext<'a> {
|
||||
fn borrow(&self) -> &Window {
|
||||
self.cx.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BorrowMut<Window> for ElementContext<'a> {
|
||||
fn borrow_mut(&mut self) -> &mut Window {
|
||||
self.cx.borrow_mut()
|
||||
}
|
||||
}
|
||||
use std::{any::Any, fmt::Debug, ops::DerefMut};
|
||||
|
||||
/// Implemented by types that participate in laying out and painting the contents of a window.
|
||||
/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
|
||||
@ -593,250 +549,3 @@ impl Element for () {
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ElementContext<'a> {
|
||||
/// Pushes the given element id onto the global stack and invokes the given closure
|
||||
/// with a `GlobalElementId`, which disambiguates the given id in the context of its ancestor
|
||||
/// ids. Because elements are discarded and recreated on each frame, the `GlobalElementId` is
|
||||
/// used to associate state with identified elements across separate frames.
|
||||
fn with_element_id<R>(
|
||||
&mut self,
|
||||
id: Option<impl Into<ElementId>>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
if let Some(id) = id.map(Into::into) {
|
||||
let window = self.window_mut();
|
||||
window.element_id_stack.push(id);
|
||||
let result = f(self);
|
||||
let window: &mut Window = self.borrow_mut();
|
||||
window.element_id_stack.pop();
|
||||
result
|
||||
} else {
|
||||
f(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke the given function with the given content mask after intersecting it
|
||||
/// with the current mask.
|
||||
fn with_content_mask<R>(
|
||||
&mut self,
|
||||
mask: Option<ContentMask<Pixels>>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
if let Some(mask) = mask {
|
||||
let mask = mask.intersect(&self.content_mask());
|
||||
self.window_mut().next_frame.content_mask_stack.push(mask);
|
||||
let result = f(self);
|
||||
self.window_mut().next_frame.content_mask_stack.pop();
|
||||
result
|
||||
} else {
|
||||
f(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke the given function with the content mask reset to that
|
||||
/// of the window.
|
||||
fn break_content_mask<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
let mask = ContentMask {
|
||||
bounds: Bounds {
|
||||
origin: Point::default(),
|
||||
size: self.window().viewport_size,
|
||||
},
|
||||
};
|
||||
let new_stacking_order_id =
|
||||
post_inc(&mut self.window_mut().next_frame.next_stacking_order_id);
|
||||
let new_root_z_index = post_inc(&mut self.window_mut().next_frame.next_root_z_index);
|
||||
let old_stacking_order = mem::take(&mut self.window_mut().next_frame.z_index_stack);
|
||||
self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id;
|
||||
self.window_mut()
|
||||
.next_frame
|
||||
.z_index_stack
|
||||
.push(new_root_z_index);
|
||||
self.window_mut().next_frame.content_mask_stack.push(mask);
|
||||
let result = f(self);
|
||||
self.window_mut().next_frame.content_mask_stack.pop();
|
||||
self.window_mut().next_frame.z_index_stack = old_stacking_order;
|
||||
result
|
||||
}
|
||||
|
||||
/// Called during painting to invoke the given closure in a new stacking context. The given
|
||||
/// z-index is interpreted relative to the previous call to `stack`.
|
||||
fn with_z_index<R>(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
let new_stacking_order_id =
|
||||
post_inc(&mut self.window_mut().next_frame.next_stacking_order_id);
|
||||
let old_stacking_order_id = mem::replace(
|
||||
&mut self.window_mut().next_frame.z_index_stack.id,
|
||||
new_stacking_order_id,
|
||||
);
|
||||
self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id;
|
||||
self.window_mut().next_frame.z_index_stack.push(z_index);
|
||||
let result = f(self);
|
||||
self.window_mut().next_frame.z_index_stack.id = old_stacking_order_id;
|
||||
self.window_mut().next_frame.z_index_stack.pop();
|
||||
result
|
||||
}
|
||||
|
||||
/// Updates the global element offset relative to the current offset. This is used to implement
|
||||
/// scrolling.
|
||||
fn with_element_offset<R>(
|
||||
&mut self,
|
||||
offset: Point<Pixels>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
if offset.is_zero() {
|
||||
return f(self);
|
||||
};
|
||||
|
||||
let abs_offset = self.element_offset() + offset;
|
||||
self.with_absolute_element_offset(abs_offset, f)
|
||||
}
|
||||
|
||||
/// Updates the global element offset based on the given offset. This is used to implement
|
||||
/// drag handles and other manual painting of elements.
|
||||
fn with_absolute_element_offset<R>(
|
||||
&mut self,
|
||||
offset: Point<Pixels>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
self.window_mut()
|
||||
.next_frame
|
||||
.element_offset_stack
|
||||
.push(offset);
|
||||
let result = f(self);
|
||||
self.window_mut().next_frame.element_offset_stack.pop();
|
||||
result
|
||||
}
|
||||
|
||||
/// Obtain the current element offset.
|
||||
fn element_offset(&self) -> Point<Pixels> {
|
||||
self.window()
|
||||
.next_frame
|
||||
.element_offset_stack
|
||||
.last()
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Obtain the current content mask.
|
||||
fn content_mask(&self) -> ContentMask<Pixels> {
|
||||
self.window()
|
||||
.next_frame
|
||||
.content_mask_stack
|
||||
.last()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| ContentMask {
|
||||
bounds: Bounds {
|
||||
origin: Point::default(),
|
||||
size: self.window().viewport_size,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// The size of an em for the base font of the application. Adjusting this value allows the
|
||||
/// UI to scale, just like zooming a web page.
|
||||
fn rem_size(&self) -> Pixels {
|
||||
self.window().rem_size
|
||||
}
|
||||
|
||||
fn parent_view_id(&self) -> EntityId {
|
||||
*self
|
||||
.window
|
||||
.next_frame
|
||||
.view_stack
|
||||
.last()
|
||||
.expect("a view should always be on the stack while drawing")
|
||||
}
|
||||
|
||||
/// Updates or initializes state for an element with the given id that lives across multiple
|
||||
/// frames. If an element with this ID existed in the rendered frame, its state will be passed
|
||||
/// to the given closure. The state returned by the closure will be stored so it can be referenced
|
||||
/// when drawing the next frame.
|
||||
pub(crate) fn with_element_state<S, R>(
|
||||
&mut self,
|
||||
id: ElementId,
|
||||
f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
|
||||
) -> R
|
||||
where
|
||||
S: 'static,
|
||||
{
|
||||
self.with_element_id(Some(id), |cx| {
|
||||
let global_id = cx.window().element_id_stack.clone();
|
||||
|
||||
if let Some(any) = cx
|
||||
.window_mut()
|
||||
.next_frame
|
||||
.element_states
|
||||
.remove(&global_id)
|
||||
.or_else(|| {
|
||||
cx.window_mut()
|
||||
.rendered_frame
|
||||
.element_states
|
||||
.remove(&global_id)
|
||||
})
|
||||
{
|
||||
let ElementStateBox {
|
||||
inner,
|
||||
parent_view_id,
|
||||
#[cfg(debug_assertions)]
|
||||
type_name
|
||||
} = any;
|
||||
// Using the extra inner option to avoid needing to reallocate a new box.
|
||||
let mut state_box = inner
|
||||
.downcast::<Option<S>>()
|
||||
.map_err(|_| {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
anyhow::anyhow!(
|
||||
"invalid element state type for id, requested_type {:?}, actual type: {:?}",
|
||||
std::any::type_name::<S>(),
|
||||
type_name
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
anyhow::anyhow!(
|
||||
"invalid element state type for id, requested_type {:?}",
|
||||
std::any::type_name::<S>(),
|
||||
)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Actual: Option<AnyElement> <- View
|
||||
// Requested: () <- AnyElement
|
||||
let state = state_box
|
||||
.take()
|
||||
.expect("element state is already on the stack");
|
||||
let (result, state) = f(Some(state), cx);
|
||||
state_box.replace(state);
|
||||
cx.window_mut()
|
||||
.next_frame
|
||||
.element_states
|
||||
.insert(global_id, ElementStateBox {
|
||||
inner: state_box,
|
||||
parent_view_id,
|
||||
#[cfg(debug_assertions)]
|
||||
type_name
|
||||
});
|
||||
result
|
||||
} else {
|
||||
let (result, state) = f(None, cx);
|
||||
let parent_view_id = cx.parent_view_id();
|
||||
cx.window_mut()
|
||||
.next_frame
|
||||
.element_states
|
||||
.insert(global_id,
|
||||
ElementStateBox {
|
||||
inner: Box::new(Some(state)),
|
||||
parent_view_id,
|
||||
#[cfg(debug_assertions)]
|
||||
type_name: std::any::type_name::<S>()
|
||||
}
|
||||
|
||||
);
|
||||
result
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use refineable::Refineable as _;
|
||||
|
||||
use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
|
||||
use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRefinement, Styled};
|
||||
|
||||
/// Construct a canvas element with the given paint callback.
|
||||
/// Useful for adding short term custom drawing to a view.
|
||||
pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContext)) -> Canvas {
|
||||
pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut ElementContext)) -> Canvas {
|
||||
Canvas {
|
||||
paint_callback: Some(Box::new(callback)),
|
||||
style: StyleRefinement::default(),
|
||||
@ -14,7 +14,7 @@ pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContex
|
||||
/// A canvas element, meant for accessing the low level paint API without defining a whole
|
||||
/// custom element
|
||||
pub struct Canvas {
|
||||
paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut WindowContext)>>,
|
||||
paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut ElementContext)>>,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ impl Element for Canvas {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
_: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (crate::LayoutId, Self::State) {
|
||||
let mut style = Style::default();
|
||||
style.refine(&self.style);
|
||||
@ -44,7 +44,7 @@ impl Element for Canvas {
|
||||
(layout_id, style)
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut WindowContext) {
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut ElementContext) {
|
||||
style.paint(bounds, cx, |cx| {
|
||||
(self.paint_callback.take().unwrap())(&bounds, cx)
|
||||
});
|
||||
|
@ -24,10 +24,11 @@
|
||||
|
||||
use crate::{
|
||||
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
|
||||
Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement, IsZero,
|
||||
KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||
MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size,
|
||||
StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext,
|
||||
Bounds, ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle,
|
||||
IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton,
|
||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
|
||||
ScrollWheelEvent, SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task,
|
||||
View, Visibility, WindowContext,
|
||||
};
|
||||
|
||||
use collections::HashMap;
|
||||
@ -1052,7 +1053,7 @@ impl Element for Div {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let mut child_layout_ids = SmallVec::new();
|
||||
let (layout_id, interactive_state) = self.interactivity.layout(
|
||||
@ -1082,7 +1083,7 @@ impl Element for Div {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
||||
let mut child_max = Point::default();
|
||||
@ -1233,8 +1234,8 @@ impl Interactivity {
|
||||
pub fn layout(
|
||||
&mut self,
|
||||
element_state: Option<InteractiveElementState>,
|
||||
cx: &mut WindowContext,
|
||||
f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
|
||||
cx: &mut ElementContext,
|
||||
f: impl FnOnce(Style, &mut ElementContext) -> LayoutId,
|
||||
) -> (LayoutId, InteractiveElementState) {
|
||||
let mut element_state = element_state.unwrap_or_default();
|
||||
|
||||
@ -1281,8 +1282,8 @@ impl Interactivity {
|
||||
bounds: Bounds<Pixels>,
|
||||
content_size: Size<Pixels>,
|
||||
element_state: &mut InteractiveElementState,
|
||||
cx: &mut WindowContext,
|
||||
f: impl FnOnce(&Style, Point<Pixels>, &mut WindowContext),
|
||||
cx: &mut ElementContext,
|
||||
f: impl FnOnce(&Style, Point<Pixels>, &mut ElementContext),
|
||||
) {
|
||||
let style = self.compute_style(Some(bounds), element_state, cx);
|
||||
let z_index = style.z_index.unwrap_or(0);
|
||||
@ -1295,7 +1296,7 @@ impl Interactivity {
|
||||
.insert(debug_selector.clone(), bounds);
|
||||
}
|
||||
|
||||
let paint_hover_group_handler = |cx: &mut WindowContext| {
|
||||
let paint_hover_group_handler = |cx: &mut ElementContext| {
|
||||
let hover_group_bounds = self
|
||||
.group_hover_style
|
||||
.as_ref()
|
||||
@ -1319,7 +1320,7 @@ impl Interactivity {
|
||||
}
|
||||
|
||||
cx.with_z_index(z_index, |cx| {
|
||||
style.paint(bounds, cx, |cx| {
|
||||
style.paint(bounds, cx, |cx: &mut ElementContext| {
|
||||
cx.with_text_style(style.text_style().cloned(), |cx| {
|
||||
cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
|
||||
#[cfg(debug_assertions)]
|
||||
@ -1333,7 +1334,7 @@ impl Interactivity {
|
||||
let element_id = format!("{:?}", self.element_id.as_ref().unwrap());
|
||||
let str_len = element_id.len();
|
||||
|
||||
let render_debug_text = |cx: &mut WindowContext| {
|
||||
let render_debug_text = |cx: &mut ElementContext| {
|
||||
if let Some(text) = cx
|
||||
.text_system()
|
||||
.shape_text(
|
||||
@ -1902,7 +1903,7 @@ impl Interactivity {
|
||||
&self,
|
||||
bounds: Option<Bounds<Pixels>>,
|
||||
element_state: &mut InteractiveElementState,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> Style {
|
||||
let mut style = Style::default();
|
||||
style.refine(&self.base_style);
|
||||
@ -2103,12 +2104,12 @@ where
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
state: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
self.element.request_layout(state, cx)
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
|
||||
self.element.paint(bounds, state, cx)
|
||||
}
|
||||
}
|
||||
@ -2178,12 +2179,12 @@ where
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
state: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
self.element.request_layout(state, cx)
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
|
||||
self.element.paint(bounds, state, cx)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
point, size, Bounds, DevicePixels, Element, ImageData, InteractiveElement,
|
||||
point, size, Bounds, DevicePixels, Element, ElementContext, ImageData, InteractiveElement,
|
||||
InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedUrl, Size,
|
||||
StyleRefinement, Styled, WindowContext,
|
||||
StyleRefinement, Styled,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use media::core_video::CVImageBuffer;
|
||||
@ -81,7 +81,7 @@ impl Element for Img {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
self.interactivity
|
||||
.layout(element_state, cx, |style, cx| cx.request_layout(&style, []))
|
||||
@ -91,7 +91,7 @@ impl Element for Img {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
let source = self.source.clone();
|
||||
self.interactivity.paint(
|
||||
|
@ -359,7 +359,7 @@ impl Element for List {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
_state: Option<Self::State>,
|
||||
cx: &mut crate::WindowContext,
|
||||
cx: &mut crate::ElementContext,
|
||||
) -> (crate::LayoutId, Self::State) {
|
||||
let mut style = Style::default();
|
||||
style.refine(&self.style);
|
||||
@ -373,7 +373,7 @@ impl Element for List {
|
||||
&mut self,
|
||||
bounds: Bounds<crate::Pixels>,
|
||||
_state: &mut Self::State,
|
||||
cx: &mut crate::WindowContext,
|
||||
cx: &mut crate::ElementContext,
|
||||
) {
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
|
||||
|
@ -2,8 +2,8 @@ use smallvec::SmallVec;
|
||||
use taffy::style::{Display, Position};
|
||||
|
||||
use crate::{
|
||||
point, AnyElement, BorrowWindow, Bounds, Element, IntoElement, LayoutId, ParentElement, Pixels,
|
||||
Point, Size, Style, WindowContext,
|
||||
point, AnyElement, Bounds, Element, ElementContext, IntoElement, LayoutId, ParentElement,
|
||||
Pixels, Point, Size, Style,
|
||||
};
|
||||
|
||||
/// The state that the overlay element uses to track its children.
|
||||
@ -74,7 +74,7 @@ impl Element for Overlay {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
_: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (crate::LayoutId, Self::State) {
|
||||
let child_layout_ids = self
|
||||
.children
|
||||
@ -97,7 +97,7 @@ impl Element for Overlay {
|
||||
&mut self,
|
||||
bounds: crate::Bounds<crate::Pixels>,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
if element_state.child_layout_ids.is_empty() {
|
||||
return;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
|
||||
IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext,
|
||||
Bounds, Element, ElementContext, ElementId, InteractiveElement, InteractiveElementState,
|
||||
Interactivity, IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
@ -32,7 +32,7 @@ impl Element for Svg {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
self.interactivity.layout(element_state, cx, |style, cx| {
|
||||
cx.request_layout(&style, None)
|
||||
@ -43,7 +43,7 @@ impl Element for Svg {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::{
|
||||
ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementId, HighlightStyle,
|
||||
IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point,
|
||||
SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine, TOOLTIP_DELAY,
|
||||
ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementContext, ElementId,
|
||||
HighlightStyle, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
|
||||
Point, SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine,
|
||||
TOOLTIP_DELAY,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
@ -21,14 +22,14 @@ impl Element for &'static str {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
_: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let mut state = TextState::default();
|
||||
let layout_id = state.layout(SharedString::from(*self), None, cx);
|
||||
(layout_id, state)
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut ElementContext) {
|
||||
state.paint(bounds, self, cx)
|
||||
}
|
||||
}
|
||||
@ -51,14 +52,14 @@ impl Element for SharedString {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
_: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let mut state = TextState::default();
|
||||
let layout_id = state.layout(self.clone(), None, cx);
|
||||
(layout_id, state)
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut ElementContext) {
|
||||
let text_str: &str = self.as_ref();
|
||||
state.paint(bounds, text_str, cx)
|
||||
}
|
||||
@ -130,14 +131,14 @@ impl Element for StyledText {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
_: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let mut state = TextState::default();
|
||||
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
|
||||
(layout_id, state)
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
|
||||
state.paint(bounds, &self.text, cx)
|
||||
}
|
||||
}
|
||||
@ -174,7 +175,7 @@ impl TextState {
|
||||
&mut self,
|
||||
text: SharedString,
|
||||
runs: Option<Vec<TextRun>>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> LayoutId {
|
||||
let text_style = cx.text_style();
|
||||
let font_size = text_style.font_size.to_pixels(cx.rem_size());
|
||||
@ -249,7 +250,7 @@ impl TextState {
|
||||
layout_id
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut ElementContext) {
|
||||
let element_state = self.lock();
|
||||
let element_state = element_state
|
||||
.as_ref()
|
||||
@ -377,7 +378,7 @@ impl Element for InteractiveText {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
state: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
if let Some(InteractiveTextState {
|
||||
mouse_down_index,
|
||||
@ -406,7 +407,7 @@ impl Element for InteractiveText {
|
||||
}
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
|
||||
if let Some(click_listener) = self.click_listener.take() {
|
||||
let mouse_position = cx.mouse_position();
|
||||
if let Some(ix) = state.text_state.index_for_position(bounds, mouse_position) {
|
||||
|
@ -5,9 +5,9 @@
|
||||
//! elements with uniform height.
|
||||
|
||||
use crate::{
|
||||
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementId,
|
||||
InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels,
|
||||
Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
|
||||
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext,
|
||||
ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
|
||||
Pixels, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||
@ -110,7 +110,7 @@ impl Element for UniformList {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
state: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let max_items = self.item_count;
|
||||
let item_size = state
|
||||
@ -158,7 +158,7 @@ impl Element for UniformList {
|
||||
&mut self,
|
||||
bounds: Bounds<crate::Pixels>,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
let style =
|
||||
self.interactivity
|
||||
@ -280,7 +280,7 @@ impl UniformList {
|
||||
self
|
||||
}
|
||||
|
||||
fn measure_item(&self, list_width: Option<Pixels>, cx: &mut WindowContext) -> Size<Pixels> {
|
||||
fn measure_item(&self, list_width: Option<Pixels>, cx: &mut ElementContext) -> Size<Pixels> {
|
||||
if self.item_count == 0 {
|
||||
return Size::default();
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Action, ActionRegistry, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, KeyMatch,
|
||||
Keymap, Keystroke, KeystrokeMatcher, WindowContext,
|
||||
Action, ActionRegistry, DispatchPhase, ElementContext, EntityId, FocusId, KeyBinding,
|
||||
KeyContext, KeyMatch, Keymap, Keystroke, KeystrokeMatcher, WindowContext,
|
||||
};
|
||||
use collections::FxHashMap;
|
||||
use parking_lot::Mutex;
|
||||
@ -36,7 +36,7 @@ pub(crate) struct DispatchNode {
|
||||
parent: Option<DispatchNodeId>,
|
||||
}
|
||||
|
||||
type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
|
||||
type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut ElementContext)>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct DispatchActionListener {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::{iter, mem, ops::Range};
|
||||
|
||||
use crate::{
|
||||
black, phi, point, quad, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds,
|
||||
ContentMask, Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement,
|
||||
Font, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
|
||||
SharedString, Size, SizeRefinement, Styled, TextRun, WindowContext,
|
||||
black, phi, point, quad, rems, AbsoluteLength, BorrowAppContext, Bounds, ContentMask, Corners,
|
||||
CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font,
|
||||
FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
|
||||
SharedString, Size, SizeRefinement, Styled, TextRun,
|
||||
};
|
||||
use collections::HashSet;
|
||||
use refineable::{Cascade, Refineable};
|
||||
@ -320,49 +320,12 @@ impl Style {
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply overflow to content mask
|
||||
pub fn apply_overflow<C, F, R>(&self, bounds: Bounds<Pixels>, cx: &mut C, f: F) -> R
|
||||
where
|
||||
C: BorrowWindow,
|
||||
F: FnOnce(&mut C) -> R,
|
||||
{
|
||||
let current_mask = cx.content_mask();
|
||||
|
||||
let min = current_mask.bounds.origin;
|
||||
let max = current_mask.bounds.lower_right();
|
||||
|
||||
let mask_bounds = match (
|
||||
self.overflow.x == Overflow::Visible,
|
||||
self.overflow.y == Overflow::Visible,
|
||||
) {
|
||||
// x and y both visible
|
||||
(true, true) => return f(cx),
|
||||
// x visible, y hidden
|
||||
(true, false) => Bounds::from_corners(
|
||||
point(min.x, bounds.origin.y),
|
||||
point(max.x, bounds.lower_right().y),
|
||||
),
|
||||
// x hidden, y visible
|
||||
(false, true) => Bounds::from_corners(
|
||||
point(bounds.origin.x, min.y),
|
||||
point(bounds.lower_right().x, max.y),
|
||||
),
|
||||
// both hidden
|
||||
(false, false) => bounds,
|
||||
};
|
||||
let mask = ContentMask {
|
||||
bounds: mask_bounds,
|
||||
};
|
||||
|
||||
cx.with_content_mask(Some(mask), f)
|
||||
}
|
||||
|
||||
/// Paints the background of an element styled with this style.
|
||||
pub fn paint(
|
||||
&self,
|
||||
bounds: Bounds<Pixels>,
|
||||
cx: &mut WindowContext,
|
||||
continuation: impl FnOnce(&mut WindowContext),
|
||||
cx: &mut ElementContext,
|
||||
continuation: impl FnOnce(&mut ElementContext),
|
||||
) {
|
||||
#[cfg(debug_assertions)]
|
||||
if self.debug_below {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
black, fill, point, px, size, Bounds, Hsla, LineLayout, Pixels, Point, Result, SharedString,
|
||||
UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
|
||||
black, fill, point, px, size, Bounds, ElementContext, Hsla, LineLayout, Pixels, Point, Result,
|
||||
SharedString, UnderlineStyle, WrapBoundary, WrappedLineLayout,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use smallvec::SmallVec;
|
||||
@ -33,7 +33,7 @@ impl ShapedLine {
|
||||
&self,
|
||||
origin: Point<Pixels>,
|
||||
line_height: Pixels,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> Result<()> {
|
||||
paint_line(
|
||||
origin,
|
||||
@ -66,7 +66,7 @@ impl WrappedLine {
|
||||
&self,
|
||||
origin: Point<Pixels>,
|
||||
line_height: Pixels,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> Result<()> {
|
||||
paint_line(
|
||||
origin,
|
||||
@ -87,7 +87,7 @@ fn paint_line(
|
||||
line_height: Pixels,
|
||||
decoration_runs: &[DecorationRun],
|
||||
wrap_boundaries: &[WrapBoundary],
|
||||
cx: &mut WindowContext<'_>,
|
||||
cx: &mut ElementContext<'_>,
|
||||
) -> Result<()> {
|
||||
let padding_top = (line_height - layout.ascent - layout.descent) / 2.;
|
||||
let baseline_offset = point(px(0.), padding_top + layout.ascent);
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
|
||||
Bounds, ContentMask, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView,
|
||||
IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style, TextStyle,
|
||||
ViewContext, VisualContext, WeakModel, WindowContext,
|
||||
seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, Bounds,
|
||||
ContentMask, Element, ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle,
|
||||
FocusableView, IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style,
|
||||
TextStyle, ViewContext, VisualContext, WeakModel,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use std::{
|
||||
@ -94,7 +94,7 @@ impl<V: Render> Element for View<V> {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
_state: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
cx.with_view_id(self.entity_id(), |cx| {
|
||||
let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
|
||||
@ -103,7 +103,7 @@ impl<V: Render> Element for View<V> {
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
|
||||
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
|
||||
cx.paint_view(self.entity_id(), |cx| element.take().unwrap().paint(cx));
|
||||
}
|
||||
}
|
||||
@ -202,7 +202,7 @@ impl<V> Eq for WeakView<V> {}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnyView {
|
||||
model: AnyModel,
|
||||
request_layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
|
||||
request_layout: fn(&AnyView, &mut ElementContext) -> (LayoutId, AnyElement),
|
||||
cache: bool,
|
||||
}
|
||||
|
||||
@ -250,7 +250,7 @@ impl AnyView {
|
||||
&self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.paint_view(self.entity_id(), |cx| {
|
||||
cx.with_absolute_element_offset(origin, |cx| {
|
||||
@ -278,7 +278,7 @@ impl Element for AnyView {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
state: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
cx.with_view_id(self.entity_id(), |cx| {
|
||||
if self.cache {
|
||||
@ -299,7 +299,7 @@ impl Element for AnyView {
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
|
||||
cx.paint_view(self.entity_id(), |cx| {
|
||||
if !self.cache {
|
||||
state.element.take().unwrap().paint(cx);
|
||||
@ -363,7 +363,7 @@ impl IntoElement for AnyView {
|
||||
/// A weak, dynamically-typed view handle that does not prevent the view from being released.
|
||||
pub struct AnyWeakView {
|
||||
model: AnyWeakModel,
|
||||
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
|
||||
layout: fn(&AnyView, &mut ElementContext) -> (LayoutId, AnyElement),
|
||||
}
|
||||
|
||||
impl AnyWeakView {
|
||||
@ -402,11 +402,11 @@ impl std::fmt::Debug for AnyWeakView {
|
||||
}
|
||||
|
||||
mod any_view {
|
||||
use crate::{AnyElement, AnyView, IntoElement, LayoutId, Render, WindowContext};
|
||||
use crate::{AnyElement, AnyView, ElementContext, IntoElement, LayoutId, Render};
|
||||
|
||||
pub(crate) fn request_layout<V: 'static + Render>(
|
||||
view: &AnyView,
|
||||
cx: &mut WindowContext,
|
||||
cx: &mut ElementContext,
|
||||
) -> (LayoutId, AnyElement) {
|
||||
let view = view.clone().downcast::<V>().unwrap();
|
||||
let mut element = view.update(cx, |view, cx| view.render(cx).into_any_element());
|
||||
|
@ -1,16 +1,13 @@
|
||||
use crate::{
|
||||
px, size, transparent_black, Action, AnyDrag, AnyTooltip, AnyView, AppContext, Arena,
|
||||
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
|
||||
DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
|
||||
Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
|
||||
ImageData, InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent,
|
||||
KeystrokeEvent, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
|
||||
MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay,
|
||||
PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel,
|
||||
Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
|
||||
Shadow, SharedString, Size, Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine,
|
||||
Task, Underline, UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions,
|
||||
SUBPIXEL_VARIANTS,
|
||||
AsyncWindowContext, AvailableSpace, Bounds, Context, Corners, CursorStyle,
|
||||
DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity,
|
||||
EntityId, EventEmitter, FileDropEvent, Flatten, GlobalElementId, Hsla, KeyBinding, KeyContext,
|
||||
KeyDownEvent, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent,
|
||||
MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler,
|
||||
PlatformWindow, Point, PromptLevel, Render, ScaledPixels, Scene, SharedString, Size,
|
||||
SubscriberSet, Subscription, TaffyLayoutEngine, Task, View, VisualContext, WeakView,
|
||||
WindowBounds, WindowOptions,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::{FxHashMap, FxHashSet};
|
||||
@ -19,13 +16,12 @@ use futures::{
|
||||
channel::{mpsc, oneshot},
|
||||
StreamExt,
|
||||
};
|
||||
use media::core_video::CVImageBuffer;
|
||||
use parking_lot::RwLock;
|
||||
use slotmap::SlotMap;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
borrow::{Borrow, BorrowMut, Cow},
|
||||
borrow::{Borrow, BorrowMut},
|
||||
cell::RefCell,
|
||||
collections::hash_map::Entry,
|
||||
fmt::{Debug, Display},
|
||||
@ -41,6 +37,9 @@ use std::{
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
mod element_cx;
|
||||
pub use element_cx::*;
|
||||
|
||||
const ACTIVE_DRAG_Z_INDEX: u8 = 1;
|
||||
|
||||
/// A global stacking order, which is created by stacking successive z-index values.
|
||||
@ -98,7 +97,7 @@ impl DispatchPhase {
|
||||
}
|
||||
|
||||
type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
|
||||
type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
|
||||
type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut ElementContext) + 'static>;
|
||||
type AnyWindowFocusListener = Box<dyn FnMut(&FocusEvent, &mut WindowContext) -> bool + 'static>;
|
||||
|
||||
struct FocusEvent {
|
||||
@ -804,80 +803,6 @@ impl<'a> WindowContext<'a> {
|
||||
result
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which
|
||||
/// layout is being requested, along with the layout ids of any children. This method is called during
|
||||
/// calls to the `Element::layout` trait method and enables any element to participate in layout.
|
||||
pub fn request_layout(
|
||||
&mut self,
|
||||
style: &Style,
|
||||
children: impl IntoIterator<Item = LayoutId>,
|
||||
) -> LayoutId {
|
||||
self.app.layout_id_buffer.clear();
|
||||
self.app.layout_id_buffer.extend(children);
|
||||
let rem_size = self.rem_size();
|
||||
|
||||
self.window.layout_engine.as_mut().unwrap().request_layout(
|
||||
style,
|
||||
rem_size,
|
||||
&self.app.layout_id_buffer,
|
||||
)
|
||||
}
|
||||
|
||||
/// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
|
||||
/// this variant takes a function that is invoked during layout so you can use arbitrary logic to
|
||||
/// determine the element's size. One place this is used internally is when measuring text.
|
||||
///
|
||||
/// The given closure is invoked at layout time with the known dimensions and available space and
|
||||
/// returns a `Size`.
|
||||
pub fn request_measured_layout<
|
||||
F: FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
|
||||
+ 'static,
|
||||
>(
|
||||
&mut self,
|
||||
style: Style,
|
||||
measure: F,
|
||||
) -> LayoutId {
|
||||
let rem_size = self.rem_size();
|
||||
self.window
|
||||
.layout_engine
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.request_measured_layout(style, rem_size, measure)
|
||||
}
|
||||
|
||||
pub(crate) fn layout_style(&self, layout_id: LayoutId) -> Option<&Style> {
|
||||
self.window
|
||||
.layout_engine
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.requested_style(layout_id)
|
||||
}
|
||||
|
||||
/// Compute the layout for the given id within the given available space.
|
||||
/// This method is called for its side effect, typically by the framework prior to painting.
|
||||
/// After calling it, you can request the bounds of the given layout node id or any descendant.
|
||||
pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
|
||||
let mut layout_engine = self.window.layout_engine.take().unwrap();
|
||||
layout_engine.compute_layout(layout_id, available_space, self);
|
||||
self.window.layout_engine = Some(layout_engine);
|
||||
}
|
||||
|
||||
/// Obtain the bounds computed for the given LayoutId relative to the window. This method should not
|
||||
/// be invoked until the paint phase begins, and will usually be invoked by GPUI itself automatically
|
||||
/// in order to pass your element its `Bounds` automatically.
|
||||
pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
|
||||
let mut bounds = self
|
||||
.window
|
||||
.layout_engine
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.layout_bounds(layout_id)
|
||||
.map(Into::into);
|
||||
bounds.origin += self.element_offset();
|
||||
bounds
|
||||
}
|
||||
|
||||
fn window_bounds_changed(&mut self) {
|
||||
self.window.scale_factor = self.window.platform_window.scale_factor();
|
||||
self.window.viewport_size = self.window.platform_window.content_size();
|
||||
@ -973,67 +898,6 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.default_prevented
|
||||
}
|
||||
|
||||
/// Register a mouse event listener on the window for the next frame. The type of event
|
||||
/// is determined by the first parameter of the given listener. When the next frame is rendered
|
||||
/// the listener will be cleared.
|
||||
pub fn on_mouse_event<Event: MouseEvent>(
|
||||
&mut self,
|
||||
mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static,
|
||||
) {
|
||||
let view_id = self.parent_view_id();
|
||||
let order = self.window.next_frame.z_index_stack.clone();
|
||||
self.window
|
||||
.next_frame
|
||||
.mouse_listeners
|
||||
.entry(TypeId::of::<Event>())
|
||||
.or_default()
|
||||
.push((
|
||||
order,
|
||||
view_id,
|
||||
Box::new(
|
||||
move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_>| {
|
||||
handler(event.downcast_ref().unwrap(), phase, cx)
|
||||
},
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
/// Register a key event listener on the window for the next frame. The type of event
|
||||
/// is determined by the first parameter of the given listener. When the next frame is rendered
|
||||
/// the listener will be cleared.
|
||||
///
|
||||
/// This is a fairly low-level method, so prefer using event handlers on elements unless you have
|
||||
/// a specific need to register a global listener.
|
||||
pub fn on_key_event<Event: KeyEvent>(
|
||||
&mut self,
|
||||
listener: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static,
|
||||
) {
|
||||
self.window.next_frame.dispatch_tree.on_key_event(Rc::new(
|
||||
move |event: &dyn Any, phase, cx: &mut WindowContext<'_>| {
|
||||
if let Some(event) = event.downcast_ref::<Event>() {
|
||||
listener(event, phase, cx)
|
||||
}
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
/// Register an action listener on the window for the next frame. The type of action
|
||||
/// is determined by the first parameter of the given listener. When the next frame is rendered
|
||||
/// the listener will be cleared.
|
||||
///
|
||||
/// This is a fairly low-level method, so prefer using action handlers on elements unless you have
|
||||
/// a specific need to register a global listener.
|
||||
pub fn on_action(
|
||||
&mut self,
|
||||
action_type: TypeId,
|
||||
listener: impl Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static,
|
||||
) {
|
||||
self.window
|
||||
.next_frame
|
||||
.dispatch_tree
|
||||
.on_action(action_type, Rc::new(listener));
|
||||
}
|
||||
|
||||
/// Determine whether the given action is available along the dispatch path to the currently focused element.
|
||||
pub fn is_action_available(&self, action: &dyn Action) -> bool {
|
||||
let target = self
|
||||
@ -1074,16 +938,6 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.next_frame.tooltip_request = Some(TooltipRequest { view_id, tooltip });
|
||||
}
|
||||
|
||||
/// Called during painting to track which z-index is on top at each pixel position
|
||||
pub fn add_opaque_layer(&mut self, bounds: Bounds<Pixels>) {
|
||||
let stacking_order = self.window.next_frame.z_index_stack.clone();
|
||||
let view_id = self.parent_view_id();
|
||||
let depth_map = &mut self.window.next_frame.depth_map;
|
||||
match depth_map.binary_search_by(|(level, _, _)| stacking_order.cmp(level)) {
|
||||
Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, view_id, bounds)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if there is no opaque layer containing the given point
|
||||
/// on top of the given level. Layers whose level is an extension of the
|
||||
/// level are not considered to be on top of the level.
|
||||
@ -1125,329 +979,6 @@ impl<'a> WindowContext<'a> {
|
||||
&self.window.next_frame.z_index_stack
|
||||
}
|
||||
|
||||
/// Paint one or more drop shadows into the scene for the next frame at the current z-index.
|
||||
pub fn paint_shadows(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
corner_radii: Corners<Pixels>,
|
||||
shadows: &[BoxShadow],
|
||||
) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let content_mask = self.content_mask();
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
for shadow in shadows {
|
||||
let mut shadow_bounds = bounds;
|
||||
shadow_bounds.origin += shadow.offset;
|
||||
shadow_bounds.dilate(shadow.spread_radius);
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Shadow {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: shadow_bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
corner_radii: corner_radii.scale(scale_factor),
|
||||
color: shadow.color,
|
||||
blur_radius: shadow.blur_radius.scale(scale_factor),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Paint one or more quads into the scene for the next frame at the current stacking context.
|
||||
/// Quads are colored rectangular regions with an optional background, border, and corner radius.
|
||||
/// see [`fill`], [`outline`], and [`quad`] to construct this type.
|
||||
pub fn paint_quad(&mut self, quad: PaintQuad) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let content_mask = self.content_mask();
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Quad {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: quad.bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
background: quad.background,
|
||||
border_color: quad.border_color,
|
||||
corner_radii: quad.corner_radii.scale(scale_factor),
|
||||
border_widths: quad.border_widths.scale(scale_factor),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Paint the given `Path` into the scene for the next frame at the current z-index.
|
||||
pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let content_mask = self.content_mask();
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
path.content_mask = content_mask;
|
||||
path.color = color.into();
|
||||
path.view_id = view_id.into();
|
||||
let window = &mut *self.window;
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.insert(&window.next_frame.z_index_stack, path.scale(scale_factor));
|
||||
}
|
||||
|
||||
/// Paint an underline into the scene for the next frame at the current z-index.
|
||||
pub fn paint_underline(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
width: Pixels,
|
||||
style: &UnderlineStyle,
|
||||
) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let height = if style.wavy {
|
||||
style.thickness * 3.
|
||||
} else {
|
||||
style.thickness
|
||||
};
|
||||
let bounds = Bounds {
|
||||
origin,
|
||||
size: size(width, height),
|
||||
};
|
||||
let content_mask = self.content_mask();
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Underline {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
thickness: style.thickness.scale(scale_factor),
|
||||
color: style.color.unwrap_or_default(),
|
||||
wavy: style.wavy,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Paint a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
|
||||
/// The y component of the origin is the baseline of the glyph.
|
||||
pub fn paint_glyph(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
font_id: FontId,
|
||||
glyph_id: GlyphId,
|
||||
font_size: Pixels,
|
||||
color: Hsla,
|
||||
) -> Result<()> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let glyph_origin = origin.scale(scale_factor);
|
||||
let subpixel_variant = Point {
|
||||
x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
|
||||
y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
|
||||
};
|
||||
let params = RenderGlyphParams {
|
||||
font_id,
|
||||
glyph_id,
|
||||
font_size,
|
||||
subpixel_variant,
|
||||
scale_factor,
|
||||
is_emoji: false,
|
||||
};
|
||||
|
||||
let raster_bounds = self.text_system().raster_bounds(¶ms)?;
|
||||
if !raster_bounds.is_zero() {
|
||||
let tile =
|
||||
self.window
|
||||
.sprite_atlas
|
||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||
let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
|
||||
Ok((size, Cow::Owned(bytes)))
|
||||
})?;
|
||||
let bounds = Bounds {
|
||||
origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
|
||||
size: tile.bounds.size.map(Into::into),
|
||||
};
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Paint an emoji glyph into the scene for the next frame at the current z-index.
|
||||
/// The y component of the origin is the baseline of the glyph.
|
||||
pub fn paint_emoji(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
font_id: FontId,
|
||||
glyph_id: GlyphId,
|
||||
font_size: Pixels,
|
||||
) -> Result<()> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let glyph_origin = origin.scale(scale_factor);
|
||||
let params = RenderGlyphParams {
|
||||
font_id,
|
||||
glyph_id,
|
||||
font_size,
|
||||
// We don't render emojis with subpixel variants.
|
||||
subpixel_variant: Default::default(),
|
||||
scale_factor,
|
||||
is_emoji: true,
|
||||
};
|
||||
|
||||
let raster_bounds = self.text_system().raster_bounds(¶ms)?;
|
||||
if !raster_bounds.is_zero() {
|
||||
let tile =
|
||||
self.window
|
||||
.sprite_atlas
|
||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||
let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
|
||||
Ok((size, Cow::Owned(bytes)))
|
||||
})?;
|
||||
let bounds = Bounds {
|
||||
origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
|
||||
size: tile.bounds.size.map(Into::into),
|
||||
};
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
corner_radii: Default::default(),
|
||||
content_mask,
|
||||
tile,
|
||||
grayscale: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Paint a monochrome SVG into the scene for the next frame at the current stacking context.
|
||||
pub fn paint_svg(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
path: SharedString,
|
||||
color: Hsla,
|
||||
) -> Result<()> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let bounds = bounds.scale(scale_factor);
|
||||
// Render the SVG at twice the size to get a higher quality result.
|
||||
let params = RenderSvgParams {
|
||||
path,
|
||||
size: bounds
|
||||
.size
|
||||
.map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
|
||||
};
|
||||
|
||||
let tile =
|
||||
self.window
|
||||
.sprite_atlas
|
||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||
let bytes = self.svg_renderer.render(¶ms)?;
|
||||
Ok((params.size, Cow::Owned(bytes)))
|
||||
})?;
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Paint an image into the scene for the next frame at the current z-index.
|
||||
pub fn paint_image(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
corner_radii: Corners<Pixels>,
|
||||
data: Arc<ImageData>,
|
||||
grayscale: bool,
|
||||
) -> Result<()> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let bounds = bounds.scale(scale_factor);
|
||||
let params = RenderImageParams { image_id: data.id };
|
||||
|
||||
let tile = self
|
||||
.window
|
||||
.sprite_atlas
|
||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||
Ok((data.size(), Cow::Borrowed(data.as_bytes())))
|
||||
})?;
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let corner_radii = corner_radii.scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
corner_radii,
|
||||
tile,
|
||||
grayscale,
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Paint a surface into the scene for the next frame at the current z-index.
|
||||
pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVImageBuffer) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let bounds = bounds.scale(scale_factor);
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Surface {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
image_buffer,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn reuse_view(&mut self) {
|
||||
let view_id = self.parent_view_id();
|
||||
let grafted_view_ids = self
|
||||
@ -1502,44 +1033,55 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
if let Some(requested_handler) = self.window.rendered_frame.requested_input_handler.as_mut()
|
||||
{
|
||||
requested_handler.handler = self.window.platform_window.take_input_handler();
|
||||
let input_handler = self.window.platform_window.take_input_handler();
|
||||
requested_handler.handler = input_handler;
|
||||
}
|
||||
|
||||
let root_view = self.window.root_view.take().unwrap();
|
||||
|
||||
self.with_z_index(0, |cx| {
|
||||
cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
|
||||
for (action_type, action_listeners) in &cx.app.global_action_listeners {
|
||||
for action_listener in action_listeners.iter().cloned() {
|
||||
cx.window.next_frame.dispatch_tree.on_action(
|
||||
*action_type,
|
||||
Rc::new(move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
|
||||
action_listener(action, phase, cx)
|
||||
}),
|
||||
)
|
||||
self.with_element_context(|cx| {
|
||||
cx.with_z_index(0, |cx| {
|
||||
cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
|
||||
// We need to use cx.cx here so we can utilize borrow splitting
|
||||
for (action_type, action_listeners) in &cx.cx.app.global_action_listeners {
|
||||
for action_listener in action_listeners.iter().cloned() {
|
||||
cx.cx.window.next_frame.dispatch_tree.on_action(
|
||||
*action_type,
|
||||
Rc::new(
|
||||
move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
|
||||
action_listener(action, phase, cx)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let available_space = cx.window.viewport_size.map(Into::into);
|
||||
root_view.draw(Point::default(), available_space, cx);
|
||||
let available_space = cx.window.viewport_size.map(Into::into);
|
||||
root_view.draw(Point::default(), available_space, cx);
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(active_drag) = self.app.active_drag.take() {
|
||||
self.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
|
||||
let offset = cx.mouse_position() - active_drag.cursor_offset;
|
||||
let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
active_drag.view.draw(offset, available_space, cx);
|
||||
self.with_element_context(|cx| {
|
||||
cx.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
|
||||
let offset = cx.mouse_position() - active_drag.cursor_offset;
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
active_drag.view.draw(offset, available_space, cx);
|
||||
})
|
||||
});
|
||||
self.active_drag = Some(active_drag);
|
||||
} else if let Some(tooltip_request) = self.window.next_frame.tooltip_request.take() {
|
||||
self.with_z_index(1, |cx| {
|
||||
let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
tooltip_request.tooltip.view.draw(
|
||||
tooltip_request.tooltip.cursor_offset,
|
||||
available_space,
|
||||
cx,
|
||||
);
|
||||
self.with_element_context(|cx| {
|
||||
cx.with_z_index(1, |cx| {
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
tooltip_request.tooltip.view.draw(
|
||||
tooltip_request.tooltip.cursor_offset,
|
||||
available_space,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
});
|
||||
self.window.next_frame.tooltip_request = Some(tooltip_request);
|
||||
}
|
||||
@ -1730,7 +1272,9 @@ impl<'a> WindowContext<'a> {
|
||||
// Capture phase, events bubble from back to front. Handlers for this phase are used for
|
||||
// special purposes, such as detecting events outside of a given Bounds.
|
||||
for (_, _, handler) in &mut handlers {
|
||||
handler(event, DispatchPhase::Capture, self);
|
||||
self.with_element_context(|cx| {
|
||||
handler(event, DispatchPhase::Capture, cx);
|
||||
});
|
||||
if !self.app.propagate_event {
|
||||
break;
|
||||
}
|
||||
@ -1739,7 +1283,9 @@ impl<'a> WindowContext<'a> {
|
||||
// Bubble phase, where most normal handlers do their work.
|
||||
if self.app.propagate_event {
|
||||
for (_, _, handler) in handlers.iter_mut().rev() {
|
||||
handler(event, DispatchPhase::Bubble, self);
|
||||
self.with_element_context(|cx| {
|
||||
handler(event, DispatchPhase::Bubble, cx);
|
||||
});
|
||||
if !self.app.propagate_event {
|
||||
break;
|
||||
}
|
||||
@ -1830,7 +1376,9 @@ impl<'a> WindowContext<'a> {
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
|
||||
for key_listener in node.key_listeners.clone() {
|
||||
key_listener(event, DispatchPhase::Capture, self);
|
||||
self.with_element_context(|cx| {
|
||||
key_listener(event, DispatchPhase::Capture, cx);
|
||||
});
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
@ -1842,7 +1390,9 @@ impl<'a> WindowContext<'a> {
|
||||
// Handle low level key events
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
for key_listener in node.key_listeners.clone() {
|
||||
key_listener(event, DispatchPhase::Bubble, self);
|
||||
self.with_element_context(|cx| {
|
||||
key_listener(event, DispatchPhase::Bubble, cx);
|
||||
});
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
@ -1877,7 +1427,10 @@ impl<'a> WindowContext<'a> {
|
||||
{
|
||||
let any_action = action.as_any();
|
||||
if action_type == any_action.type_id() {
|
||||
listener(any_action, DispatchPhase::Capture, self);
|
||||
self.with_element_context(|cx| {
|
||||
listener(any_action, DispatchPhase::Capture, cx);
|
||||
});
|
||||
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
@ -1895,7 +1448,11 @@ impl<'a> WindowContext<'a> {
|
||||
let any_action = action.as_any();
|
||||
if action_type == any_action.type_id() {
|
||||
self.propagate_event = false; // Actions stop propagation by default during the bubble phase
|
||||
listener(any_action, DispatchPhase::Bubble, self);
|
||||
|
||||
self.with_element_context(|cx| {
|
||||
listener(any_action, DispatchPhase::Bubble, cx);
|
||||
});
|
||||
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
@ -2019,84 +1576,6 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke the given function with the given focus handle present on the key dispatch stack.
|
||||
/// If you want an element to participate in key dispatch, use this method to push its key context and focus handle into the stack during paint.
|
||||
pub fn with_key_dispatch<R>(
|
||||
&mut self,
|
||||
context: Option<KeyContext>,
|
||||
focus_handle: Option<FocusHandle>,
|
||||
f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
|
||||
) -> R {
|
||||
let window = &mut self.window;
|
||||
let focus_id = focus_handle.as_ref().map(|handle| handle.id);
|
||||
window
|
||||
.next_frame
|
||||
.dispatch_tree
|
||||
.push_node(context.clone(), focus_id, None);
|
||||
|
||||
let result = f(focus_handle, self);
|
||||
|
||||
self.window.next_frame.dispatch_tree.pop_node();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Invoke the given function with the given view id present on the view stack.
|
||||
/// This is a fairly low-level method used to layout views.
|
||||
pub fn with_view_id<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
let text_system = self.text_system().clone();
|
||||
text_system.with_view(view_id, || {
|
||||
if self.window.next_frame.view_stack.last() == Some(&view_id) {
|
||||
return f(self);
|
||||
} else {
|
||||
self.window.next_frame.view_stack.push(view_id);
|
||||
let result = f(self);
|
||||
self.window.next_frame.view_stack.pop();
|
||||
result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Invoke the given function with the given view id present on the view stack.
|
||||
/// This is a fairly low-level method used to paint views.
|
||||
pub fn paint_view<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
let text_system = self.text_system().clone();
|
||||
text_system.with_view(view_id, || {
|
||||
if self.window.next_frame.view_stack.last() == Some(&view_id) {
|
||||
return f(self);
|
||||
} else {
|
||||
self.window.next_frame.view_stack.push(view_id);
|
||||
self.window
|
||||
.next_frame
|
||||
.dispatch_tree
|
||||
.push_node(None, None, Some(view_id));
|
||||
let result = f(self);
|
||||
self.window.next_frame.dispatch_tree.pop_node();
|
||||
self.window.next_frame.view_stack.pop();
|
||||
result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the
|
||||
/// platform to receive textual input with proper integration with concerns such
|
||||
/// as IME interactions. This handler will be active for the upcoming frame until the following frame is
|
||||
/// rendered.
|
||||
///
|
||||
/// [element_input_handler]: crate::ElementInputHandler
|
||||
pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
|
||||
if focus_handle.is_focused(self) {
|
||||
let view_id = self.parent_view_id();
|
||||
self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
|
||||
view_id,
|
||||
handler: Some(PlatformInputHandler::new(
|
||||
self.to_async(),
|
||||
Box::new(input_handler),
|
||||
)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a callback that can interrupt the closing of the current window based the returned boolean.
|
||||
/// If the callback returns false, the window won't be closed.
|
||||
pub fn on_window_should_close(&mut self, f: impl Fn(&mut WindowContext) -> bool + 'static) {
|
||||
@ -2115,6 +1594,32 @@ impl<'a> WindowContext<'a> {
|
||||
.unwrap_or(true)
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn parent_view_id(&self) -> EntityId {
|
||||
*self
|
||||
.window
|
||||
.next_frame
|
||||
.view_stack
|
||||
.last()
|
||||
.expect("a view should always be on the stack while drawing")
|
||||
}
|
||||
|
||||
/// Register an action listener on the window for the next frame. The type of action
|
||||
/// is determined by the first parameter of the given listener. When the next frame is rendered
|
||||
/// the listener will be cleared.
|
||||
///
|
||||
/// This is a fairly low-level method, so prefer using action handlers on elements unless you have
|
||||
/// a specific need to register a global listener.
|
||||
pub fn on_action(
|
||||
&mut self,
|
||||
action_type: TypeId,
|
||||
listener: impl Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static,
|
||||
) {
|
||||
self.window
|
||||
.next_frame
|
||||
.dispatch_tree
|
||||
.on_action(action_type, Rc::new(listener));
|
||||
}
|
||||
}
|
||||
|
||||
impl Context for WindowContext<'_> {
|
||||
@ -2739,34 +2244,6 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
||||
subscription
|
||||
}
|
||||
|
||||
/// Add a listener for any mouse event that occurs in the window.
|
||||
/// This is a fairly low level method.
|
||||
/// Typically, you'll want to use methods on UI elements, which perform bounds checking etc.
|
||||
pub fn on_mouse_event<Event: MouseEvent>(
|
||||
&mut self,
|
||||
handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + 'static,
|
||||
) {
|
||||
let handle = self.view().clone();
|
||||
self.window_cx.on_mouse_event(move |event, phase, cx| {
|
||||
handle.update(cx, |view, cx| {
|
||||
handler(view, event, phase, cx);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when the given Key Event is dispatched to the window.
|
||||
pub fn on_key_event<Event: KeyEvent>(
|
||||
&mut self,
|
||||
handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + 'static,
|
||||
) {
|
||||
let handle = self.view().clone();
|
||||
self.window_cx.on_key_event(move |event, phase, cx| {
|
||||
handle.update(cx, |view, cx| {
|
||||
handler(view, event, phase, cx);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when the given Action type is dispatched to the window.
|
||||
pub fn on_action(
|
||||
&mut self,
|
||||
|
939
crates/gpui/src/window/element_cx.rs
Normal file
939
crates/gpui/src/window/element_cx.rs
Normal file
@ -0,0 +1,939 @@
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
borrow::{Borrow, BorrowMut, Cow},
|
||||
mem,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use media::core_video::CVImageBuffer;
|
||||
use util::post_inc;
|
||||
|
||||
use crate::{
|
||||
prelude::*, size, AppContext, AvailableSpace, Bounds, BoxShadow, ContentMask, Corners,
|
||||
DevicePixels, DispatchPhase, ElementId, ElementStateBox, EntityId, FocusHandle, FontId,
|
||||
GlyphId, Hsla, ImageData, InputHandler, IsZero, KeyContext, KeyEvent, LayoutId,
|
||||
MonochromeSprite, MouseEvent, PaintQuad, Path, Pixels, PlatformInputHandler, Point,
|
||||
PolychromeSprite, Quad, RenderGlyphParams, RenderImageParams, RenderSvgParams, Shadow,
|
||||
SharedString, Size, Style, Surface, Underline, UnderlineStyle, Window, WindowContext,
|
||||
SUBPIXEL_VARIANTS,
|
||||
};
|
||||
|
||||
use super::RequestedInputHandler;
|
||||
|
||||
/// This context is used for assisting in the implementation of the element trait
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct ElementContext<'a> {
|
||||
pub(crate) cx: WindowContext<'a>,
|
||||
}
|
||||
|
||||
impl<'a> WindowContext<'a> {
|
||||
pub(crate) fn with_element_context<R>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut ElementContext) -> R,
|
||||
) -> R {
|
||||
f(&mut ElementContext {
|
||||
cx: WindowContext::new(self.app, self.window),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Borrow<AppContext> for ElementContext<'a> {
|
||||
fn borrow(&self) -> &AppContext {
|
||||
self.cx.app
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BorrowMut<AppContext> for ElementContext<'a> {
|
||||
fn borrow_mut(&mut self) -> &mut AppContext {
|
||||
self.cx.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Borrow<WindowContext<'a>> for ElementContext<'a> {
|
||||
fn borrow(&self) -> &WindowContext<'a> {
|
||||
&self.cx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BorrowMut<WindowContext<'a>> for ElementContext<'a> {
|
||||
fn borrow_mut(&mut self) -> &mut WindowContext<'a> {
|
||||
&mut self.cx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Borrow<Window> for ElementContext<'a> {
|
||||
fn borrow(&self) -> &Window {
|
||||
self.cx.window
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BorrowMut<Window> for ElementContext<'a> {
|
||||
fn borrow_mut(&mut self) -> &mut Window {
|
||||
self.cx.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Context for ElementContext<'a> {
|
||||
type Result<T> = <WindowContext<'a> as Context>::Result<T>;
|
||||
|
||||
fn new_model<T: 'static>(
|
||||
&mut self,
|
||||
build_model: impl FnOnce(&mut crate::ModelContext<'_, T>) -> T,
|
||||
) -> Self::Result<crate::Model<T>> {
|
||||
self.cx.new_model(build_model)
|
||||
}
|
||||
|
||||
fn update_model<T, R>(
|
||||
&mut self,
|
||||
handle: &crate::Model<T>,
|
||||
update: impl FnOnce(&mut T, &mut crate::ModelContext<'_, T>) -> R,
|
||||
) -> Self::Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.cx.update_model(handle, update)
|
||||
}
|
||||
|
||||
fn read_model<T, R>(
|
||||
&self,
|
||||
handle: &crate::Model<T>,
|
||||
read: impl FnOnce(&T, &AppContext) -> R,
|
||||
) -> Self::Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.cx.read_model(handle, read)
|
||||
}
|
||||
|
||||
fn update_window<T, F>(&mut self, window: crate::AnyWindowHandle, f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(crate::AnyView, &mut WindowContext<'_>) -> T,
|
||||
{
|
||||
self.cx.update_window(window, f)
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &crate::WindowHandle<T>,
|
||||
read: impl FnOnce(crate::View<T>, &AppContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.cx.read_window(window, read)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VisualContext for ElementContext<'a> {
|
||||
fn new_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut crate::ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<crate::View<V>>
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
self.cx.new_view(build_view)
|
||||
}
|
||||
|
||||
fn update_view<V: 'static, R>(
|
||||
&mut self,
|
||||
view: &crate::View<V>,
|
||||
update: impl FnOnce(&mut V, &mut crate::ViewContext<'_, V>) -> R,
|
||||
) -> Self::Result<R> {
|
||||
self.cx.update_view(view, update)
|
||||
}
|
||||
|
||||
fn replace_root_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut crate::ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<crate::View<V>>
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
self.cx.replace_root_view(build_view)
|
||||
}
|
||||
|
||||
fn focus_view<V>(&mut self, view: &crate::View<V>) -> Self::Result<()>
|
||||
where
|
||||
V: crate::FocusableView,
|
||||
{
|
||||
self.cx.focus_view(view)
|
||||
}
|
||||
|
||||
fn dismiss_view<V>(&mut self, view: &crate::View<V>) -> Self::Result<()>
|
||||
where
|
||||
V: crate::ManagedView,
|
||||
{
|
||||
self.cx.dismiss_view(view)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ElementContext<'a> {
|
||||
/// Pushes the given element id onto the global stack and invokes the given closure
|
||||
/// with a `GlobalElementId`, which disambiguates the given id in the context of its ancestor
|
||||
/// ids. Because elements are discarded and recreated on each frame, the `GlobalElementId` is
|
||||
/// used to associate state with identified elements across separate frames.
|
||||
pub(crate) fn with_element_id<R>(
|
||||
&mut self,
|
||||
id: Option<impl Into<ElementId>>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
if let Some(id) = id.map(Into::into) {
|
||||
let window = self.window_mut();
|
||||
window.element_id_stack.push(id);
|
||||
let result = f(self);
|
||||
let window: &mut Window = self.borrow_mut();
|
||||
window.element_id_stack.pop();
|
||||
result
|
||||
} else {
|
||||
f(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke the given function with the given content mask after intersecting it
|
||||
/// with the current mask.
|
||||
pub(crate) fn with_content_mask<R>(
|
||||
&mut self,
|
||||
mask: Option<ContentMask<Pixels>>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
if let Some(mask) = mask {
|
||||
let mask = mask.intersect(&self.content_mask());
|
||||
self.window_mut().next_frame.content_mask_stack.push(mask);
|
||||
let result = f(self);
|
||||
self.window_mut().next_frame.content_mask_stack.pop();
|
||||
result
|
||||
} else {
|
||||
f(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke the given function with the content mask reset to that
|
||||
/// of the window.
|
||||
pub(crate) fn break_content_mask<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
let mask = ContentMask {
|
||||
bounds: Bounds {
|
||||
origin: Point::default(),
|
||||
size: self.window().viewport_size,
|
||||
},
|
||||
};
|
||||
let new_stacking_order_id =
|
||||
post_inc(&mut self.window_mut().next_frame.next_stacking_order_id);
|
||||
let new_root_z_index = post_inc(&mut self.window_mut().next_frame.next_root_z_index);
|
||||
let old_stacking_order = mem::take(&mut self.window_mut().next_frame.z_index_stack);
|
||||
self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id;
|
||||
self.window_mut()
|
||||
.next_frame
|
||||
.z_index_stack
|
||||
.push(new_root_z_index);
|
||||
self.window_mut().next_frame.content_mask_stack.push(mask);
|
||||
let result = f(self);
|
||||
self.window_mut().next_frame.content_mask_stack.pop();
|
||||
self.window_mut().next_frame.z_index_stack = old_stacking_order;
|
||||
result
|
||||
}
|
||||
|
||||
/// Called during painting to invoke the given closure in a new stacking context. The given
|
||||
/// z-index is interpreted relative to the previous call to `stack`.
|
||||
pub(crate) fn with_z_index<R>(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
let new_stacking_order_id =
|
||||
post_inc(&mut self.window_mut().next_frame.next_stacking_order_id);
|
||||
let old_stacking_order_id = mem::replace(
|
||||
&mut self.window_mut().next_frame.z_index_stack.id,
|
||||
new_stacking_order_id,
|
||||
);
|
||||
self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id;
|
||||
self.window_mut().next_frame.z_index_stack.push(z_index);
|
||||
let result = f(self);
|
||||
self.window_mut().next_frame.z_index_stack.id = old_stacking_order_id;
|
||||
self.window_mut().next_frame.z_index_stack.pop();
|
||||
result
|
||||
}
|
||||
|
||||
/// Updates the global element offset relative to the current offset. This is used to implement
|
||||
/// scrolling.
|
||||
pub(crate) fn with_element_offset<R>(
|
||||
&mut self,
|
||||
offset: Point<Pixels>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
if offset.is_zero() {
|
||||
return f(self);
|
||||
};
|
||||
|
||||
let abs_offset = self.element_offset() + offset;
|
||||
self.with_absolute_element_offset(abs_offset, f)
|
||||
}
|
||||
|
||||
/// Updates the global element offset based on the given offset. This is used to implement
|
||||
/// drag handles and other manual painting of elements.
|
||||
pub(crate) fn with_absolute_element_offset<R>(
|
||||
&mut self,
|
||||
offset: Point<Pixels>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
self.window_mut()
|
||||
.next_frame
|
||||
.element_offset_stack
|
||||
.push(offset);
|
||||
let result = f(self);
|
||||
self.window_mut().next_frame.element_offset_stack.pop();
|
||||
result
|
||||
}
|
||||
|
||||
/// Obtain the current element offset.
|
||||
pub(crate) fn element_offset(&self) -> Point<Pixels> {
|
||||
self.window()
|
||||
.next_frame
|
||||
.element_offset_stack
|
||||
.last()
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Obtain the current content mask.
|
||||
pub(crate) fn content_mask(&self) -> ContentMask<Pixels> {
|
||||
self.window()
|
||||
.next_frame
|
||||
.content_mask_stack
|
||||
.last()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| ContentMask {
|
||||
bounds: Bounds {
|
||||
origin: Point::default(),
|
||||
size: self.window().viewport_size,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// The size of an em for the base font of the application. Adjusting this value allows the
|
||||
/// UI to scale, just like zooming a web page.
|
||||
pub(crate) fn rem_size(&self) -> Pixels {
|
||||
self.window().rem_size
|
||||
}
|
||||
|
||||
/// Updates or initializes state for an element with the given id that lives across multiple
|
||||
/// frames. If an element with this ID existed in the rendered frame, its state will be passed
|
||||
/// to the given closure. The state returned by the closure will be stored so it can be referenced
|
||||
/// when drawing the next frame.
|
||||
pub(crate) fn with_element_state<S, R>(
|
||||
&mut self,
|
||||
id: ElementId,
|
||||
f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
|
||||
) -> R
|
||||
where
|
||||
S: 'static,
|
||||
{
|
||||
self.with_element_id(Some(id), |cx| {
|
||||
let global_id = cx.window().element_id_stack.clone();
|
||||
|
||||
if let Some(any) = cx
|
||||
.window_mut()
|
||||
.next_frame
|
||||
.element_states
|
||||
.remove(&global_id)
|
||||
.or_else(|| {
|
||||
cx.window_mut()
|
||||
.rendered_frame
|
||||
.element_states
|
||||
.remove(&global_id)
|
||||
})
|
||||
{
|
||||
let ElementStateBox {
|
||||
inner,
|
||||
parent_view_id,
|
||||
#[cfg(debug_assertions)]
|
||||
type_name
|
||||
} = any;
|
||||
// Using the extra inner option to avoid needing to reallocate a new box.
|
||||
let mut state_box = inner
|
||||
.downcast::<Option<S>>()
|
||||
.map_err(|_| {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
anyhow::anyhow!(
|
||||
"invalid element state type for id, requested_type {:?}, actual type: {:?}",
|
||||
std::any::type_name::<S>(),
|
||||
type_name
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
anyhow::anyhow!(
|
||||
"invalid element state type for id, requested_type {:?}",
|
||||
std::any::type_name::<S>(),
|
||||
)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Actual: Option<AnyElement> <- View
|
||||
// Requested: () <- AnyElement
|
||||
let state = state_box
|
||||
.take()
|
||||
.expect("element state is already on the stack");
|
||||
let (result, state) = f(Some(state), cx);
|
||||
state_box.replace(state);
|
||||
cx.window_mut()
|
||||
.next_frame
|
||||
.element_states
|
||||
.insert(global_id, ElementStateBox {
|
||||
inner: state_box,
|
||||
parent_view_id,
|
||||
#[cfg(debug_assertions)]
|
||||
type_name
|
||||
});
|
||||
result
|
||||
} else {
|
||||
let (result, state) = f(None, cx);
|
||||
let parent_view_id = cx.parent_view_id();
|
||||
cx.window_mut()
|
||||
.next_frame
|
||||
.element_states
|
||||
.insert(global_id,
|
||||
ElementStateBox {
|
||||
inner: Box::new(Some(state)),
|
||||
parent_view_id,
|
||||
#[cfg(debug_assertions)]
|
||||
type_name: std::any::type_name::<S>()
|
||||
}
|
||||
|
||||
);
|
||||
result
|
||||
}
|
||||
})
|
||||
}
|
||||
/// Paint one or more drop shadows into the scene for the next frame at the current z-index.
|
||||
pub fn paint_shadows(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
corner_radii: Corners<Pixels>,
|
||||
shadows: &[BoxShadow],
|
||||
) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let content_mask = self.content_mask();
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
for shadow in shadows {
|
||||
let mut shadow_bounds = bounds;
|
||||
shadow_bounds.origin += shadow.offset;
|
||||
shadow_bounds.dilate(shadow.spread_radius);
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Shadow {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: shadow_bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
corner_radii: corner_radii.scale(scale_factor),
|
||||
color: shadow.color,
|
||||
blur_radius: shadow.blur_radius.scale(scale_factor),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Paint one or more quads into the scene for the next frame at the current stacking context.
|
||||
/// Quads are colored rectangular regions with an optional background, border, and corner radius.
|
||||
/// see [`fill`], [`outline`], and [`quad`] to construct this type.
|
||||
pub fn paint_quad(&mut self, quad: PaintQuad) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let content_mask = self.content_mask();
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Quad {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: quad.bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
background: quad.background,
|
||||
border_color: quad.border_color,
|
||||
corner_radii: quad.corner_radii.scale(scale_factor),
|
||||
border_widths: quad.border_widths.scale(scale_factor),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Paint the given `Path` into the scene for the next frame at the current z-index.
|
||||
pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let content_mask = self.content_mask();
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
path.content_mask = content_mask;
|
||||
path.color = color.into();
|
||||
path.view_id = view_id.into();
|
||||
let window = &mut *self.window;
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.insert(&window.next_frame.z_index_stack, path.scale(scale_factor));
|
||||
}
|
||||
|
||||
/// Paint an underline into the scene for the next frame at the current z-index.
|
||||
pub fn paint_underline(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
width: Pixels,
|
||||
style: &UnderlineStyle,
|
||||
) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let height = if style.wavy {
|
||||
style.thickness * 3.
|
||||
} else {
|
||||
style.thickness
|
||||
};
|
||||
let bounds = Bounds {
|
||||
origin,
|
||||
size: size(width, height),
|
||||
};
|
||||
let content_mask = self.content_mask();
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Underline {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
thickness: style.thickness.scale(scale_factor),
|
||||
color: style.color.unwrap_or_default(),
|
||||
wavy: style.wavy,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Paint a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
|
||||
/// The y component of the origin is the baseline of the glyph.
|
||||
pub fn paint_glyph(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
font_id: FontId,
|
||||
glyph_id: GlyphId,
|
||||
font_size: Pixels,
|
||||
color: Hsla,
|
||||
) -> Result<()> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let glyph_origin = origin.scale(scale_factor);
|
||||
let subpixel_variant = Point {
|
||||
x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
|
||||
y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
|
||||
};
|
||||
let params = RenderGlyphParams {
|
||||
font_id,
|
||||
glyph_id,
|
||||
font_size,
|
||||
subpixel_variant,
|
||||
scale_factor,
|
||||
is_emoji: false,
|
||||
};
|
||||
|
||||
let raster_bounds = self.text_system().raster_bounds(¶ms)?;
|
||||
if !raster_bounds.is_zero() {
|
||||
let tile =
|
||||
self.window
|
||||
.sprite_atlas
|
||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||
let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
|
||||
Ok((size, Cow::Owned(bytes)))
|
||||
})?;
|
||||
let bounds = Bounds {
|
||||
origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
|
||||
size: tile.bounds.size.map(Into::into),
|
||||
};
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Paint an emoji glyph into the scene for the next frame at the current z-index.
|
||||
/// The y component of the origin is the baseline of the glyph.
|
||||
pub fn paint_emoji(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
font_id: FontId,
|
||||
glyph_id: GlyphId,
|
||||
font_size: Pixels,
|
||||
) -> Result<()> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let glyph_origin = origin.scale(scale_factor);
|
||||
let params = RenderGlyphParams {
|
||||
font_id,
|
||||
glyph_id,
|
||||
font_size,
|
||||
// We don't render emojis with subpixel variants.
|
||||
subpixel_variant: Default::default(),
|
||||
scale_factor,
|
||||
is_emoji: true,
|
||||
};
|
||||
|
||||
let raster_bounds = self.text_system().raster_bounds(¶ms)?;
|
||||
if !raster_bounds.is_zero() {
|
||||
let tile =
|
||||
self.window
|
||||
.sprite_atlas
|
||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||
let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
|
||||
Ok((size, Cow::Owned(bytes)))
|
||||
})?;
|
||||
let bounds = Bounds {
|
||||
origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
|
||||
size: tile.bounds.size.map(Into::into),
|
||||
};
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
corner_radii: Default::default(),
|
||||
content_mask,
|
||||
tile,
|
||||
grayscale: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Paint a monochrome SVG into the scene for the next frame at the current stacking context.
|
||||
pub fn paint_svg(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
path: SharedString,
|
||||
color: Hsla,
|
||||
) -> Result<()> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let bounds = bounds.scale(scale_factor);
|
||||
// Render the SVG at twice the size to get a higher quality result.
|
||||
let params = RenderSvgParams {
|
||||
path,
|
||||
size: bounds
|
||||
.size
|
||||
.map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
|
||||
};
|
||||
|
||||
let tile =
|
||||
self.window
|
||||
.sprite_atlas
|
||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||
let bytes = self.svg_renderer.render(¶ms)?;
|
||||
Ok((params.size, Cow::Owned(bytes)))
|
||||
})?;
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Paint an image into the scene for the next frame at the current z-index.
|
||||
pub fn paint_image(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
corner_radii: Corners<Pixels>,
|
||||
data: Arc<ImageData>,
|
||||
grayscale: bool,
|
||||
) -> Result<()> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let bounds = bounds.scale(scale_factor);
|
||||
let params = RenderImageParams { image_id: data.id };
|
||||
|
||||
let tile = self
|
||||
.window
|
||||
.sprite_atlas
|
||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||
Ok((data.size(), Cow::Borrowed(data.as_bytes())))
|
||||
})?;
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let corner_radii = corner_radii.scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
corner_radii,
|
||||
tile,
|
||||
grayscale,
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Paint a surface into the scene for the next frame at the current z-index.
|
||||
pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVImageBuffer) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let bounds = bounds.scale(scale_factor);
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Surface {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
image_buffer,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which
|
||||
/// layout is being requested, along with the layout ids of any children. This method is called during
|
||||
/// calls to the `Element::layout` trait method and enables any element to participate in layout.
|
||||
pub fn request_layout(
|
||||
&mut self,
|
||||
style: &Style,
|
||||
children: impl IntoIterator<Item = LayoutId>,
|
||||
) -> LayoutId {
|
||||
self.app.layout_id_buffer.clear();
|
||||
self.app.layout_id_buffer.extend(children);
|
||||
let rem_size = self.rem_size();
|
||||
|
||||
self.cx
|
||||
.window
|
||||
.layout_engine
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.request_layout(style, rem_size, &self.cx.app.layout_id_buffer)
|
||||
}
|
||||
|
||||
/// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
|
||||
/// this variant takes a function that is invoked during layout so you can use arbitrary logic to
|
||||
/// determine the element's size. One place this is used internally is when measuring text.
|
||||
///
|
||||
/// The given closure is invoked at layout time with the known dimensions and available space and
|
||||
/// returns a `Size`.
|
||||
pub fn request_measured_layout<
|
||||
F: FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
|
||||
+ 'static,
|
||||
>(
|
||||
&mut self,
|
||||
style: Style,
|
||||
measure: F,
|
||||
) -> LayoutId {
|
||||
let rem_size = self.rem_size();
|
||||
self.window
|
||||
.layout_engine
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.request_measured_layout(style, rem_size, measure)
|
||||
}
|
||||
|
||||
/// Compute the layout for the given id within the given available space.
|
||||
/// This method is called for its side effect, typically by the framework prior to painting.
|
||||
/// After calling it, you can request the bounds of the given layout node id or any descendant.
|
||||
pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
|
||||
let mut layout_engine = self.window.layout_engine.take().unwrap();
|
||||
layout_engine.compute_layout(layout_id, available_space, self);
|
||||
self.window.layout_engine = Some(layout_engine);
|
||||
}
|
||||
|
||||
/// Obtain the bounds computed for the given LayoutId relative to the window. This method should not
|
||||
/// be invoked until the paint phase begins, and will usually be invoked by GPUI itself automatically
|
||||
/// in order to pass your element its `Bounds` automatically.
|
||||
pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
|
||||
let mut bounds = self
|
||||
.window
|
||||
.layout_engine
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.layout_bounds(layout_id)
|
||||
.map(Into::into);
|
||||
bounds.origin += self.element_offset();
|
||||
bounds
|
||||
}
|
||||
|
||||
pub(crate) fn layout_style(&self, layout_id: LayoutId) -> Option<&Style> {
|
||||
self.window
|
||||
.layout_engine
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.requested_style(layout_id)
|
||||
}
|
||||
|
||||
/// Called during painting to track which z-index is on top at each pixel position
|
||||
pub fn add_opaque_layer(&mut self, bounds: Bounds<Pixels>) {
|
||||
let stacking_order = self.window.next_frame.z_index_stack.clone();
|
||||
let view_id = self.parent_view_id();
|
||||
let depth_map = &mut self.window.next_frame.depth_map;
|
||||
match depth_map.binary_search_by(|(level, _, _)| stacking_order.cmp(level)) {
|
||||
Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, view_id, bounds)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke the given function with the given focus handle present on the key dispatch stack.
|
||||
/// If you want an element to participate in key dispatch, use this method to push its key context and focus handle into the stack during paint.
|
||||
pub fn with_key_dispatch<R>(
|
||||
&mut self,
|
||||
context: Option<KeyContext>,
|
||||
focus_handle: Option<FocusHandle>,
|
||||
f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
|
||||
) -> R {
|
||||
let window = &mut self.window;
|
||||
let focus_id = focus_handle.as_ref().map(|handle| handle.id);
|
||||
window
|
||||
.next_frame
|
||||
.dispatch_tree
|
||||
.push_node(context.clone(), focus_id, None);
|
||||
|
||||
let result = f(focus_handle, self);
|
||||
|
||||
self.window.next_frame.dispatch_tree.pop_node();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Invoke the given function with the given view id present on the view stack.
|
||||
/// This is a fairly low-level method used to layout views.
|
||||
pub fn with_view_id<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
let text_system = self.text_system().clone();
|
||||
text_system.with_view(view_id, || {
|
||||
if self.window.next_frame.view_stack.last() == Some(&view_id) {
|
||||
return f(self);
|
||||
} else {
|
||||
self.window.next_frame.view_stack.push(view_id);
|
||||
let result = f(self);
|
||||
self.window.next_frame.view_stack.pop();
|
||||
result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Invoke the given function with the given view id present on the view stack.
|
||||
/// This is a fairly low-level method used to paint views.
|
||||
pub fn paint_view<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
let text_system = self.text_system().clone();
|
||||
text_system.with_view(view_id, || {
|
||||
if self.window.next_frame.view_stack.last() == Some(&view_id) {
|
||||
return f(self);
|
||||
} else {
|
||||
self.window.next_frame.view_stack.push(view_id);
|
||||
self.window
|
||||
.next_frame
|
||||
.dispatch_tree
|
||||
.push_node(None, None, Some(view_id));
|
||||
let result = f(self);
|
||||
self.window.next_frame.dispatch_tree.pop_node();
|
||||
self.window.next_frame.view_stack.pop();
|
||||
result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the
|
||||
/// platform to receive textual input with proper integration with concerns such
|
||||
/// as IME interactions. This handler will be active for the upcoming frame until the following frame is
|
||||
/// rendered.
|
||||
///
|
||||
/// [element_input_handler]: crate::ElementInputHandler
|
||||
pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
|
||||
if focus_handle.is_focused(self) {
|
||||
let view_id = self.parent_view_id();
|
||||
self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
|
||||
view_id,
|
||||
handler: Some(PlatformInputHandler::new(
|
||||
self.to_async(),
|
||||
Box::new(input_handler),
|
||||
)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a mouse event listener on the window for the next frame. The type of event
|
||||
/// is determined by the first parameter of the given listener. When the next frame is rendered
|
||||
/// the listener will be cleared.
|
||||
pub fn on_mouse_event<Event: MouseEvent>(
|
||||
&mut self,
|
||||
mut handler: impl FnMut(&Event, DispatchPhase, &mut ElementContext) + 'static,
|
||||
) {
|
||||
let view_id = self.parent_view_id();
|
||||
let order = self.window.next_frame.z_index_stack.clone();
|
||||
self.window
|
||||
.next_frame
|
||||
.mouse_listeners
|
||||
.entry(TypeId::of::<Event>())
|
||||
.or_default()
|
||||
.push((
|
||||
order,
|
||||
view_id,
|
||||
Box::new(
|
||||
move |event: &dyn Any, phase: DispatchPhase, cx: &mut ElementContext<'_>| {
|
||||
handler(event.downcast_ref().unwrap(), phase, cx)
|
||||
},
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
/// Register a key event listener on the window for the next frame. The type of event
|
||||
/// is determined by the first parameter of the given listener. When the next frame is rendered
|
||||
/// the listener will be cleared.
|
||||
///
|
||||
/// This is a fairly low-level method, so prefer using event handlers on elements unless you have
|
||||
/// a specific need to register a global listener.
|
||||
pub fn on_key_event<Event: KeyEvent>(
|
||||
&mut self,
|
||||
listener: impl Fn(&Event, DispatchPhase, &mut ElementContext) + 'static,
|
||||
) {
|
||||
self.window.next_frame.dispatch_tree.on_key_event(Rc::new(
|
||||
move |event: &dyn Any, phase, cx: &mut ElementContext<'_>| {
|
||||
if let Some(event) = event.downcast_ref::<Event>() {
|
||||
listener(event, phase, cx)
|
||||
}
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user