Detailed changes
@@ -3105,9 +3105,9 @@ dependencies = [
[[package]]
name = "grid"
-version = "0.11.0"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1df00eed8d1f0db937f6be10e46e8072b0671accb504cf0f959c5c52c679f5b9"
+checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c"
[[package]]
name = "h2"
@@ -7680,11 +7680,12 @@ dependencies = [
[[package]]
name = "taffy"
version = "0.3.11"
-source = "git+https://github.com/DioxusLabs/taffy?rev=1876f72bee5e376023eaa518aa7b8a34c769bd1b#1876f72bee5e376023eaa518aa7b8a34c769bd1b"
+source = "git+https://github.com/zed-industries/taffy?rev=5e6c2d23e70e9f2156911d11050cb686362ba277#5e6c2d23e70e9f2156911d11050cb686362ba277"
dependencies = [
"arrayvec 0.7.4",
"grid",
"num-traits",
+ "serde",
"slotmap",
]
@@ -69,7 +69,7 @@ impl CopilotCodeVerification {
let user_code = data.user_code.clone();
move |_, cx| {
cx.write_to_clipboard(ClipboardItem::new(user_code.clone()));
- cx.notify();
+ cx.refresh();
}
})
.child(div().flex_1().child(Label::new(data.user_code.clone())))
@@ -26,11 +26,11 @@ use git::diff::DiffHunkStatus;
use gpui::{
div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action,
AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners,
- CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Hsla, InteractiveBounds,
- InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
- MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine,
- SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun,
- TextStyle, View, ViewContext, WindowContext,
+ CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla,
+ InteractiveBounds, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton,
+ MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta,
+ ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement,
+ Style, Styled, TextRun, TextStyle, View, ViewContext, WindowContext,
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
@@ -2801,44 +2801,49 @@ impl Element for EditorElement {
_element_state: Option<Self::State>,
cx: &mut gpui::WindowContext,
) -> (gpui::LayoutId, Self::State) {
- self.editor.update(cx, |editor, cx| {
- editor.set_style(self.style.clone(), cx);
-
- let layout_id = match editor.mode {
- EditorMode::SingleLine => {
- let rem_size = cx.rem_size();
- let mut style = Style::default();
- style.size.width = relative(1.).into();
- style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
- cx.request_layout(&style, None)
- }
- EditorMode::AutoHeight { max_lines } => {
- let editor_handle = cx.view().clone();
- let max_line_number_width =
- self.max_line_number_width(&editor.snapshot(cx), cx);
- cx.request_measured_layout(Style::default(), move |known_dimensions, _, cx| {
- editor_handle
- .update(cx, |editor, cx| {
- compute_auto_height_layout(
- editor,
- max_lines,
- max_line_number_width,
- known_dimensions,
- cx,
- )
- })
- .unwrap_or_default()
- })
- }
- EditorMode::Full => {
- let mut style = Style::default();
- style.size.width = relative(1.).into();
- style.size.height = relative(1.).into();
- cx.request_layout(&style, None)
- }
- };
+ cx.with_view_id(self.editor.entity_id(), |cx| {
+ self.editor.update(cx, |editor, cx| {
+ editor.set_style(self.style.clone(), cx);
+
+ let layout_id = match editor.mode {
+ EditorMode::SingleLine => {
+ let rem_size = cx.rem_size();
+ let mut style = Style::default();
+ style.size.width = relative(1.).into();
+ style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
+ cx.request_layout(&style, None)
+ }
+ EditorMode::AutoHeight { max_lines } => {
+ let editor_handle = cx.view().clone();
+ let max_line_number_width =
+ self.max_line_number_width(&editor.snapshot(cx), cx);
+ cx.request_measured_layout(
+ Style::default(),
+ move |known_dimensions, _, cx| {
+ editor_handle
+ .update(cx, |editor, cx| {
+ compute_auto_height_layout(
+ editor,
+ max_lines,
+ max_line_number_width,
+ known_dimensions,
+ cx,
+ )
+ })
+ .unwrap_or_default()
+ },
+ )
+ }
+ EditorMode::Full => {
+ let mut style = Style::default();
+ style.size.width = relative(1.).into();
+ style.size.height = relative(1.).into();
+ cx.request_layout(&style, None)
+ }
+ };
- (layout_id, ())
+ (layout_id, ())
+ })
})
}
@@ -2850,65 +2855,67 @@ impl Element for EditorElement {
) {
let editor = self.editor.clone();
- cx.with_text_style(
- Some(gpui::TextStyleRefinement {
- font_size: Some(self.style.text.font_size),
- ..Default::default()
- }),
- |cx| {
- let mut layout = self.compute_layout(bounds, cx);
- let gutter_bounds = Bounds {
- origin: bounds.origin,
- size: layout.gutter_size,
- };
- let text_bounds = Bounds {
- origin: gutter_bounds.upper_right(),
- size: layout.text_size,
- };
+ cx.paint_view(self.editor.entity_id(), |cx| {
+ cx.with_text_style(
+ Some(gpui::TextStyleRefinement {
+ font_size: Some(self.style.text.font_size),
+ ..Default::default()
+ }),
+ |cx| {
+ let mut layout = self.compute_layout(bounds, cx);
+ let gutter_bounds = Bounds {
+ origin: bounds.origin,
+ size: layout.gutter_size,
+ };
+ let text_bounds = Bounds {
+ origin: gutter_bounds.upper_right(),
+ size: layout.text_size,
+ };
- let focus_handle = editor.focus_handle(cx);
- let key_context = self.editor.read(cx).key_context(cx);
- cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
- self.register_actions(cx);
- self.register_key_listeners(cx);
+ let focus_handle = editor.focus_handle(cx);
+ let key_context = self.editor.read(cx).key_context(cx);
+ cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
+ self.register_actions(cx);
+ self.register_key_listeners(cx);
- cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
- let input_handler =
- ElementInputHandler::new(bounds, self.editor.clone(), cx);
- cx.handle_input(&focus_handle, input_handler);
+ cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
+ let input_handler =
+ ElementInputHandler::new(bounds, self.editor.clone(), cx);
+ cx.handle_input(&focus_handle, input_handler);
- self.paint_background(gutter_bounds, text_bounds, &layout, cx);
- if layout.gutter_size.width > Pixels::ZERO {
- self.paint_gutter(gutter_bounds, &mut layout, cx);
- }
- self.paint_text(text_bounds, &mut layout, cx);
+ self.paint_background(gutter_bounds, text_bounds, &layout, cx);
+ if layout.gutter_size.width > Pixels::ZERO {
+ self.paint_gutter(gutter_bounds, &mut layout, cx);
+ }
+ self.paint_text(text_bounds, &mut layout, cx);
- cx.with_z_index(0, |cx| {
- self.paint_mouse_listeners(
- bounds,
- gutter_bounds,
- text_bounds,
- &layout,
- cx,
- );
- });
- if !layout.blocks.is_empty() {
cx.with_z_index(0, |cx| {
- cx.with_element_id(Some("editor_blocks"), |cx| {
- self.paint_blocks(bounds, &mut layout, cx);
- });
- })
- }
+ self.paint_mouse_listeners(
+ bounds,
+ gutter_bounds,
+ text_bounds,
+ &layout,
+ cx,
+ );
+ });
+ if !layout.blocks.is_empty() {
+ cx.with_z_index(0, |cx| {
+ cx.with_element_id(Some("editor_blocks"), |cx| {
+ self.paint_blocks(bounds, &mut layout, cx);
+ });
+ })
+ }
- cx.with_z_index(1, |cx| {
- self.paint_overlays(text_bounds, &mut layout, cx);
- });
+ cx.with_z_index(1, |cx| {
+ self.paint_overlays(text_bounds, &mut layout, cx);
+ });
- cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx));
- });
- })
- },
- );
+ cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx));
+ });
+ })
+ },
+ )
+ })
}
}
@@ -3404,14 +3411,16 @@ mod tests {
})
.unwrap();
let state = cx
- .update_window(window.into(), |_, cx| {
- element.compute_layout(
- Bounds {
- origin: point(px(500.), px(500.)),
- size: size(px(500.), px(500.)),
- },
- cx,
- )
+ .update_window(window.into(), |view, cx| {
+ cx.with_view_id(view.entity_id(), |cx| {
+ element.compute_layout(
+ Bounds {
+ origin: point(px(500.), px(500.)),
+ size: size(px(500.), px(500.)),
+ },
+ cx,
+ )
+ })
})
.unwrap();
@@ -3496,14 +3505,16 @@ mod tests {
});
let state = cx
- .update_window(window.into(), |_, cx| {
- element.compute_layout(
- Bounds {
- origin: point(px(500.), px(500.)),
- size: size(px(500.), px(500.)),
- },
- cx,
- )
+ .update_window(window.into(), |view, cx| {
+ cx.with_view_id(view.entity_id(), |cx| {
+ element.compute_layout(
+ Bounds {
+ origin: point(px(500.), px(500.)),
+ size: size(px(500.), px(500.)),
+ },
+ cx,
+ )
+ })
})
.unwrap();
assert_eq!(state.selections.len(), 1);
@@ -3558,14 +3569,16 @@ mod tests {
let mut element = EditorElement::new(&editor, style);
let state = cx
- .update_window(window.into(), |_, cx| {
- element.compute_layout(
- Bounds {
- origin: point(px(500.), px(500.)),
- size: size(px(500.), px(500.)),
- },
- cx,
- )
+ .update_window(window.into(), |view, cx| {
+ cx.with_view_id(view.entity_id(), |cx| {
+ element.compute_layout(
+ Bounds {
+ origin: point(px(500.), px(500.)),
+ size: size(px(500.), px(500.)),
+ },
+ cx,
+ )
+ })
})
.unwrap();
let size = state.position_map.size;
@@ -3582,10 +3595,8 @@ mod tests {
// Don't panic.
let bounds = Bounds::<Pixels>::new(Default::default(), size);
- cx.update_window(window.into(), |_, cx| {
- element.paint(bounds, &mut (), cx);
- })
- .unwrap()
+ cx.update_window(window.into(), |_, cx| element.paint(bounds, &mut (), cx))
+ .unwrap()
}
#[gpui::test]
@@ -46,7 +46,7 @@ serde_derive.workspace = true
serde_json.workspace = true
smallvec.workspace = true
smol.workspace = true
-taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "1876f72bee5e376023eaa518aa7b8a34c769bd1b" }
+taffy = { git = "https://github.com/zed-industries/taffy", rev = "5e6c2d23e70e9f2156911d11050cb686362ba277" }
thiserror.workspace = true
time.workspace = true
tiny-skia = "0.5"
@@ -2,7 +2,7 @@ use crate::{seal::Sealed, AppContext, Context, Entity, ModelContext};
use anyhow::{anyhow, Result};
use derive_more::{Deref, DerefMut};
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
-use slotmap::{SecondaryMap, SlotMap};
+use slotmap::{KeyData, SecondaryMap, SlotMap};
use std::{
any::{type_name, Any, TypeId},
fmt::{self, Display},
@@ -24,6 +24,12 @@ slotmap::new_key_type! {
pub struct EntityId;
}
+impl From<u64> for EntityId {
+ fn from(value: u64) -> Self {
+ Self(KeyData::from_ffi(value))
+ }
+}
+
impl EntityId {
pub fn as_u64(self) -> u64 {
self.0.as_ffi()
@@ -992,7 +992,7 @@ impl Interactivity {
if phase == DispatchPhase::Capture
&& group_bounds.contains(&event.position) != hovered
{
- cx.notify();
+ cx.refresh();
}
});
}
@@ -1046,7 +1046,7 @@ impl Interactivity {
if e.modifiers.command != command_held
&& text_bounds.contains(&cx.mouse_position())
{
- cx.notify();
+ cx.refresh();
}
}
});
@@ -1057,7 +1057,7 @@ impl Interactivity {
if phase == DispatchPhase::Capture
&& bounds.contains(&event.position) != hovered
{
- cx.notify();
+ cx.refresh();
}
},
);
@@ -1197,7 +1197,7 @@ impl Interactivity {
if phase == DispatchPhase::Capture
&& bounds.contains(&event.position) != hovered
{
- cx.notify();
+ cx.refresh();
}
});
}
@@ -1231,7 +1231,7 @@ impl Interactivity {
if can_drop {
listener(drag.value.as_ref(), cx);
- cx.notify();
+ cx.refresh();
cx.stop_propagation();
}
}
@@ -1262,7 +1262,7 @@ impl Interactivity {
&& interactive_bounds.visibly_contains(&event.position, cx)
{
*pending_mouse_down.borrow_mut() = Some(event.clone());
- cx.notify();
+ cx.refresh();
}
}
});
@@ -1293,7 +1293,7 @@ impl Interactivity {
cursor_offset,
});
pending_mouse_down.take();
- cx.notify();
+ cx.refresh();
cx.stop_propagation();
}
}
@@ -1313,7 +1313,7 @@ impl Interactivity {
pending_mouse_down.borrow_mut();
if pending_mouse_down.is_some() {
captured_mouse_down = pending_mouse_down.take();
- cx.notify();
+ cx.refresh();
}
}
// Fire click handlers during the bubble phase.
@@ -1407,7 +1407,7 @@ impl Interactivity {
_task: None,
},
);
- cx.notify();
+ cx.refresh();
})
.ok();
}
@@ -1447,7 +1447,7 @@ impl Interactivity {
cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Capture {
*active_state.borrow_mut() = ElementClickedState::default();
- cx.notify();
+ cx.refresh();
}
});
} else {
@@ -1465,7 +1465,7 @@ impl Interactivity {
if group || element {
*active_state.borrow_mut() =
ElementClickedState { group, element };
- cx.notify();
+ cx.refresh();
}
}
});
@@ -1525,7 +1525,7 @@ impl Interactivity {
}
if *scroll_offset != old_scroll_offset {
- cx.notify();
+ cx.refresh();
cx.stop_propagation();
}
}
@@ -109,7 +109,7 @@ impl Element for Img {
} else {
cx.spawn(|mut cx| async move {
if image_future.await.ok().is_some() {
- cx.on_next_frame(|cx| cx.notify());
+ cx.on_next_frame(|cx| cx.refresh());
}
})
.detach();
@@ -260,7 +260,7 @@ impl StateInner {
);
}
- cx.notify();
+ cx.refresh();
}
fn logical_scroll_top(&self) -> ListOffset {
@@ -392,7 +392,7 @@ impl Element for InteractiveText {
}
mouse_down.take();
- cx.notify();
+ cx.refresh();
}
});
} else {
@@ -402,7 +402,7 @@ impl Element for InteractiveText {
text_state.index_for_position(bounds, event.position)
{
mouse_down.set(Some(mouse_down_index));
- cx.notify();
+ cx.refresh();
}
}
});
@@ -2272,7 +2272,7 @@ impl From<f64> for GlobalPixels {
/// For example, if the root element's font-size is `16px`, then `1rem` equals `16px`. A length of `2rems` would then be `32px`.
///
/// [set_rem_size]: crate::WindowContext::set_rem_size
-#[derive(Clone, Copy, Default, Add, Sub, Mul, Div, Neg)]
+#[derive(Clone, Copy, Default, Add, Sub, Mul, Div, Neg, PartialEq)]
pub struct Rems(pub f32);
impl Mul<Pixels> for Rems {
@@ -2295,7 +2295,7 @@ impl Debug for Rems {
/// affected by the current font size, or a number of rems, which is relative to the font size of
/// the root element. It is used for specifying dimensions that are either independent of or
/// related to the typographic scale.
-#[derive(Clone, Copy, Debug, Neg)]
+#[derive(Clone, Copy, Debug, Neg, PartialEq)]
pub enum AbsoluteLength {
/// A length in pixels.
Pixels(Pixels),
@@ -2366,7 +2366,7 @@ impl Default for AbsoluteLength {
/// This enum represents lengths that have a specific value, as opposed to lengths that are automatically
/// determined by the context. It includes absolute lengths in pixels or rems, and relative lengths as a
/// fraction of the parent's size.
-#[derive(Clone, Copy, Neg)]
+#[derive(Clone, Copy, Neg, PartialEq)]
pub enum DefiniteLength {
/// An absolute length specified in pixels or rems.
Absolute(AbsoluteLength),
@@ -1,12 +1,13 @@
use crate::{
- arena::ArenaRef, Action, ActionRegistry, DispatchPhase, FocusId, KeyBinding, KeyContext,
- KeyMatch, Keymap, Keystroke, KeystrokeMatcher, WindowContext,
+ Action, ActionRegistry, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, KeyMatch,
+ Keymap, Keystroke, KeystrokeMatcher, WindowContext,
};
-use collections::HashMap;
+use collections::FxHashMap;
use parking_lot::Mutex;
-use smallvec::SmallVec;
+use smallvec::{smallvec, SmallVec};
use std::{
any::{Any, TypeId},
+ mem,
rc::Rc,
sync::Arc,
};
@@ -18,8 +19,9 @@ pub(crate) struct DispatchTree {
node_stack: Vec<DispatchNodeId>,
pub(crate) context_stack: Vec<KeyContext>,
nodes: Vec<DispatchNode>,
- focusable_node_ids: HashMap<FocusId, DispatchNodeId>,
- keystroke_matchers: HashMap<SmallVec<[KeyContext; 4]>, KeystrokeMatcher>,
+ focusable_node_ids: FxHashMap<FocusId, DispatchNodeId>,
+ view_node_ids: FxHashMap<EntityId, DispatchNodeId>,
+ keystroke_matchers: FxHashMap<SmallVec<[KeyContext; 4]>, KeystrokeMatcher>,
keymap: Arc<Mutex<Keymap>>,
action_registry: Rc<ActionRegistry>,
}
@@ -30,15 +32,16 @@ pub(crate) struct DispatchNode {
pub action_listeners: Vec<DispatchActionListener>,
pub context: Option<KeyContext>,
focus_id: Option<FocusId>,
+ view_id: Option<EntityId>,
parent: Option<DispatchNodeId>,
}
-type KeyListener = ArenaRef<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
+type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
#[derive(Clone)]
pub(crate) struct DispatchActionListener {
pub(crate) action_type: TypeId,
- pub(crate) listener: ArenaRef<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
+ pub(crate) listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
}
impl DispatchTree {
@@ -47,8 +50,9 @@ impl DispatchTree {
node_stack: Vec::new(),
context_stack: Vec::new(),
nodes: Vec::new(),
- focusable_node_ids: HashMap::default(),
- keystroke_matchers: HashMap::default(),
+ focusable_node_ids: FxHashMap::default(),
+ view_node_ids: FxHashMap::default(),
+ keystroke_matchers: FxHashMap::default(),
keymap,
action_registry,
}
@@ -56,31 +60,101 @@ impl DispatchTree {
pub fn clear(&mut self) {
self.node_stack.clear();
- self.nodes.clear();
self.context_stack.clear();
+ self.nodes.clear();
self.focusable_node_ids.clear();
+ self.view_node_ids.clear();
self.keystroke_matchers.clear();
}
- pub fn push_node(&mut self, context: Option<KeyContext>) {
+ pub fn push_node(
+ &mut self,
+ context: Option<KeyContext>,
+ focus_id: Option<FocusId>,
+ view_id: Option<EntityId>,
+ ) {
let parent = self.node_stack.last().copied();
let node_id = DispatchNodeId(self.nodes.len());
self.nodes.push(DispatchNode {
parent,
+ focus_id,
+ view_id,
..Default::default()
});
self.node_stack.push(node_id);
+
if let Some(context) = context {
self.active_node().context = Some(context.clone());
self.context_stack.push(context);
}
+
+ if let Some(focus_id) = focus_id {
+ self.focusable_node_ids.insert(focus_id, node_id);
+ }
+
+ if let Some(view_id) = view_id {
+ self.view_node_ids.insert(view_id, node_id);
+ }
}
pub fn pop_node(&mut self) {
- let node_id = self.node_stack.pop().unwrap();
- if self.nodes[node_id.0].context.is_some() {
+ let node = &self.nodes[self.active_node_id().0];
+ if node.context.is_some() {
self.context_stack.pop();
}
+ self.node_stack.pop();
+ }
+
+ fn move_node(&mut self, source: &mut DispatchNode) {
+ self.push_node(source.context.take(), source.focus_id, source.view_id);
+ let target = self.active_node();
+ target.key_listeners = mem::take(&mut source.key_listeners);
+ target.action_listeners = mem::take(&mut source.action_listeners);
+ }
+
+ pub fn graft(&mut self, view_id: EntityId, source: &mut Self) -> SmallVec<[EntityId; 8]> {
+ let view_source_node_id = source
+ .view_node_ids
+ .get(&view_id)
+ .expect("view should exist in previous dispatch tree");
+ let view_source_node = &mut source.nodes[view_source_node_id.0];
+ self.move_node(view_source_node);
+
+ let mut grafted_view_ids = smallvec![view_id];
+ let mut source_stack = vec![*view_source_node_id];
+ for (source_node_id, source_node) in source
+ .nodes
+ .iter_mut()
+ .enumerate()
+ .skip(view_source_node_id.0 + 1)
+ {
+ let source_node_id = DispatchNodeId(source_node_id);
+ while let Some(source_ancestor) = source_stack.last() {
+ if source_node.parent != Some(*source_ancestor) {
+ source_stack.pop();
+ self.pop_node();
+ } else {
+ break;
+ }
+ }
+
+ if source_stack.is_empty() {
+ break;
+ } else {
+ source_stack.push(source_node_id);
+ self.move_node(source_node);
+ if let Some(view_id) = source_node.view_id {
+ grafted_view_ids.push(view_id);
+ }
+ }
+ }
+
+ while !source_stack.is_empty() {
+ source_stack.pop();
+ self.pop_node();
+ }
+
+ grafted_view_ids
}
pub fn clear_pending_keystrokes(&mut self) {
@@ -117,7 +191,7 @@ impl DispatchTree {
pub fn on_action(
&mut self,
action_type: TypeId,
- listener: ArenaRef<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
+ listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
) {
self.active_node()
.action_listeners
@@ -127,12 +201,6 @@ impl DispatchTree {
});
}
- pub fn make_focusable(&mut self, focus_id: FocusId) {
- let node_id = self.active_node_id();
- self.active_node().focus_id = Some(focus_id);
- self.focusable_node_ids.insert(focus_id, node_id);
- }
-
pub fn focus_contains(&self, parent: FocusId, child: FocusId) -> bool {
if parent == child {
return true;
@@ -261,6 +329,20 @@ impl DispatchTree {
focus_path
}
+ pub fn view_path(&self, view_id: EntityId) -> SmallVec<[EntityId; 8]> {
+ let mut view_path: SmallVec<[EntityId; 8]> = SmallVec::new();
+ let mut current_node_id = self.view_node_ids.get(&view_id).copied();
+ while let Some(node_id) = current_node_id {
+ let node = self.node(node_id);
+ if let Some(view_id) = node.view_id {
+ view_path.push(view_id);
+ }
+ current_node_id = node.parent;
+ }
+ view_path.reverse(); // Reverse the path so it goes from the root to the view node.
+ view_path
+ }
+
pub fn node(&self, node_id: DispatchNodeId) -> &DispatchNode {
&self.nodes[node_id.0]
}
@@ -44,8 +44,6 @@ pub(crate) fn current_platform() -> Rc<dyn Platform> {
Rc::new(MacPlatform::new())
}
-pub type DrawWindow = Box<dyn FnMut() -> Result<Scene>>;
-
pub(crate) trait Platform: 'static {
fn background_executor(&self) -> BackgroundExecutor;
fn foreground_executor(&self) -> ForegroundExecutor;
@@ -66,7 +64,6 @@ pub(crate) trait Platform: 'static {
&self,
handle: AnyWindowHandle,
options: WindowOptions,
- draw: DrawWindow,
) -> Box<dyn PlatformWindow>;
fn set_display_link_output_callback(
@@ -148,7 +145,7 @@ pub trait PlatformWindow {
fn modifiers(&self) -> Modifiers;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
- fn clear_input_handler(&mut self);
+ fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>>;
fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
fn activate(&self);
fn set_title(&mut self, title: &str);
@@ -157,6 +154,7 @@ pub trait PlatformWindow {
fn minimize(&self);
fn zoom(&self);
fn toggle_full_screen(&self);
+ fn on_request_frame(&self, callback: Box<dyn FnMut()>);
fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
@@ -167,6 +165,7 @@ pub trait PlatformWindow {
fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
fn invalidate(&self);
+ fn draw(&self, scene: &Scene);
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
@@ -3,8 +3,7 @@ use crate::{
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
ForegroundExecutor, InputEvent, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker,
MacTextSystem, MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
- PlatformTextSystem, PlatformWindow, Result, Scene, SemanticVersion, VideoTimestamp,
- WindowOptions,
+ PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp, WindowOptions,
};
use anyhow::anyhow;
use block::ConcreteBlock;
@@ -498,14 +497,8 @@ impl Platform for MacPlatform {
&self,
handle: AnyWindowHandle,
options: WindowOptions,
- draw: Box<dyn FnMut() -> Result<Scene>>,
) -> Box<dyn PlatformWindow> {
- Box::new(MacWindow::open(
- handle,
- options,
- draw,
- self.foreground_executor(),
- ))
+ Box::new(MacWindow::open(handle, options, self.foreground_executor()))
}
fn set_display_link_output_callback(
@@ -1,6 +1,6 @@
use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
use crate::{
- display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, DrawWindow, ExternalPaths,
+ display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, ExternalPaths,
FileDropEvent, ForegroundExecutor, GlobalPixels, InputEvent, KeyDownEvent, Keystroke,
Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
@@ -46,7 +46,6 @@ use std::{
sync::{Arc, Weak},
time::Duration,
};
-use util::ResultExt;
const WINDOW_STATE_IVAR: &str = "windowState";
@@ -318,8 +317,8 @@ struct MacWindowState {
executor: ForegroundExecutor,
native_window: id,
renderer: MetalRenderer,
- draw: Option<DrawWindow>,
kind: WindowKind,
+ request_frame_callback: Option<Box<dyn FnMut()>>,
event_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
activate_callback: Option<Box<dyn FnMut(bool)>>,
resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
@@ -455,7 +454,6 @@ impl MacWindow {
pub fn open(
handle: AnyWindowHandle,
options: WindowOptions,
- draw: DrawWindow,
executor: ForegroundExecutor,
) -> Self {
unsafe {
@@ -547,8 +545,8 @@ impl MacWindow {
executor,
native_window,
renderer: MetalRenderer::new(true),
- draw: Some(draw),
kind: options.kind,
+ request_frame_callback: None,
event_callback: None,
activate_callback: None,
resize_callback: None,
@@ -770,8 +768,8 @@ impl PlatformWindow for MacWindow {
self.0.as_ref().lock().input_handler = Some(input_handler);
}
- fn clear_input_handler(&mut self) {
- self.0.as_ref().lock().input_handler = None;
+ fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>> {
+ self.0.as_ref().lock().input_handler.take()
}
fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize> {
@@ -926,6 +924,10 @@ impl PlatformWindow for MacWindow {
.detach();
}
+ fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
+ self.0.as_ref().lock().request_frame_callback = Some(callback);
+ }
+
fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
self.0.as_ref().lock().event_callback = Some(callback);
}
@@ -990,6 +992,11 @@ impl PlatformWindow for MacWindow {
}
}
+ fn draw(&self, scene: &crate::Scene) {
+ let mut this = self.0.lock();
+ this.renderer.draw(scene);
+ }
+
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
self.0.lock().renderer.sprite_atlas().clone()
}
@@ -1437,15 +1444,12 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
}
extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
- unsafe {
- let window_state = get_window_state(this);
- let mut draw = window_state.lock().draw.take().unwrap();
- let scene = draw().log_err();
- let mut window_state = window_state.lock();
- window_state.draw = Some(draw);
- if let Some(scene) = scene {
- window_state.renderer.draw(&scene);
- }
+ let window_state = unsafe { get_window_state(this) };
+ let mut lock = window_state.lock();
+ if let Some(mut callback) = lock.request_frame_callback.take() {
+ drop(lock);
+ callback();
+ window_state.lock().request_frame_callback = Some(callback);
}
}
@@ -1,7 +1,6 @@
use crate::{
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor,
- Keymap, Platform, PlatformDisplay, PlatformTextSystem, Scene, TestDisplay, TestWindow,
- WindowOptions,
+ Keymap, Platform, PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions,
};
use anyhow::{anyhow, Result};
use collections::VecDeque;
@@ -166,7 +165,6 @@ impl Platform for TestPlatform {
&self,
handle: AnyWindowHandle,
options: WindowOptions,
- _draw: Box<dyn FnMut() -> Result<Scene>>,
) -> Box<dyn crate::PlatformWindow> {
let window = TestWindow::new(
options,
@@ -167,8 +167,8 @@ impl PlatformWindow for TestWindow {
self.0.lock().input_handler = Some(input_handler);
}
- fn clear_input_handler(&mut self) {
- self.0.lock().input_handler = None;
+ fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>> {
+ self.0.lock().input_handler.take()
}
fn prompt(
@@ -218,6 +218,8 @@ impl PlatformWindow for TestWindow {
unimplemented!()
}
+ fn on_request_frame(&self, _callback: Box<dyn FnMut()>) {}
+
fn on_input(&self, callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
self.0.lock().input_callback = Some(callback)
}
@@ -254,9 +256,9 @@ impl PlatformWindow for TestWindow {
unimplemented!()
}
- fn invalidate(&self) {
- // (self.draw.lock())().unwrap();
- }
+ fn invalidate(&self) {}
+
+ fn draw(&self, _scene: &crate::Scene) {}
fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
self.0.lock().sprite_atlas.clone()
@@ -1,9 +1,9 @@
use crate::{
- point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, Hsla, Pixels, Point,
- ScaledPixels, StackingOrder,
+ point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, EntityId, Hsla, Pixels,
+ Point, ScaledPixels, StackingOrder,
};
-use collections::BTreeMap;
-use std::{fmt::Debug, iter::Peekable, mem, slice};
+use collections::{BTreeMap, FxHashSet};
+use std::{fmt::Debug, iter::Peekable, slice};
// Exported to metal
pub(crate) type PointF = Point<f32>;
@@ -11,74 +11,85 @@ pub(crate) type PointF = Point<f32>;
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
pub type LayerId = u32;
-
pub type DrawOrder = u32;
-#[derive(Default)]
-pub(crate) struct SceneBuilder {
- last_order: Option<(StackingOrder, LayerId)>,
- layers_by_order: BTreeMap<StackingOrder, LayerId>,
- shadows: Vec<Shadow>,
- quads: Vec<Quad>,
- paths: Vec<Path<ScaledPixels>>,
- underlines: Vec<Underline>,
- monochrome_sprites: Vec<MonochromeSprite>,
- polychrome_sprites: Vec<PolychromeSprite>,
- surfaces: Vec<Surface>,
+#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
+#[repr(C)]
+pub struct ViewId {
+ low_bits: u32,
+ high_bits: u32,
}
-impl SceneBuilder {
- pub fn build(&mut self) -> Scene {
- let mut orders = vec![0; self.layers_by_order.len()];
- for (ix, layer_id) in self.layers_by_order.values().enumerate() {
- orders[*layer_id as usize] = ix as u32;
- }
- self.layers_by_order.clear();
- self.last_order = None;
-
- for shadow in &mut self.shadows {
- shadow.order = orders[shadow.order as usize];
- }
- self.shadows.sort_by_key(|shadow| shadow.order);
-
- for quad in &mut self.quads {
- quad.order = orders[quad.order as usize];
- }
- self.quads.sort_by_key(|quad| quad.order);
-
- for path in &mut self.paths {
- path.order = orders[path.order as usize];
+impl From<EntityId> for ViewId {
+ fn from(value: EntityId) -> Self {
+ let value = value.as_u64();
+ Self {
+ low_bits: value as u32,
+ high_bits: (value >> 32) as u32,
}
- self.paths.sort_by_key(|path| path.order);
+ }
+}
- for underline in &mut self.underlines {
- underline.order = orders[underline.order as usize];
- }
- self.underlines.sort_by_key(|underline| underline.order);
+impl From<ViewId> for EntityId {
+ fn from(value: ViewId) -> Self {
+ let value = (value.low_bits as u64) | ((value.high_bits as u64) << 32);
+ value.into()
+ }
+}
- for monochrome_sprite in &mut self.monochrome_sprites {
- monochrome_sprite.order = orders[monochrome_sprite.order as usize];
- }
- self.monochrome_sprites.sort_by_key(|sprite| sprite.order);
+#[derive(Default)]
+pub struct Scene {
+ layers_by_order: BTreeMap<StackingOrder, LayerId>,
+ orders_by_layer: BTreeMap<LayerId, StackingOrder>,
+ pub(crate) shadows: Vec<Shadow>,
+ pub(crate) quads: Vec<Quad>,
+ pub(crate) paths: Vec<Path<ScaledPixels>>,
+ pub(crate) underlines: Vec<Underline>,
+ pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
+ pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
+ pub(crate) surfaces: Vec<Surface>,
+}
- for polychrome_sprite in &mut self.polychrome_sprites {
- polychrome_sprite.order = orders[polychrome_sprite.order as usize];
- }
- self.polychrome_sprites.sort_by_key(|sprite| sprite.order);
+impl Scene {
+ pub fn clear(&mut self) {
+ self.layers_by_order.clear();
+ self.orders_by_layer.clear();
+ self.shadows.clear();
+ self.quads.clear();
+ self.paths.clear();
+ self.underlines.clear();
+ self.monochrome_sprites.clear();
+ self.polychrome_sprites.clear();
+ self.surfaces.clear();
+ }
- for surface in &mut self.surfaces {
- surface.order = orders[surface.order as usize];
- }
- self.surfaces.sort_by_key(|surface| surface.order);
+ pub fn paths(&self) -> &[Path<ScaledPixels>] {
+ &self.paths
+ }
- Scene {
- shadows: mem::take(&mut self.shadows),
- quads: mem::take(&mut self.quads),
- paths: mem::take(&mut self.paths),
- underlines: mem::take(&mut self.underlines),
- monochrome_sprites: mem::take(&mut self.monochrome_sprites),
- polychrome_sprites: mem::take(&mut self.polychrome_sprites),
- surfaces: mem::take(&mut self.surfaces),
+ pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
+ BatchIterator {
+ shadows: &self.shadows,
+ shadows_start: 0,
+ shadows_iter: self.shadows.iter().peekable(),
+ quads: &self.quads,
+ quads_start: 0,
+ quads_iter: self.quads.iter().peekable(),
+ paths: &self.paths,
+ paths_start: 0,
+ paths_iter: self.paths.iter().peekable(),
+ underlines: &self.underlines,
+ underlines_start: 0,
+ underlines_iter: self.underlines.iter().peekable(),
+ monochrome_sprites: &self.monochrome_sprites,
+ monochrome_sprites_start: 0,
+ monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
+ polychrome_sprites: &self.polychrome_sprites,
+ polychrome_sprites_start: 0,
+ polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
+ surfaces: &self.surfaces,
+ surfaces_start: 0,
+ surfaces_iter: self.surfaces.iter().peekable(),
}
}
@@ -96,95 +107,139 @@ impl SceneBuilder {
let layer_id = self.layer_id_for_order(order);
match primitive {
Primitive::Shadow(mut shadow) => {
- shadow.order = layer_id;
+ shadow.layer_id = layer_id;
self.shadows.push(shadow);
}
Primitive::Quad(mut quad) => {
- quad.order = layer_id;
+ quad.layer_id = layer_id;
self.quads.push(quad);
}
Primitive::Path(mut path) => {
- path.order = layer_id;
+ path.layer_id = layer_id;
path.id = PathId(self.paths.len());
self.paths.push(path);
}
Primitive::Underline(mut underline) => {
- underline.order = layer_id;
+ underline.layer_id = layer_id;
self.underlines.push(underline);
}
Primitive::MonochromeSprite(mut sprite) => {
- sprite.order = layer_id;
+ sprite.layer_id = layer_id;
self.monochrome_sprites.push(sprite);
}
Primitive::PolychromeSprite(mut sprite) => {
- sprite.order = layer_id;
+ sprite.layer_id = layer_id;
self.polychrome_sprites.push(sprite);
}
Primitive::Surface(mut surface) => {
- surface.order = layer_id;
+ surface.layer_id = layer_id;
self.surfaces.push(surface);
}
}
}
- fn layer_id_for_order(&mut self, order: &StackingOrder) -> u32 {
- if let Some((last_order, last_layer_id)) = self.last_order.as_ref() {
- if last_order == order {
- return *last_layer_id;
- }
- };
-
- let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
+ fn layer_id_for_order(&mut self, order: &StackingOrder) -> LayerId {
+ if let Some(layer_id) = self.layers_by_order.get(order) {
*layer_id
} else {
let next_id = self.layers_by_order.len() as LayerId;
self.layers_by_order.insert(order.clone(), next_id);
+ self.orders_by_layer.insert(next_id, order.clone());
next_id
- };
- self.last_order = Some((order.clone(), layer_id));
- layer_id
+ }
}
-}
-pub struct Scene {
- pub shadows: Vec<Shadow>,
- pub quads: Vec<Quad>,
- pub paths: Vec<Path<ScaledPixels>>,
- pub underlines: Vec<Underline>,
- pub monochrome_sprites: Vec<MonochromeSprite>,
- pub polychrome_sprites: Vec<PolychromeSprite>,
- pub surfaces: Vec<Surface>,
-}
+ pub fn reuse_views(&mut self, views: &FxHashSet<EntityId>, prev_scene: &mut Self) {
+ for shadow in prev_scene.shadows.drain(..) {
+ if views.contains(&shadow.view_id.into()) {
+ let order = &prev_scene.orders_by_layer[&shadow.layer_id];
+ self.insert(&order, shadow);
+ }
+ }
-impl Scene {
- pub fn paths(&self) -> &[Path<ScaledPixels>] {
- &self.paths
+ for quad in prev_scene.quads.drain(..) {
+ if views.contains(&quad.view_id.into()) {
+ let order = &prev_scene.orders_by_layer[&quad.layer_id];
+ self.insert(&order, quad);
+ }
+ }
+
+ for path in prev_scene.paths.drain(..) {
+ if views.contains(&path.view_id.into()) {
+ let order = &prev_scene.orders_by_layer[&path.layer_id];
+ self.insert(&order, path);
+ }
+ }
+
+ for underline in prev_scene.underlines.drain(..) {
+ if views.contains(&underline.view_id.into()) {
+ let order = &prev_scene.orders_by_layer[&underline.layer_id];
+ self.insert(&order, underline);
+ }
+ }
+
+ for sprite in prev_scene.monochrome_sprites.drain(..) {
+ if views.contains(&sprite.view_id.into()) {
+ let order = &prev_scene.orders_by_layer[&sprite.layer_id];
+ self.insert(&order, sprite);
+ }
+ }
+
+ for sprite in prev_scene.polychrome_sprites.drain(..) {
+ if views.contains(&sprite.view_id.into()) {
+ let order = &prev_scene.orders_by_layer[&sprite.layer_id];
+ self.insert(&order, sprite);
+ }
+ }
+
+ for surface in prev_scene.surfaces.drain(..) {
+ if views.contains(&surface.view_id.into()) {
+ let order = &prev_scene.orders_by_layer[&surface.layer_id];
+ self.insert(&order, surface);
+ }
+ }
}
- pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
- BatchIterator {
- shadows: &self.shadows,
- shadows_start: 0,
- shadows_iter: self.shadows.iter().peekable(),
- quads: &self.quads,
- quads_start: 0,
- quads_iter: self.quads.iter().peekable(),
- paths: &self.paths,
- paths_start: 0,
- paths_iter: self.paths.iter().peekable(),
- underlines: &self.underlines,
- underlines_start: 0,
- underlines_iter: self.underlines.iter().peekable(),
- monochrome_sprites: &self.monochrome_sprites,
- monochrome_sprites_start: 0,
- monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
- polychrome_sprites: &self.polychrome_sprites,
- polychrome_sprites_start: 0,
- polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
- surfaces: &self.surfaces,
- surfaces_start: 0,
- surfaces_iter: self.surfaces.iter().peekable(),
+ pub fn finish(&mut self) {
+ let mut orders = vec![0; self.layers_by_order.len()];
+ for (ix, layer_id) in self.layers_by_order.values().enumerate() {
+ orders[*layer_id as usize] = ix as u32;
+ }
+
+ for shadow in &mut self.shadows {
+ shadow.order = orders[shadow.layer_id as usize];
+ }
+ self.shadows.sort_by_key(|shadow| shadow.order);
+
+ for quad in &mut self.quads {
+ quad.order = orders[quad.layer_id as usize];
+ }
+ self.quads.sort_by_key(|quad| quad.order);
+
+ for path in &mut self.paths {
+ path.order = orders[path.layer_id as usize];
+ }
+ self.paths.sort_by_key(|path| path.order);
+
+ for underline in &mut self.underlines {
+ underline.order = orders[underline.layer_id as usize];
+ }
+ self.underlines.sort_by_key(|underline| underline.order);
+
+ for monochrome_sprite in &mut self.monochrome_sprites {
+ monochrome_sprite.order = orders[monochrome_sprite.layer_id as usize];
}
+ self.monochrome_sprites.sort_by_key(|sprite| sprite.order);
+
+ for polychrome_sprite in &mut self.polychrome_sprites {
+ polychrome_sprite.order = orders[polychrome_sprite.layer_id as usize];
+ }
+ self.polychrome_sprites.sort_by_key(|sprite| sprite.order);
+
+ for surface in &mut self.surfaces {
+ surface.order = orders[surface.layer_id as usize];
+ }
+ self.surfaces.sort_by_key(|surface| surface.order);
}
}
@@ -439,7 +494,9 @@ pub(crate) enum PrimitiveBatch<'a> {
#[derive(Default, Debug, Clone, Eq, PartialEq)]
#[repr(C)]
pub struct Quad {
- pub order: u32, // Initially a LayerId, then a DrawOrder.
+ pub view_id: ViewId,
+ pub layer_id: LayerId,
+ pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub background: Hsla,
@@ -469,7 +526,9 @@ impl From<Quad> for Primitive {
#[derive(Debug, Clone, Eq, PartialEq)]
#[repr(C)]
pub struct Underline {
- pub order: u32,
+ pub view_id: ViewId,
+ pub layer_id: LayerId,
+ pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub thickness: ScaledPixels,
@@ -498,7 +557,9 @@ impl From<Underline> for Primitive {
#[derive(Debug, Clone, Eq, PartialEq)]
#[repr(C)]
pub struct Shadow {
- pub order: u32,
+ pub view_id: ViewId,
+ pub layer_id: LayerId,
+ pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub corner_radii: Corners<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
@@ -527,7 +588,9 @@ impl From<Shadow> for Primitive {
#[derive(Clone, Debug, Eq, PartialEq)]
#[repr(C)]
pub struct MonochromeSprite {
- pub order: u32,
+ pub view_id: ViewId,
+ pub layer_id: LayerId,
+ pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub color: Hsla,
@@ -558,7 +621,9 @@ impl From<MonochromeSprite> for Primitive {
#[derive(Clone, Debug, Eq, PartialEq)]
#[repr(C)]
pub struct PolychromeSprite {
- pub order: u32,
+ pub view_id: ViewId,
+ pub layer_id: LayerId,
+ pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub corner_radii: Corners<ScaledPixels>,
@@ -589,7 +654,9 @@ impl From<PolychromeSprite> for Primitive {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Surface {
- pub order: u32,
+ pub view_id: ViewId,
+ pub layer_id: LayerId,
+ pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub image_buffer: media::core_video::CVImageBuffer,
@@ -619,7 +686,9 @@ pub(crate) struct PathId(pub(crate) usize);
#[derive(Debug)]
pub struct Path<P: Clone + Default + Debug> {
pub(crate) id: PathId,
- order: u32,
+ pub(crate) view_id: ViewId,
+ layer_id: LayerId,
+ order: DrawOrder,
pub(crate) bounds: Bounds<P>,
pub(crate) content_mask: ContentMask<P>,
pub(crate) vertices: Vec<PathVertex<P>>,
@@ -633,7 +702,9 @@ impl Path<Pixels> {
pub fn new(start: Point<Pixels>) -> Self {
Self {
id: PathId(0),
- order: 0,
+ view_id: ViewId::default(),
+ layer_id: LayerId::default(),
+ order: DrawOrder::default(),
vertices: Vec::new(),
start,
current: start,
@@ -650,6 +721,8 @@ impl Path<Pixels> {
pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
Path {
id: self.id,
+ view_id: self.view_id,
+ layer_id: self.layer_id,
order: self.order,
bounds: self.bounds.scale(factor),
content_mask: self.content_mask.scale(factor),
@@ -146,7 +146,7 @@ pub enum WhiteSpace {
Nowrap,
}
-#[derive(Refineable, Clone, Debug)]
+#[derive(Refineable, Clone, Debug, PartialEq)]
#[refineable(Debug)]
pub struct TextStyle {
pub color: Hsla,
@@ -6,15 +6,13 @@ use collections::{FxHashMap, FxHashSet};
use smallvec::SmallVec;
use std::fmt::Debug;
use taffy::{
- geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
- style::AvailableSpace as TaffyAvailableSpace,
- tree::NodeId,
- Taffy,
+ AvailableSpace as TaffyAvailableSpace, NodeId, Point as TaffyPoint, Rect as TaffyRect,
+ Size as TaffySize, TaffyTree, TraversePartialTree,
};
pub struct TaffyLayoutEngine {
- taffy: Taffy,
- children_to_parents: FxHashMap<LayoutId, LayoutId>,
+ tree: TaffyTree,
+ styles: FxHashMap<LayoutId, Style>,
absolute_layout_bounds: FxHashMap<LayoutId, Bounds<Pixels>>,
computed_layouts: FxHashSet<LayoutId>,
nodes_to_measure: FxHashMap<
@@ -34,8 +32,8 @@ static EXPECT_MESSAGE: &str = "we should avoid taffy layout errors by constructi
impl TaffyLayoutEngine {
pub fn new() -> Self {
TaffyLayoutEngine {
- taffy: Taffy::new(),
- children_to_parents: FxHashMap::default(),
+ tree: TaffyTree::new(),
+ styles: FxHashMap::default(),
absolute_layout_bounds: FxHashMap::default(),
computed_layouts: FxHashSet::default(),
nodes_to_measure: FxHashMap::default(),
@@ -43,11 +41,15 @@ impl TaffyLayoutEngine {
}
pub fn clear(&mut self) {
- self.taffy.clear();
- self.children_to_parents.clear();
+ self.tree.clear();
self.absolute_layout_bounds.clear();
self.computed_layouts.clear();
self.nodes_to_measure.clear();
+ self.styles.clear();
+ }
+
+ pub fn requested_style(&self, layout_id: LayoutId) -> Option<&Style> {
+ self.styles.get(&layout_id)
}
pub fn request_layout(
@@ -56,21 +58,21 @@ impl TaffyLayoutEngine {
rem_size: Pixels,
children: &[LayoutId],
) -> LayoutId {
- let style = style.to_taffy(rem_size);
- if children.is_empty() {
- self.taffy.new_leaf(style).expect(EXPECT_MESSAGE).into()
+ let taffy_style = style.to_taffy(rem_size);
+ let layout_id = if children.is_empty() {
+ self.tree
+ .new_leaf(taffy_style)
+ .expect(EXPECT_MESSAGE)
+ .into()
} else {
- let parent_id = self
- .taffy
+ self.tree
// This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId.
- .new_with_children(style, unsafe { std::mem::transmute(children) })
+ .new_with_children(taffy_style, unsafe { std::mem::transmute(children) })
.expect(EXPECT_MESSAGE)
- .into();
- for child_id in children {
- self.children_to_parents.insert(*child_id, parent_id);
- }
- parent_id
- }
+ .into()
+ };
+ self.styles.insert(layout_id, style.clone());
+ layout_id
}
pub fn request_measured_layout(
@@ -80,14 +82,16 @@ impl TaffyLayoutEngine {
measure: impl FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
+ 'static,
) -> LayoutId {
- let style = style.to_taffy(rem_size);
+ let style = style.clone();
+ let taffy_style = style.to_taffy(rem_size);
let layout_id = self
- .taffy
- .new_leaf_with_context(style, ())
+ .tree
+ .new_leaf_with_context(taffy_style, ())
.expect(EXPECT_MESSAGE)
.into();
self.nodes_to_measure.insert(layout_id, Box::new(measure));
+ self.styles.insert(layout_id, style.clone());
layout_id
}
@@ -96,7 +100,7 @@ impl TaffyLayoutEngine {
fn count_all_children(&self, parent: LayoutId) -> anyhow::Result<u32> {
let mut count = 0;
- for child in self.taffy.children(parent.0)? {
+ for child in self.tree.children(parent.0)? {
// Count this child.
count += 1;
@@ -112,12 +116,12 @@ impl TaffyLayoutEngine {
fn max_depth(&self, depth: u32, parent: LayoutId) -> anyhow::Result<u32> {
println!(
"{parent:?} at depth {depth} has {} children",
- self.taffy.child_count(parent.0)?
+ self.tree.child_count(parent.0)
);
let mut max_child_depth = 0;
- for child in self.taffy.children(parent.0)? {
+ for child in self.tree.children(parent.0)? {
max_child_depth = std::cmp::max(max_child_depth, self.max_depth(0, LayoutId(child))?);
}
@@ -129,7 +133,7 @@ impl TaffyLayoutEngine {
fn get_edges(&self, parent: LayoutId) -> anyhow::Result<Vec<(LayoutId, LayoutId)>> {
let mut edges = Vec::new();
- for child in self.taffy.children(parent.0)? {
+ for child in self.tree.children(parent.0)? {
edges.push((parent, LayoutId(child)));
edges.extend(self.get_edges(LayoutId(child))?);
@@ -162,7 +166,7 @@ impl TaffyLayoutEngine {
while let Some(id) = stack.pop() {
self.absolute_layout_bounds.remove(&id);
stack.extend(
- self.taffy
+ self.tree
.children(id.into())
.expect(EXPECT_MESSAGE)
.into_iter()
@@ -172,7 +176,7 @@ impl TaffyLayoutEngine {
}
// let started_at = std::time::Instant::now();
- self.taffy
+ self.tree
.compute_layout_with_measure(
id.into(),
available_space.into(),
@@ -199,14 +203,14 @@ impl TaffyLayoutEngine {
return layout;
}
- let layout = self.taffy.layout(id.into()).expect(EXPECT_MESSAGE);
+ let layout = self.tree.layout(id.into()).expect(EXPECT_MESSAGE);
let mut bounds = Bounds {
origin: layout.location.into(),
size: layout.size.into(),
};
- if let Some(parent_id) = self.children_to_parents.get(&id).copied() {
- let parent_bounds = self.layout_bounds(parent_id);
+ if let Some(parent_id) = self.tree.parent(id.0) {
+ let parent_bounds = self.layout_bounds(parent_id.into());
bounds.origin += parent_bounds.origin;
}
self.absolute_layout_bounds.insert(id, bounds);
@@ -9,11 +9,11 @@ pub use line_layout::*;
pub use line_wrapper::*;
use crate::{
- px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size,
- UnderlineStyle,
+ px, Bounds, DevicePixels, EntityId, Hsla, Pixels, PlatformTextSystem, Point, Result,
+ SharedString, Size, UnderlineStyle,
};
use anyhow::anyhow;
-use collections::FxHashMap;
+use collections::{FxHashMap, FxHashSet};
use core::fmt;
use itertools::Itertools;
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
@@ -189,6 +189,10 @@ impl TextSystem {
}
}
+ pub fn with_view<R>(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R {
+ self.line_layout_cache.with_view(view_id, f)
+ }
+
pub fn layout_line(
&self,
text: &str,
@@ -363,8 +367,8 @@ impl TextSystem {
Ok(lines)
}
- pub fn start_frame(&self) {
- self.line_layout_cache.start_frame()
+ pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
+ self.line_layout_cache.finish_frame(reused_views)
}
pub fn line_wrapper(self: &Arc<Self>, font: Font, font_size: Pixels) -> LineWrapperHandle {
@@ -1,5 +1,5 @@
-use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
-use collections::FxHashMap;
+use crate::{px, EntityId, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
+use collections::{FxHashMap, FxHashSet};
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
use smallvec::SmallVec;
use std::{
@@ -236,6 +236,7 @@ impl WrappedLineLayout {
}
pub(crate) struct LineLayoutCache {
+ view_stack: Mutex<Vec<EntityId>>,
previous_frame: Mutex<FxHashMap<CacheKey, Arc<LineLayout>>>,
current_frame: RwLock<FxHashMap<CacheKey, Arc<LineLayout>>>,
previous_frame_wrapped: Mutex<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
@@ -246,6 +247,7 @@ pub(crate) struct LineLayoutCache {
impl LineLayoutCache {
pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
Self {
+ view_stack: Mutex::default(),
previous_frame: Mutex::default(),
current_frame: RwLock::default(),
previous_frame_wrapped: Mutex::default(),
@@ -254,11 +256,43 @@ impl LineLayoutCache {
}
}
- pub fn start_frame(&self) {
+ pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
+ debug_assert_eq!(self.view_stack.lock().len(), 0);
+
let mut prev_frame = self.previous_frame.lock();
let mut curr_frame = self.current_frame.write();
+ for (key, layout) in prev_frame.drain() {
+ if key
+ .parent_view_id
+ .map_or(false, |view_id| reused_views.contains(&view_id))
+ {
+ curr_frame.insert(key, layout);
+ }
+ }
std::mem::swap(&mut *prev_frame, &mut *curr_frame);
- curr_frame.clear();
+
+ let mut prev_frame_wrapped = self.previous_frame_wrapped.lock();
+ let mut curr_frame_wrapped = self.current_frame_wrapped.write();
+ for (key, layout) in prev_frame_wrapped.drain() {
+ if key
+ .parent_view_id
+ .map_or(false, |view_id| reused_views.contains(&view_id))
+ {
+ curr_frame_wrapped.insert(key, layout);
+ }
+ }
+ std::mem::swap(&mut *prev_frame_wrapped, &mut *curr_frame_wrapped);
+ }
+
+ pub fn with_view<R>(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R {
+ self.view_stack.lock().push(view_id);
+ let result = f();
+ self.view_stack.lock().pop();
+ result
+ }
+
+ fn parent_view_id(&self) -> Option<EntityId> {
+ self.view_stack.lock().last().copied()
}
pub fn layout_wrapped_line(
@@ -273,6 +307,7 @@ impl LineLayoutCache {
font_size,
runs,
wrap_width,
+ parent_view_id: self.parent_view_id(),
} as &dyn AsCacheKeyRef;
let current_frame = self.current_frame_wrapped.upgradable_read();
@@ -301,6 +336,7 @@ impl LineLayoutCache {
font_size,
runs: SmallVec::from(runs),
wrap_width,
+ parent_view_id: self.parent_view_id(),
};
current_frame.insert(key, layout.clone());
layout
@@ -313,6 +349,7 @@ impl LineLayoutCache {
font_size,
runs,
wrap_width: None,
+ parent_view_id: self.parent_view_id(),
} as &dyn AsCacheKeyRef;
let current_frame = self.current_frame.upgradable_read();
@@ -331,6 +368,7 @@ impl LineLayoutCache {
font_size,
runs: SmallVec::from(runs),
wrap_width: None,
+ parent_view_id: self.parent_view_id(),
};
current_frame.insert(key, layout.clone());
layout
@@ -348,12 +386,13 @@ trait AsCacheKeyRef {
fn as_cache_key_ref(&self) -> CacheKeyRef;
}
-#[derive(Eq)]
+#[derive(Debug, Eq)]
struct CacheKey {
text: String,
font_size: Pixels,
runs: SmallVec<[FontRun; 1]>,
wrap_width: Option<Pixels>,
+ parent_view_id: Option<EntityId>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
@@ -362,6 +401,7 @@ struct CacheKeyRef<'a> {
font_size: Pixels,
runs: &'a [FontRun],
wrap_width: Option<Pixels>,
+ parent_view_id: Option<EntityId>,
}
impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
@@ -385,6 +425,7 @@ impl AsCacheKeyRef for CacheKey {
font_size: self.font_size,
runs: self.runs.as_slice(),
wrap_width: self.wrap_width,
+ parent_view_id: self.parent_view_id,
}
}
}
@@ -1,8 +1,8 @@
use crate::{
seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
- Bounds, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement,
- LayoutId, Model, Pixels, Point, Render, Size, ViewContext, VisualContext, WeakModel,
- WindowContext,
+ Bounds, ContentMask, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView,
+ IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style, TextStyle,
+ ViewContext, VisualContext, WeakModel, WindowContext,
};
use anyhow::{Context, Result};
use std::{
@@ -17,6 +17,19 @@ pub struct View<V> {
impl<V> Sealed for View<V> {}
+pub struct AnyViewState {
+ root_style: Style,
+ cache_key: Option<ViewCacheKey>,
+ element: Option<AnyElement>,
+}
+
+struct ViewCacheKey {
+ bounds: Bounds<Pixels>,
+ stacking_order: StackingOrder,
+ content_mask: ContentMask<Pixels>,
+ text_style: TextStyle,
+}
+
impl<V: 'static> Entity<V> for View<V> {
type Weak = WeakView<V>;
@@ -76,13 +89,15 @@ impl<V: Render> Element for View<V> {
_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
- let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
- let layout_id = element.request_layout(cx);
- (layout_id, Some(element))
+ cx.with_view_id(self.entity_id(), |cx| {
+ let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
+ let layout_id = element.request_layout(cx);
+ (layout_id, Some(element))
+ })
}
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
- element.take().unwrap().paint(cx);
+ cx.paint_view(self.entity_id(), |cx| element.take().unwrap().paint(cx));
}
}
@@ -173,16 +188,20 @@ impl<V> Eq for WeakView<V> {}
#[derive(Clone, Debug)]
pub struct AnyView {
model: AnyModel,
- layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
- paint: fn(&AnyView, &mut AnyElement, &mut WindowContext),
+ request_layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
+ cache: bool,
}
impl AnyView {
+ pub fn cached(mut self) -> Self {
+ self.cache = true;
+ self
+ }
+
pub fn downgrade(&self) -> AnyWeakView {
AnyWeakView {
model: self.model.downgrade(),
- layout: self.layout,
- paint: self.paint,
+ layout: self.request_layout,
}
}
@@ -191,8 +210,8 @@ impl AnyView {
Ok(model) => Ok(View { model }),
Err(model) => Err(Self {
model,
- layout: self.layout,
- paint: self.paint,
+ request_layout: self.request_layout,
+ cache: self.cache,
}),
}
}
@@ -211,10 +230,12 @@ impl AnyView {
available_space: Size<AvailableSpace>,
cx: &mut WindowContext,
) {
- cx.with_absolute_element_offset(origin, |cx| {
- let (layout_id, mut rendered_element) = (self.layout)(self, cx);
- cx.compute_layout(layout_id, available_space);
- (self.paint)(self, &mut rendered_element, cx);
+ cx.paint_view(self.entity_id(), |cx| {
+ cx.with_absolute_element_offset(origin, |cx| {
+ let (layout_id, mut rendered_element) = (self.request_layout)(self, cx);
+ cx.compute_layout(layout_id, available_space);
+ rendered_element.paint(cx)
+ });
})
}
}
@@ -223,30 +244,72 @@ impl<V: Render> From<View<V>> for AnyView {
fn from(value: View<V>) -> Self {
AnyView {
model: value.model.into_any(),
- layout: any_view::layout::<V>,
- paint: any_view::paint,
+ request_layout: any_view::request_layout::<V>,
+ cache: false,
}
}
}
impl Element for AnyView {
- type State = Option<AnyElement>;
+ type State = AnyViewState;
fn request_layout(
&mut self,
- _state: Option<Self::State>,
+ state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
- let (layout_id, state) = (self.layout)(self, cx);
- (layout_id, Some(state))
+ cx.with_view_id(self.entity_id(), |cx| {
+ if self.cache {
+ if let Some(state) = state {
+ let layout_id = cx.request_layout(&state.root_style, None);
+ return (layout_id, state);
+ }
+ }
+
+ let (layout_id, element) = (self.request_layout)(self, cx);
+ let root_style = cx.layout_style(layout_id).unwrap().clone();
+ let state = AnyViewState {
+ root_style,
+ cache_key: None,
+ element: Some(element),
+ };
+ (layout_id, state)
+ })
}
- fn paint(&mut self, _: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
- debug_assert!(
- state.is_some(),
- "state is None. Did you include an AnyView twice in the tree?"
- );
- (self.paint)(self, state.as_mut().unwrap(), cx)
+ fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+ cx.paint_view(self.entity_id(), |cx| {
+ if !self.cache {
+ state.element.take().unwrap().paint(cx);
+ return;
+ }
+
+ if let Some(cache_key) = state.cache_key.as_mut() {
+ if cache_key.bounds == bounds
+ && cache_key.content_mask == cx.content_mask()
+ && cache_key.stacking_order == *cx.stacking_order()
+ && cache_key.text_style == cx.text_style()
+ && !cx.window.dirty_views.contains(&self.entity_id())
+ && !cx.window.refreshing
+ {
+ cx.reuse_view();
+ return;
+ }
+ }
+
+ let mut element = state
+ .element
+ .take()
+ .unwrap_or_else(|| (self.request_layout)(self, cx).1);
+ element.draw(bounds.origin, bounds.size.into(), cx);
+
+ state.cache_key = Some(ViewCacheKey {
+ bounds,
+ stacking_order: cx.stacking_order().clone(),
+ content_mask: cx.content_mask(),
+ text_style: cx.text_style(),
+ });
+ })
}
}
@@ -277,7 +340,6 @@ impl IntoElement for AnyView {
pub struct AnyWeakView {
model: AnyWeakModel,
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
- paint: fn(&AnyView, &mut AnyElement, &mut WindowContext),
}
impl AnyWeakView {
@@ -285,8 +347,8 @@ impl AnyWeakView {
let model = self.model.upgrade()?;
Some(AnyView {
model,
- layout: self.layout,
- paint: self.paint,
+ request_layout: self.layout,
+ cache: false,
})
}
}
@@ -295,8 +357,7 @@ impl<V: 'static + Render> From<WeakView<V>> for AnyWeakView {
fn from(view: WeakView<V>) -> Self {
Self {
model: view.model.into(),
- layout: any_view::layout::<V>,
- paint: any_view::paint,
+ layout: any_view::request_layout::<V>,
}
}
}
@@ -318,7 +379,7 @@ impl std::fmt::Debug for AnyWeakView {
mod any_view {
use crate::{AnyElement, AnyView, IntoElement, LayoutId, Render, WindowContext};
- pub(crate) fn layout<V: 'static + Render>(
+ pub(crate) fn request_layout<V: 'static + Render>(
view: &AnyView,
cx: &mut WindowContext,
) -> (LayoutId, AnyElement) {
@@ -327,8 +388,4 @@ mod any_view {
let layout_id = element.request_layout(cx);
(layout_id, element)
}
-
- pub(crate) fn paint(_view: &AnyView, element: &mut AnyElement, cx: &mut WindowContext) {
- element.paint(cx);
- }
}
@@ -1,20 +1,20 @@
#![deny(missing_docs)]
use crate::{
- px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, ArenaBox, ArenaRef,
- AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
- DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
- Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
+ px, size, transparent_black, Action, AnyDrag, 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, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId,
Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent,
Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
- RenderSvgParams, ScaledPixels, Scene, SceneBuilder, Shadow, SharedString, Size, Style,
- SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View,
- VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
+ RenderSvgParams, ScaledPixels, Scene, Shadow, SharedString, Size, Style, SubscriberSet,
+ Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext,
+ WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
};
use anyhow::{anyhow, Context as _, Result};
-use collections::FxHashMap;
+use collections::{FxHashMap, FxHashSet};
use derive_more::{Deref, DerefMut};
use futures::{
channel::{mpsc, oneshot},
@@ -99,7 +99,7 @@ impl DispatchPhase {
}
type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
-type AnyMouseListener = ArenaBox<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
type AnyWindowFocusListener = Box<dyn FnMut(&FocusEvent, &mut WindowContext) -> bool + 'static>;
struct FocusEvent {
@@ -266,19 +266,19 @@ pub struct Window {
pub(crate) element_id_stack: GlobalElementId,
pub(crate) rendered_frame: Frame,
pub(crate) next_frame: Frame,
- frame_arena: Arena,
+ pub(crate) dirty_views: FxHashSet<EntityId>,
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
focus_listeners: SubscriberSet<(), AnyWindowFocusListener>,
focus_lost_listeners: SubscriberSet<(), AnyObserver>,
default_prevented: bool,
mouse_position: Point<Pixels>,
modifiers: Modifiers,
- requested_cursor_style: Option<CursorStyle>,
scale_factor: f32,
bounds: WindowBounds,
bounds_observers: SubscriberSet<(), AnyObserver>,
active: bool,
pub(crate) dirty: bool,
+ pub(crate) refreshing: bool,
pub(crate) drawing: bool,
activation_observers: SubscriberSet<(), AnyObserver>,
pub(crate) focus: Option<FocusId>,
@@ -290,22 +290,33 @@ pub struct Window {
pub(crate) struct ElementStateBox {
inner: Box<dyn Any>,
+ parent_view_id: EntityId,
#[cfg(debug_assertions)]
type_name: &'static str,
}
+struct RequestedInputHandler {
+ view_id: EntityId,
+ handler: Option<Box<dyn PlatformInputHandler>>,
+}
+
pub(crate) struct Frame {
focus: Option<FocusId>,
window_active: bool,
pub(crate) element_states: FxHashMap<GlobalElementId, ElementStateBox>,
- mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, AnyMouseListener)>>,
+ mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, EntityId, AnyMouseListener)>>,
pub(crate) dispatch_tree: DispatchTree,
- pub(crate) scene_builder: SceneBuilder,
- pub(crate) depth_map: Vec<(StackingOrder, Bounds<Pixels>)>,
+ pub(crate) scene: Scene,
+ pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>,
pub(crate) z_index_stack: StackingOrder,
pub(crate) next_stacking_order_id: u32,
content_mask_stack: Vec<ContentMask<Pixels>>,
element_offset_stack: Vec<Point<Pixels>>,
+ requested_input_handler: Option<RequestedInputHandler>,
+ cursor_styles: FxHashMap<EntityId, CursorStyle>,
+ requested_cursor_style: Option<CursorStyle>,
+ pub(crate) view_stack: Vec<EntityId>,
+ pub(crate) reused_views: FxHashSet<EntityId>,
}
impl Frame {
@@ -316,12 +327,17 @@ impl Frame {
element_states: FxHashMap::default(),
mouse_listeners: FxHashMap::default(),
dispatch_tree,
- scene_builder: SceneBuilder::default(),
+ scene: Scene::default(),
z_index_stack: StackingOrder::default(),
next_stacking_order_id: 0,
- depth_map: Default::default(),
+ depth_map: Vec::new(),
content_mask_stack: Vec::new(),
element_offset_stack: Vec::new(),
+ requested_input_handler: None,
+ cursor_styles: FxHashMap::default(),
+ requested_cursor_style: None,
+ view_stack: Vec::new(),
+ reused_views: FxHashSet::default(),
}
}
@@ -331,6 +347,12 @@ impl Frame {
self.dispatch_tree.clear();
self.depth_map.clear();
self.next_stacking_order_id = 0;
+ self.reused_views.clear();
+ self.scene.clear();
+ self.requested_input_handler.take();
+ self.cursor_styles.clear();
+ self.requested_cursor_style.take();
+ debug_assert_eq!(self.view_stack.len(), 0);
}
fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
@@ -338,6 +360,42 @@ impl Frame {
.map(|focus_id| self.dispatch_tree.focus_path(focus_id))
.unwrap_or_default()
}
+
+ fn finish(&mut self, prev_frame: &mut Self) {
+ // Reuse mouse listeners that didn't change since the last frame.
+ for (type_id, listeners) in &mut prev_frame.mouse_listeners {
+ let next_listeners = self.mouse_listeners.entry(*type_id).or_default();
+ for (order, view_id, listener) in listeners.drain(..) {
+ if self.reused_views.contains(&view_id) {
+ next_listeners.push((order, view_id, listener));
+ }
+ }
+ }
+
+ // Reuse entries in the depth map that didn't change since the last frame.
+ for (order, view_id, bounds) in prev_frame.depth_map.drain(..) {
+ if self.reused_views.contains(&view_id) {
+ match self
+ .depth_map
+ .binary_search_by(|(level, _, _)| order.cmp(level))
+ {
+ Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)),
+ }
+ }
+ }
+
+ // Retain element states for views that didn't change since the last frame.
+ for (element_id, state) in prev_frame.element_states.drain() {
+ if self.reused_views.contains(&state.parent_view_id) {
+ self.element_states.entry(element_id).or_insert(state);
+ }
+ }
+
+ // Reuse geometry that didn't change since the last frame.
+ self.scene
+ .reuse_views(&self.reused_views, &mut prev_frame.scene);
+ self.scene.finish();
+ }
}
impl Window {
@@ -346,14 +404,7 @@ impl Window {
options: WindowOptions,
cx: &mut AppContext,
) -> Self {
- let platform_window = cx.platform.open_window(
- handle,
- options,
- Box::new({
- let mut cx = cx.to_async();
- move || handle.update(&mut cx, |_, cx| cx.draw())
- }),
- );
+ let platform_window = cx.platform.open_window(handle, options);
let display_id = platform_window.display().id();
let sprite_atlas = platform_window.sprite_atlas();
let mouse_position = platform_window.mouse_position();
@@ -362,6 +413,12 @@ impl Window {
let scale_factor = platform_window.scale_factor();
let bounds = platform_window.bounds();
+ platform_window.on_request_frame(Box::new({
+ let mut cx = cx.to_async();
+ move || {
+ handle.update(&mut cx, |_, cx| cx.draw()).log_err();
+ }
+ }));
platform_window.on_resize(Box::new({
let mut cx = cx.to_async();
move |_, _| {
@@ -416,19 +473,19 @@ impl Window {
element_id_stack: GlobalElementId::default(),
rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
- frame_arena: Arena::new(1024 * 1024),
+ dirty_views: FxHashSet::default(),
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
focus_listeners: SubscriberSet::new(),
focus_lost_listeners: SubscriberSet::new(),
default_prevented: true,
mouse_position,
modifiers,
- requested_cursor_style: None,
scale_factor,
bounds,
bounds_observers: SubscriberSet::new(),
active: false,
dirty: false,
+ refreshing: false,
drawing: false,
activation_observers: SubscriberSet::new(),
focus: None,
@@ -484,8 +541,9 @@ impl<'a> WindowContext<'a> {
}
/// Mark the window as dirty, scheduling it to be redrawn on the next frame.
- pub fn notify(&mut self) {
+ pub fn refresh(&mut self) {
if !self.window.drawing {
+ self.window.refreshing = true;
self.window.dirty = true;
}
}
@@ -525,7 +583,7 @@ impl<'a> WindowContext<'a> {
self.window.focus_invalidated = true;
}
- self.notify();
+ self.refresh();
}
/// Remove focus from all elements within this context's window.
@@ -535,7 +593,7 @@ impl<'a> WindowContext<'a> {
}
self.window.focus = None;
- self.notify();
+ self.refresh();
}
/// Blur the window and don't allow anything in it to be focused again.
@@ -772,6 +830,14 @@ impl<'a> WindowContext<'a> {
.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.
@@ -801,7 +867,7 @@ impl<'a> WindowContext<'a> {
self.window.viewport_size = self.window.platform_window.content_size();
self.window.bounds = self.window.platform_window.bounds();
self.window.display_id = self.window.platform_window.display().id();
- self.notify();
+ self.refresh();
self.window
.bounds_observers
@@ -898,22 +964,22 @@ impl<'a> WindowContext<'a> {
&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();
- let handler = self
- .window
- .frame_arena
- .alloc(|| {
- move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_>| {
- handler(event.downcast_ref().unwrap(), phase, cx)
- }
- })
- .map(|handler| handler as _);
self.window
.next_frame
.mouse_listeners
.entry(TypeId::of::<Event>())
.or_default()
- .push((order, handler))
+ .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
@@ -926,21 +992,13 @@ impl<'a> WindowContext<'a> {
&mut self,
listener: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static,
) {
- let listener = self
- .window
- .frame_arena
- .alloc(|| {
- move |event: &dyn Any, phase, cx: &mut WindowContext<'_>| {
- if let Some(event) = event.downcast_ref::<Event>() {
- listener(event, phase, cx)
- }
+ 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)
}
- })
- .map(|handler| handler as _);
- self.window
- .next_frame
- .dispatch_tree
- .on_key_event(ArenaRef::from(listener));
+ },
+ ));
}
/// Register an action listener on the window for the next frame. The type of action
@@ -954,15 +1012,10 @@ impl<'a> WindowContext<'a> {
action_type: TypeId,
listener: impl Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static,
) {
- let listener = self
- .window
- .frame_arena
- .alloc(|| listener)
- .map(|handler| handler as _);
self.window
.next_frame
.dispatch_tree
- .on_action(action_type, ArenaRef::from(listener));
+ .on_action(action_type, Rc::new(listener));
}
/// Determine whether the given action is available along the dispatch path to the currently focused element.
@@ -994,15 +1047,18 @@ impl<'a> WindowContext<'a> {
/// Update the cursor style at the platform level.
pub fn set_cursor_style(&mut self, style: CursorStyle) {
- self.window.requested_cursor_style = Some(style)
+ let view_id = self.parent_view_id();
+ self.window.next_frame.cursor_styles.insert(view_id, style);
+ self.window.next_frame.requested_cursor_style = Some(style);
}
/// 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, bounds)),
+ match depth_map.binary_search_by(|(level, _, _)| stacking_order.cmp(level)) {
+ Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, view_id, bounds)),
}
}
@@ -1010,7 +1066,7 @@ impl<'a> WindowContext<'a> {
/// 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.
pub fn was_top_layer(&self, point: &Point<Pixels>, level: &StackingOrder) -> bool {
- for (opaque_level, bounds) in self.window.rendered_frame.depth_map.iter() {
+ for (opaque_level, _, bounds) in self.window.rendered_frame.depth_map.iter() {
if level >= opaque_level {
break;
}
@@ -1027,7 +1083,7 @@ impl<'a> WindowContext<'a> {
point: &Point<Pixels>,
level: &StackingOrder,
) -> bool {
- for (opaque_level, bounds) in self.window.rendered_frame.depth_map.iter() {
+ for (opaque_level, _, bounds) in self.window.rendered_frame.depth_map.iter() {
if level >= opaque_level {
break;
}
@@ -1056,14 +1112,17 @@ impl<'a> WindowContext<'a> {
) {
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_builder.insert(
+ 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),
@@ -1081,11 +1140,14 @@ impl<'a> WindowContext<'a> {
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_builder.insert(
+ 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),
@@ -1101,12 +1163,15 @@ impl<'a> WindowContext<'a> {
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_builder
+ .scene
.insert(&window.next_frame.z_index_stack, path.scale(scale_factor));
}
@@ -1128,10 +1193,14 @@ impl<'a> WindowContext<'a> {
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_builder.insert(
+ 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),
@@ -1181,10 +1250,13 @@ impl<'a> WindowContext<'a> {
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_builder.insert(
+ 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,
@@ -1231,11 +1303,14 @@ impl<'a> WindowContext<'a> {
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_builder.insert(
+ 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(),
@@ -1273,11 +1348,14 @@ impl<'a> WindowContext<'a> {
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_builder.insert(
+ 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,
@@ -1309,11 +1387,14 @@ impl<'a> WindowContext<'a> {
})?;
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_builder.insert(
+ 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,
@@ -1330,10 +1411,13 @@ impl<'a> WindowContext<'a> {
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_builder.insert(
+ 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,
@@ -1342,8 +1426,38 @@ impl<'a> WindowContext<'a> {
);
}
+ pub(crate) fn reuse_view(&mut self) {
+ let view_id = self.parent_view_id();
+ let grafted_view_ids = self
+ .window
+ .next_frame
+ .dispatch_tree
+ .graft(view_id, &mut self.window.rendered_frame.dispatch_tree);
+ for view_id in grafted_view_ids {
+ assert!(self.window.next_frame.reused_views.insert(view_id));
+
+ // Reuse the previous input handler if it was associated with one of
+ // the views grafted from the tree in the previous frame.
+ if self
+ .window
+ .rendered_frame
+ .requested_input_handler
+ .as_ref()
+ .map_or(false, |requested| requested.view_id == view_id)
+ {
+ self.window.next_frame.requested_input_handler =
+ self.window.rendered_frame.requested_input_handler.take();
+ }
+
+ if let Some(style) = self.window.rendered_frame.cursor_styles.remove(&view_id) {
+ self.window.next_frame.cursor_styles.insert(view_id, style);
+ self.window.next_frame.requested_cursor_style = Some(style);
+ }
+ }
+ }
+
/// Draw pixels to the display for this window based on the contents of its scene.
- pub(crate) fn draw(&mut self) -> Scene {
+ pub(crate) fn draw(&mut self) {
self.window.dirty = false;
self.window.drawing = true;
@@ -1352,30 +1466,23 @@ impl<'a> WindowContext<'a> {
self.window.focus_invalidated = false;
}
- self.text_system().start_frame();
- self.window.platform_window.clear_input_handler();
- self.window.layout_engine.as_mut().unwrap().clear();
- self.window.next_frame.clear();
- self.window.frame_arena.clear();
+ 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 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() {
- let listener = cx
- .window
- .frame_arena
- .alloc(|| {
- move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
- action_listener(action, phase, cx)
- }
- })
- .map(|listener| listener as _);
- cx.window
- .next_frame
- .dispatch_tree
- .on_action(*action_type, ArenaRef::from(listener))
+ 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)
+ }),
+ )
}
}
@@ -1399,6 +1506,7 @@ impl<'a> WindowContext<'a> {
.draw(active_tooltip.cursor_offset, available_space, cx);
});
}
+ self.window.dirty_views.clear();
self.window
.next_frame
@@ -1411,17 +1519,10 @@ impl<'a> WindowContext<'a> {
self.window.next_frame.window_active = self.window.active;
self.window.root_view = Some(root_view);
- let previous_focus_path = self.window.rendered_frame.focus_path();
- let previous_window_active = self.window.rendered_frame.window_active;
- mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame);
- let current_focus_path = self.window.rendered_frame.focus_path();
- let current_window_active = self.window.rendered_frame.window_active;
-
- let scene = self.window.rendered_frame.scene_builder.build();
-
// Set the cursor only if we're the active window.
let cursor_style = self
.window
+ .next_frame
.requested_cursor_style
.take()
.unwrap_or(CursorStyle::Arrow);
@@ -1429,6 +1530,28 @@ impl<'a> WindowContext<'a> {
self.platform.set_cursor_style(cursor_style);
}
+ // Register requested input handler with the platform window.
+ if let Some(requested_input) = self.window.next_frame.requested_input_handler.as_mut() {
+ if let Some(handler) = requested_input.handler.take() {
+ self.window.platform_window.set_input_handler(handler);
+ }
+ }
+
+ self.window.layout_engine.as_mut().unwrap().clear();
+ self.text_system()
+ .finish_frame(&self.window.next_frame.reused_views);
+ self.window
+ .next_frame
+ .finish(&mut self.window.rendered_frame);
+ ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear());
+
+ let previous_focus_path = self.window.rendered_frame.focus_path();
+ let previous_window_active = self.window.rendered_frame.window_active;
+ mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame);
+ self.window.next_frame.clear();
+ let current_focus_path = self.window.rendered_frame.focus_path();
+ let current_window_active = self.window.rendered_frame.window_active;
+
if previous_focus_path != current_focus_path
|| previous_window_active != current_window_active
{
@@ -1457,10 +1580,11 @@ impl<'a> WindowContext<'a> {
.retain(&(), |listener| listener(&event, self));
}
+ self.window
+ .platform_window
+ .draw(&self.window.rendered_frame.scene);
+ self.window.refreshing = false;
self.window.drawing = false;
- ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear());
-
- scene
}
/// Dispatch a mouse or keyboard event on the window.
@@ -1564,11 +1688,11 @@ impl<'a> WindowContext<'a> {
.remove(&event.type_id())
{
// Because handlers may add other handlers, we sort every time.
- handlers.sort_by(|(a, _), (b, _)| a.cmp(b));
+ handlers.sort_by(|(a, _, _), (b, _, _)| a.cmp(b));
// 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 {
+ for (_, _, handler) in &mut handlers {
handler(event, DispatchPhase::Capture, self);
if !self.app.propagate_event {
break;
@@ -1577,7 +1701,7 @@ 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() {
+ for (_, _, handler) in handlers.iter_mut().rev() {
handler(event, DispatchPhase::Bubble, self);
if !self.app.propagate_event {
break;
@@ -1595,12 +1719,12 @@ impl<'a> WindowContext<'a> {
if event.is::<MouseMoveEvent>() {
// If this was a mouse move event, redraw the window so that the
// active drag can follow the mouse cursor.
- self.notify();
+ self.refresh();
} else if event.is::<MouseUpEvent>() {
// If this was a mouse up event, cancel the active drag and redraw
// the window.
self.active_drag = None;
- self.notify();
+ self.refresh();
}
}
}
@@ -1867,13 +1991,12 @@ impl<'a> WindowContext<'a> {
f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
) -> R {
let window = &mut self.window;
- window.next_frame.dispatch_tree.push_node(context.clone());
- if let Some(focus_handle) = focus_handle.as_ref() {
- window
- .next_frame
- .dispatch_tree
- .make_focusable(focus_handle.id);
- }
+ 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();
@@ -1881,6 +2004,145 @@ impl<'a> WindowContext<'a> {
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
+ }
+ })
+ }
+
+ /// Update or initialize 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!(
+ "invalid element state type for id, requested_type {:?}, actual type: {:?}",
+ std::any::type_name::<S>(),
+ type_name
+ )
+ }
+
+ #[cfg(not(debug_assertions))]
+ {
+ anyhow!(
+ "invalid element state type for id, requested_type {:?}",
+ std::any::type_name::<S>(),
+ )
+ }
+ })
+ .unwrap();
+
+ // Actual: Option<AnyElement> <- View
+ // Requested: () <- AnyElemet
+ 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
+ }
+ })
+ }
+
+ fn parent_view_id(&self) -> EntityId {
+ *self
+ .window
+ .next_frame
+ .view_stack
+ .last()
+ .expect("a view should always be on the stack while drawing")
+ }
+
/// Set 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.
@@ -1892,9 +2154,11 @@ impl<'a> WindowContext<'a> {
input_handler: impl PlatformInputHandler,
) {
if focus_handle.is_focused(self) {
- self.window
- .platform_window
- .set_input_handler(Box::new(input_handler));
+ let view_id = self.parent_view_id();
+ self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
+ view_id,
+ handler: Some(Box::new(input_handler)),
+ })
}
}
@@ -2040,7 +2304,7 @@ impl VisualContext for WindowContext<'_> {
{
let view = self.new_view(build_view);
self.window.root_view = Some(view.clone().into());
- self.notify();
+ self.refresh();
view
}
@@ -2223,98 +2487,6 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
.unwrap_or_default()
}
- /// Update or initialize 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.
- 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,
-
- #[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!(
- "invalid element state type for id, requested_type {:?}, actual type: {:?}",
- std::any::type_name::<S>(),
- type_name
- )
- }
-
- #[cfg(not(debug_assertions))]
- {
- anyhow!(
- "invalid element state type for id, requested_type {:?}",
- std::any::type_name::<S>(),
- )
- }
- })
- .unwrap();
-
- // Actual: Option<AnyElement> <- View
- // Requested: () <- AnyElemet
- 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,
-
- #[cfg(debug_assertions)]
- type_name
- });
- result
- } else {
- let (result, state) = f(None, cx);
- cx.window_mut()
- .next_frame
- .element_states
- .insert(global_id,
- ElementStateBox {
- inner: Box::new(Some(state)),
-
- #[cfg(debug_assertions)]
- type_name: std::any::type_name::<S>()
- }
-
- );
- result
- }
- })
- }
-
/// Obtain the current content mask.
fn content_mask(&self) -> ContentMask<Pixels> {
self.window()
@@ -598,7 +598,7 @@ impl TerminalElement {
this.update(cx, |term, _| term.try_modifiers_change(&event.modifiers));
if handled {
- cx.notify();
+ cx.refresh();
}
}
});
@@ -55,7 +55,7 @@ impl<M: ManagedView> PopoverMenu<M> {
}
}
*menu2.borrow_mut() = None;
- cx.notify();
+ cx.refresh();
})
.detach();
cx.focus_view(&new_menu);
@@ -154,7 +154,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
}
}
*menu2.borrow_mut() = None;
- cx.notify();
+ cx.refresh();
})
.detach();
cx.focus_view(&new_menu);
@@ -167,7 +167,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
} else {
cx.mouse_position()
};
- cx.notify();
+ cx.refresh();
}
});
}
@@ -583,7 +583,7 @@ impl Render for Dock {
Axis::Horizontal => this.min_w(size).h_full(),
Axis::Vertical => this.min_h(size).w_full(),
})
- .child(entry.panel.to_any()),
+ .child(entry.panel.to_any().cached()),
)
.child(handle)
} else {
@@ -3,8 +3,8 @@ use anyhow::{anyhow, Result};
use call::{ActiveCall, ParticipantLocation};
use collections::HashMap;
use gpui::{
- point, size, AnyWeakView, Axis, Bounds, Entity as _, IntoElement, Model, Pixels, Point, View,
- ViewContext,
+ point, size, AnyView, AnyWeakView, Axis, Bounds, Entity as _, IntoElement, Model, Pixels,
+ Point, View, ViewContext,
};
use parking_lot::Mutex;
use project::Project;
@@ -244,7 +244,7 @@ impl Member {
.relative()
.flex_1()
.size_full()
- .child(pane.clone())
+ .child(AnyView::from(pane.clone()).cached())
.when_some(leader_border, |this, color| {
this.child(
div()
@@ -701,7 +701,7 @@ mod element {
workspace
.update(cx, |this, cx| this.schedule_serialize(cx))
.log_err();
- cx.notify();
+ cx.refresh();
}
fn push_handle(
@@ -757,7 +757,7 @@ mod element {
workspace
.update(cx, |this, cx| this.schedule_serialize(cx))
.log_err();
- cx.notify();
+ cx.refresh();
}
}
}