Introduce view caching (#4019)

<img width="996" alt="image"
src="https://github.com/zed-industries/zed/assets/482957/a5683382-6ff1-4558-9e9b-ebc17bc00289">

TODO: 
- [x] Fix rename editor not updating

Release Notes:

- Improved rendering performance when editing.
This commit is contained in:
Antonio Scandurra
2024-01-15 13:26:08 +01:00
committed by GitHub
28 changed files with 1122 additions and 664 deletions
Generated
+4 -3
View File
@@ -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",
]
+1 -1
View File
@@ -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())))
+132 -121
View File
@@ -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);
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)
}
};
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]
+1 -1
View File
@@ -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"
+7 -1
View File
@@ -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()
+12 -12
View File
@@ -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();
}
}
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -260,7 +260,7 @@ impl StateInner {
);
}
cx.notify();
cx.refresh();
}
fn logical_scroll_top(&self) -> ListOffset {
+2 -2
View File
@@ -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();
}
}
});
+3 -3
View File
@@ -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),
+103 -21
View File
@@ -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]
}
+3 -4
View File
@@ -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>;
+2 -9
View File
@@ -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(
+20 -16
View File
@@ -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 -3
View File
@@ -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,
+7 -5
View File
@@ -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()
+224 -151
View File
@@ -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,152 +11,58 @@ pub(crate) type PointF = Point<f32>;
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
pub type LayerId = u32;
pub type DrawOrder = u32;
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(C)]
pub struct ViewId {
low_bits: u32,
high_bits: u32,
}
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,
}
}
}
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()
}
}
#[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>,
}
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];
}
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);
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);
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);
for surface in &mut self.surfaces {
surface.order = orders[surface.order as usize];
}
self.surfaces.sort_by_key(|surface| surface.order);
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 fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) {
let primitive = primitive.into();
let clipped_bounds = primitive
.bounds()
.intersect(&primitive.content_mask().bounds);
if clipped_bounds.size.width <= ScaledPixels(0.)
|| clipped_bounds.size.height <= ScaledPixels(0.)
{
return;
}
let layer_id = self.layer_id_for_order(order);
match primitive {
Primitive::Shadow(mut shadow) => {
shadow.order = layer_id;
self.shadows.push(shadow);
}
Primitive::Quad(mut quad) => {
quad.order = layer_id;
self.quads.push(quad);
}
Primitive::Path(mut path) => {
path.order = layer_id;
path.id = PathId(self.paths.len());
self.paths.push(path);
}
Primitive::Underline(mut underline) => {
underline.order = layer_id;
self.underlines.push(underline);
}
Primitive::MonochromeSprite(mut sprite) => {
sprite.order = layer_id;
self.monochrome_sprites.push(sprite);
}
Primitive::PolychromeSprite(mut sprite) => {
sprite.order = layer_id;
self.polychrome_sprites.push(sprite);
}
Primitive::Surface(mut surface) => {
surface.order = 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) {
*layer_id
} else {
let next_id = self.layers_by_order.len() as LayerId;
self.layers_by_order.insert(order.clone(), next_id);
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>,
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>,
}
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();
}
pub fn paths(&self) -> &[Path<ScaledPixels>] {
&self.paths
}
@@ -186,6 +92,155 @@ impl Scene {
surfaces_iter: self.surfaces.iter().peekable(),
}
}
pub fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) {
let primitive = primitive.into();
let clipped_bounds = primitive
.bounds()
.intersect(&primitive.content_mask().bounds);
if clipped_bounds.size.width <= ScaledPixels(0.)
|| clipped_bounds.size.height <= ScaledPixels(0.)
{
return;
}
let layer_id = self.layer_id_for_order(order);
match primitive {
Primitive::Shadow(mut shadow) => {
shadow.layer_id = layer_id;
self.shadows.push(shadow);
}
Primitive::Quad(mut quad) => {
quad.layer_id = layer_id;
self.quads.push(quad);
}
Primitive::Path(mut path) => {
path.layer_id = layer_id;
path.id = PathId(self.paths.len());
self.paths.push(path);
}
Primitive::Underline(mut underline) => {
underline.layer_id = layer_id;
self.underlines.push(underline);
}
Primitive::MonochromeSprite(mut sprite) => {
sprite.layer_id = layer_id;
self.monochrome_sprites.push(sprite);
}
Primitive::PolychromeSprite(mut sprite) => {
sprite.layer_id = layer_id;
self.polychrome_sprites.push(sprite);
}
Primitive::Surface(mut surface) => {
surface.layer_id = layer_id;
self.surfaces.push(surface);
}
}
}
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
}
}
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);
}
}
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 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);
}
}
struct BatchIterator<'a> {
@@ -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),
+1 -1
View File
@@ -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,
+40 -36
View File
@@ -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()
} else {
let parent_id = self
.taffy
// This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId.
.new_with_children(style, unsafe { std::mem::transmute(children) })
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();
for child_id in children {
self.children_to_parents.insert(*child_id, parent_id);
}
parent_id
}
.into()
} else {
self.tree
// This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId.
.new_with_children(taffy_style, unsafe { std::mem::transmute(children) })
.expect(EXPECT_MESSAGE)
.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 -5
View File
@@ -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 {
+46 -5
View File
@@ -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,
}
}
}
+96 -39
View File
@@ -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);
}
}
+396 -213
View File
@@ -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()
@@ -2554,8 +2726,19 @@ impl<'a, V: 'static> ViewContext<'a, V> {
/// Indicate that this view has changed, which will invoke any observers and also mark the window as dirty.
/// If this view or any of its ancestors are *cached*, notifying it will cause it or its ancestors to be redrawn.
pub fn notify(&mut self) {
for view_id in self
.window
.rendered_frame
.dispatch_tree
.view_path(self.view.entity_id())
{
if !self.window.dirty_views.insert(view_id) {
break;
}
}
if !self.window.drawing {
self.window_cx.notify();
self.window_cx.window.dirty = true;
self.window_cx.app.push_effect(Effect::Notify {
emitter: self.view.model.entity_id,
});
+1 -1
View File
@@ -598,7 +598,7 @@ impl TerminalElement {
this.update(cx, |term, _| term.try_modifiers_change(&event.modifiers));
if handled {
cx.notify();
cx.refresh();
}
}
});
+1 -1
View File
@@ -55,7 +55,7 @@ impl<M: ManagedView> PopoverMenu<M> {
}
}
*menu2.borrow_mut() = None;
cx.notify();
cx.refresh();
})
.detach();
cx.focus_view(&new_menu);
+2 -2
View File
@@ -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();
}
});
}
+1 -1
View File
@@ -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 {
+5 -5
View File
@@ -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();
}
}
}