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:
Generated
+4
-3
@@ -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())))
|
||||
|
||||
+132
-121
@@ -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]
|
||||
|
||||
@@ -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),
|
||||
|
||||
+103
-21
@@ -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()
|
||||
|
||||
+224
-151
@@ -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),
|
||||
|
||||
@@ -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
@@ -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,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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+96
-39
@@ -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
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user