Detailed changes
@@ -912,7 +912,7 @@ mod tests {
display_map::{BlockContext, TransformBlock},
DisplayPoint, GutterDimensions,
};
- use gpui::{px, Stateful, TestAppContext, VisualTestContext, WindowContext};
+ use gpui::{px, AvailableSpace, Stateful, TestAppContext, VisualTestContext};
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16, Unclipped};
use project::FakeFs;
use serde_json::json;
@@ -1049,67 +1049,66 @@ mod tests {
cx,
)
});
+ let editor = view.update(cx, |view, _| view.editor.clone());
view.next_notification(cx).await;
- view.update(cx, |view, cx| {
- assert_eq!(
- editor_blocks(&view.editor, cx),
- [
- (0, "path header block".into()),
- (2, "diagnostic header".into()),
- (15, "collapsed context".into()),
- (16, "diagnostic header".into()),
- (25, "collapsed context".into()),
- ]
- );
+ assert_eq!(
+ editor_blocks(&editor, cx),
+ [
+ (0, "path header block".into()),
+ (2, "diagnostic header".into()),
+ (15, "collapsed context".into()),
+ (16, "diagnostic header".into()),
+ (25, "collapsed context".into()),
+ ]
+ );
+ assert_eq!(
+ editor.update(cx, |editor, cx| editor.display_text(cx)),
+ concat!(
+ //
+ // main.rs
+ //
+ "\n", // filename
+ "\n", // padding
+ // diagnostic group 1
+ "\n", // primary message
+ "\n", // padding
+ " let x = vec![];\n",
+ " let y = vec![];\n",
+ "\n", // supporting diagnostic
+ " a(x);\n",
+ " b(y);\n",
+ "\n", // supporting diagnostic
+ " // comment 1\n",
+ " // comment 2\n",
+ " c(y);\n",
+ "\n", // supporting diagnostic
+ " d(x);\n",
+ "\n", // context ellipsis
+ // diagnostic group 2
+ "\n", // primary message
+ "\n", // padding
+ "fn main() {\n",
+ " let x = vec![];\n",
+ "\n", // supporting diagnostic
+ " let y = vec![];\n",
+ " a(x);\n",
+ "\n", // supporting diagnostic
+ " b(y);\n",
+ "\n", // context ellipsis
+ " c(y);\n",
+ " d(x);\n",
+ "\n", // supporting diagnostic
+ "}"
+ )
+ );
+
+ // Cursor is at the first diagnostic
+ editor.update(cx, |editor, cx| {
assert_eq!(
- view.editor.update(cx, |editor, cx| editor.display_text(cx)),
- concat!(
- //
- // main.rs
- //
- "\n", // filename
- "\n", // padding
- // diagnostic group 1
- "\n", // primary message
- "\n", // padding
- " let x = vec![];\n",
- " let y = vec![];\n",
- "\n", // supporting diagnostic
- " a(x);\n",
- " b(y);\n",
- "\n", // supporting diagnostic
- " // comment 1\n",
- " // comment 2\n",
- " c(y);\n",
- "\n", // supporting diagnostic
- " d(x);\n",
- "\n", // context ellipsis
- // diagnostic group 2
- "\n", // primary message
- "\n", // padding
- "fn main() {\n",
- " let x = vec![];\n",
- "\n", // supporting diagnostic
- " let y = vec![];\n",
- " a(x);\n",
- "\n", // supporting diagnostic
- " b(y);\n",
- "\n", // context ellipsis
- " c(y);\n",
- " d(x);\n",
- "\n", // supporting diagnostic
- "}"
- )
+ editor.selections.display_ranges(cx),
+ [DisplayPoint::new(12, 6)..DisplayPoint::new(12, 6)]
);
-
- // Cursor is at the first diagnostic
- view.editor.update(cx, |editor, cx| {
- assert_eq!(
- editor.selections.display_ranges(cx),
- [DisplayPoint::new(12, 6)..DisplayPoint::new(12, 6)]
- );
- });
});
// Diagnostics are added for another earlier path.
@@ -1138,78 +1137,77 @@ mod tests {
});
view.next_notification(cx).await;
- view.update(cx, |view, cx| {
- assert_eq!(
- editor_blocks(&view.editor, cx),
- [
- (0, "path header block".into()),
- (2, "diagnostic header".into()),
- (7, "path header block".into()),
- (9, "diagnostic header".into()),
- (22, "collapsed context".into()),
- (23, "diagnostic header".into()),
- (32, "collapsed context".into()),
- ]
- );
+ assert_eq!(
+ editor_blocks(&editor, cx),
+ [
+ (0, "path header block".into()),
+ (2, "diagnostic header".into()),
+ (7, "path header block".into()),
+ (9, "diagnostic header".into()),
+ (22, "collapsed context".into()),
+ (23, "diagnostic header".into()),
+ (32, "collapsed context".into()),
+ ]
+ );
+
+ assert_eq!(
+ editor.update(cx, |editor, cx| editor.display_text(cx)),
+ concat!(
+ //
+ // consts.rs
+ //
+ "\n", // filename
+ "\n", // padding
+ // diagnostic group 1
+ "\n", // primary message
+ "\n", // padding
+ "const a: i32 = 'a';\n",
+ "\n", // supporting diagnostic
+ "const b: i32 = c;\n",
+ //
+ // main.rs
+ //
+ "\n", // filename
+ "\n", // padding
+ // diagnostic group 1
+ "\n", // primary message
+ "\n", // padding
+ " let x = vec![];\n",
+ " let y = vec![];\n",
+ "\n", // supporting diagnostic
+ " a(x);\n",
+ " b(y);\n",
+ "\n", // supporting diagnostic
+ " // comment 1\n",
+ " // comment 2\n",
+ " c(y);\n",
+ "\n", // supporting diagnostic
+ " d(x);\n",
+ "\n", // collapsed context
+ // diagnostic group 2
+ "\n", // primary message
+ "\n", // filename
+ "fn main() {\n",
+ " let x = vec![];\n",
+ "\n", // supporting diagnostic
+ " let y = vec![];\n",
+ " a(x);\n",
+ "\n", // supporting diagnostic
+ " b(y);\n",
+ "\n", // context ellipsis
+ " c(y);\n",
+ " d(x);\n",
+ "\n", // supporting diagnostic
+ "}"
+ )
+ );
+
+ // Cursor keeps its position.
+ editor.update(cx, |editor, cx| {
assert_eq!(
- view.editor.update(cx, |editor, cx| editor.display_text(cx)),
- concat!(
- //
- // consts.rs
- //
- "\n", // filename
- "\n", // padding
- // diagnostic group 1
- "\n", // primary message
- "\n", // padding
- "const a: i32 = 'a';\n",
- "\n", // supporting diagnostic
- "const b: i32 = c;\n",
- //
- // main.rs
- //
- "\n", // filename
- "\n", // padding
- // diagnostic group 1
- "\n", // primary message
- "\n", // padding
- " let x = vec![];\n",
- " let y = vec![];\n",
- "\n", // supporting diagnostic
- " a(x);\n",
- " b(y);\n",
- "\n", // supporting diagnostic
- " // comment 1\n",
- " // comment 2\n",
- " c(y);\n",
- "\n", // supporting diagnostic
- " d(x);\n",
- "\n", // collapsed context
- // diagnostic group 2
- "\n", // primary message
- "\n", // filename
- "fn main() {\n",
- " let x = vec![];\n",
- "\n", // supporting diagnostic
- " let y = vec![];\n",
- " a(x);\n",
- "\n", // supporting diagnostic
- " b(y);\n",
- "\n", // context ellipsis
- " c(y);\n",
- " d(x);\n",
- "\n", // supporting diagnostic
- "}"
- )
+ editor.selections.display_ranges(cx),
+ [DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)]
);
-
- // Cursor keeps its position.
- view.editor.update(cx, |editor, cx| {
- assert_eq!(
- editor.selections.display_ranges(cx),
- [DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)]
- );
- });
});
// Diagnostics are added to the first path
@@ -1254,80 +1252,79 @@ mod tests {
});
view.next_notification(cx).await;
- view.update(cx, |view, cx| {
- assert_eq!(
- editor_blocks(&view.editor, cx),
- [
- (0, "path header block".into()),
- (2, "diagnostic header".into()),
- (7, "collapsed context".into()),
- (8, "diagnostic header".into()),
- (13, "path header block".into()),
- (15, "diagnostic header".into()),
- (28, "collapsed context".into()),
- (29, "diagnostic header".into()),
- (38, "collapsed context".into()),
- ]
- );
- assert_eq!(
- view.editor.update(cx, |editor, cx| editor.display_text(cx)),
- concat!(
- //
- // consts.rs
- //
- "\n", // filename
- "\n", // padding
- // diagnostic group 1
- "\n", // primary message
- "\n", // padding
- "const a: i32 = 'a';\n",
- "\n", // supporting diagnostic
- "const b: i32 = c;\n",
- "\n", // context ellipsis
- // diagnostic group 2
- "\n", // primary message
- "\n", // padding
- "const a: i32 = 'a';\n",
- "const b: i32 = c;\n",
- "\n", // supporting diagnostic
- //
- // main.rs
- //
- "\n", // filename
- "\n", // padding
- // diagnostic group 1
- "\n", // primary message
- "\n", // padding
- " let x = vec![];\n",
- " let y = vec![];\n",
- "\n", // supporting diagnostic
- " a(x);\n",
- " b(y);\n",
- "\n", // supporting diagnostic
- " // comment 1\n",
- " // comment 2\n",
- " c(y);\n",
- "\n", // supporting diagnostic
- " d(x);\n",
- "\n", // context ellipsis
- // diagnostic group 2
- "\n", // primary message
- "\n", // filename
- "fn main() {\n",
- " let x = vec![];\n",
- "\n", // supporting diagnostic
- " let y = vec![];\n",
- " a(x);\n",
- "\n", // supporting diagnostic
- " b(y);\n",
- "\n", // context ellipsis
- " c(y);\n",
- " d(x);\n",
- "\n", // supporting diagnostic
- "}"
- )
- );
- });
+ assert_eq!(
+ editor_blocks(&editor, cx),
+ [
+ (0, "path header block".into()),
+ (2, "diagnostic header".into()),
+ (7, "collapsed context".into()),
+ (8, "diagnostic header".into()),
+ (13, "path header block".into()),
+ (15, "diagnostic header".into()),
+ (28, "collapsed context".into()),
+ (29, "diagnostic header".into()),
+ (38, "collapsed context".into()),
+ ]
+ );
+
+ assert_eq!(
+ editor.update(cx, |editor, cx| editor.display_text(cx)),
+ concat!(
+ //
+ // consts.rs
+ //
+ "\n", // filename
+ "\n", // padding
+ // diagnostic group 1
+ "\n", // primary message
+ "\n", // padding
+ "const a: i32 = 'a';\n",
+ "\n", // supporting diagnostic
+ "const b: i32 = c;\n",
+ "\n", // context ellipsis
+ // diagnostic group 2
+ "\n", // primary message
+ "\n", // padding
+ "const a: i32 = 'a';\n",
+ "const b: i32 = c;\n",
+ "\n", // supporting diagnostic
+ //
+ // main.rs
+ //
+ "\n", // filename
+ "\n", // padding
+ // diagnostic group 1
+ "\n", // primary message
+ "\n", // padding
+ " let x = vec![];\n",
+ " let y = vec![];\n",
+ "\n", // supporting diagnostic
+ " a(x);\n",
+ " b(y);\n",
+ "\n", // supporting diagnostic
+ " // comment 1\n",
+ " // comment 2\n",
+ " c(y);\n",
+ "\n", // supporting diagnostic
+ " d(x);\n",
+ "\n", // context ellipsis
+ // diagnostic group 2
+ "\n", // primary message
+ "\n", // filename
+ "fn main() {\n",
+ " let x = vec![];\n",
+ "\n", // supporting diagnostic
+ " let y = vec![];\n",
+ " a(x);\n",
+ "\n", // supporting diagnostic
+ " b(y);\n",
+ "\n", // context ellipsis
+ " c(y);\n",
+ " d(x);\n",
+ "\n", // supporting diagnostic
+ "}"
+ )
+ );
}
#[gpui::test]
@@ -1364,6 +1361,7 @@ mod tests {
cx,
)
});
+ let editor = view.update(cx, |view, _| view.editor.clone());
// Two language servers start updating diagnostics
project.update(cx, |project, cx| {
@@ -1397,27 +1395,25 @@ mod tests {
// Only the first language server's diagnostics are shown.
cx.executor().run_until_parked();
- view.update(cx, |view, cx| {
- assert_eq!(
- editor_blocks(&view.editor, cx),
- [
- (0, "path header block".into()),
- (2, "diagnostic header".into()),
- ]
- );
- assert_eq!(
- view.editor.update(cx, |editor, cx| editor.display_text(cx)),
- concat!(
- "\n", // filename
- "\n", // padding
- // diagnostic group 1
- "\n", // primary message
- "\n", // padding
- "a();\n", //
- "b();",
- )
- );
- });
+ assert_eq!(
+ editor_blocks(&editor, cx),
+ [
+ (0, "path header block".into()),
+ (2, "diagnostic header".into()),
+ ]
+ );
+ assert_eq!(
+ editor.update(cx, |editor, cx| editor.display_text(cx)),
+ concat!(
+ "\n", // filename
+ "\n", // padding
+ // diagnostic group 1
+ "\n", // primary message
+ "\n", // padding
+ "a();\n", //
+ "b();",
+ )
+ );
// The second language server finishes
project.update(cx, |project, cx| {
@@ -1445,36 +1441,34 @@ mod tests {
// Both language server's diagnostics are shown.
cx.executor().run_until_parked();
- view.update(cx, |view, cx| {
- assert_eq!(
- editor_blocks(&view.editor, cx),
- [
- (0, "path header block".into()),
- (2, "diagnostic header".into()),
- (6, "collapsed context".into()),
- (7, "diagnostic header".into()),
- ]
- );
- assert_eq!(
- view.editor.update(cx, |editor, cx| editor.display_text(cx)),
- concat!(
- "\n", // filename
- "\n", // padding
- // diagnostic group 1
- "\n", // primary message
- "\n", // padding
- "a();\n", // location
- "b();\n", //
- "\n", // collapsed context
- // diagnostic group 2
- "\n", // primary message
- "\n", // padding
- "a();\n", // context
- "b();\n", //
- "c();", // context
- )
- );
- });
+ assert_eq!(
+ editor_blocks(&editor, cx),
+ [
+ (0, "path header block".into()),
+ (2, "diagnostic header".into()),
+ (6, "collapsed context".into()),
+ (7, "diagnostic header".into()),
+ ]
+ );
+ assert_eq!(
+ editor.update(cx, |editor, cx| editor.display_text(cx)),
+ concat!(
+ "\n", // filename
+ "\n", // padding
+ // diagnostic group 1
+ "\n", // primary message
+ "\n", // padding
+ "a();\n", // location
+ "b();\n", //
+ "\n", // collapsed context
+ // diagnostic group 2
+ "\n", // primary message
+ "\n", // padding
+ "a();\n", // context
+ "b();\n", //
+ "c();", // context
+ )
+ );
// Both language servers start updating diagnostics, and the first server finishes.
project.update(cx, |project, cx| {
@@ -1513,37 +1507,35 @@ mod tests {
// Only the first language server's diagnostics are updated.
cx.executor().run_until_parked();
- view.update(cx, |view, cx| {
- assert_eq!(
- editor_blocks(&view.editor, cx),
- [
- (0, "path header block".into()),
- (2, "diagnostic header".into()),
- (7, "collapsed context".into()),
- (8, "diagnostic header".into()),
- ]
- );
- assert_eq!(
- view.editor.update(cx, |editor, cx| editor.display_text(cx)),
- concat!(
- "\n", // filename
- "\n", // padding
- // diagnostic group 1
- "\n", // primary message
- "\n", // padding
- "a();\n", // location
- "b();\n", //
- "c();\n", // context
- "\n", // collapsed context
- // diagnostic group 2
- "\n", // primary message
- "\n", // padding
- "b();\n", // context
- "c();\n", //
- "d();", // context
- )
- );
- });
+ assert_eq!(
+ editor_blocks(&editor, cx),
+ [
+ (0, "path header block".into()),
+ (2, "diagnostic header".into()),
+ (7, "collapsed context".into()),
+ (8, "diagnostic header".into()),
+ ]
+ );
+ assert_eq!(
+ editor.update(cx, |editor, cx| editor.display_text(cx)),
+ concat!(
+ "\n", // filename
+ "\n", // padding
+ // diagnostic group 1
+ "\n", // primary message
+ "\n", // padding
+ "a();\n", // location
+ "b();\n", //
+ "c();\n", // context
+ "\n", // collapsed context
+ // diagnostic group 2
+ "\n", // primary message
+ "\n", // padding
+ "b();\n", // context
+ "c();\n", //
+ "d();", // context
+ )
+ );
// The second language server finishes.
project.update(cx, |project, cx| {
@@ -1571,37 +1563,35 @@ mod tests {
// Both language servers' diagnostics are updated.
cx.executor().run_until_parked();
- view.update(cx, |view, cx| {
- assert_eq!(
- editor_blocks(&view.editor, cx),
- [
- (0, "path header block".into()),
- (2, "diagnostic header".into()),
- (7, "collapsed context".into()),
- (8, "diagnostic header".into()),
- ]
- );
- assert_eq!(
- view.editor.update(cx, |editor, cx| editor.display_text(cx)),
- concat!(
- "\n", // filename
- "\n", // padding
- // diagnostic group 1
- "\n", // primary message
- "\n", // padding
- "b();\n", // location
- "c();\n", //
- "d();\n", // context
- "\n", // collapsed context
- // diagnostic group 2
- "\n", // primary message
- "\n", // padding
- "c();\n", // context
- "d();\n", //
- "e();", // context
- )
- );
- });
+ assert_eq!(
+ editor_blocks(&editor, cx),
+ [
+ (0, "path header block".into()),
+ (2, "diagnostic header".into()),
+ (7, "collapsed context".into()),
+ (8, "diagnostic header".into()),
+ ]
+ );
+ assert_eq!(
+ editor.update(cx, |editor, cx| editor.display_text(cx)),
+ concat!(
+ "\n", // filename
+ "\n", // padding
+ // diagnostic group 1
+ "\n", // primary message
+ "\n", // padding
+ "b();\n", // location
+ "c();\n", //
+ "d();\n", // context
+ "\n", // collapsed context
+ // diagnostic group 2
+ "\n", // primary message
+ "\n", // padding
+ "c();\n", // context
+ "d();\n", //
+ "e();", // context
+ )
+ );
}
fn init_test(cx: &mut TestAppContext) {
@@ -1618,45 +1608,58 @@ mod tests {
});
}
- fn editor_blocks(editor: &View<Editor>, cx: &mut WindowContext) -> Vec<(u32, SharedString)> {
- editor.update(cx, |editor, cx| {
- let snapshot = editor.snapshot(cx);
- snapshot
- .blocks_in_range(0..snapshot.max_point().row())
- .enumerate()
- .filter_map(|(ix, (row, block))| {
- let name: SharedString = match block {
- TransformBlock::Custom(block) => cx.with_element_context({
- |cx| -> Option<SharedString> {
- let mut element = block.render(&mut BlockContext {
- context: cx,
- anchor_x: px(0.),
- gutter_dimensions: &GutterDimensions::default(),
- line_height: px(0.),
- em_width: px(0.),
- max_width: px(0.),
- block_id: ix,
- editor_style: &editor::EditorStyle::default(),
- });
- let element = element.downcast_mut::<Stateful<Div>>().unwrap();
- element.interactivity().element_id.clone()?.try_into().ok()
- }
- })?,
-
- TransformBlock::ExcerptHeader {
- starts_new_buffer, ..
- } => {
- if *starts_new_buffer {
- "path header block".into()
- } else {
- "collapsed context".into()
- }
- }
- };
+ fn editor_blocks(
+ editor: &View<Editor>,
+ cx: &mut VisualTestContext,
+ ) -> Vec<(u32, SharedString)> {
+ let mut blocks = Vec::new();
+ cx.draw(gpui::Point::default(), AvailableSpace::min_size(), |cx| {
+ editor.update(cx, |editor, cx| {
+ let snapshot = editor.snapshot(cx);
+ blocks.extend(
+ snapshot
+ .blocks_in_range(0..snapshot.max_point().row())
+ .enumerate()
+ .filter_map(|(ix, (row, block))| {
+ let name: SharedString = match block {
+ TransformBlock::Custom(block) => {
+ let mut element = block.render(&mut BlockContext {
+ context: cx,
+ anchor_x: px(0.),
+ gutter_dimensions: &GutterDimensions::default(),
+ line_height: px(0.),
+ em_width: px(0.),
+ max_width: px(0.),
+ block_id: ix,
+ editor_style: &editor::EditorStyle::default(),
+ });
+ let element = element.downcast_mut::<Stateful<Div>>().unwrap();
+ element
+ .interactivity()
+ .element_id
+ .clone()?
+ .try_into()
+ .ok()?
+ }
- Some((row, name))
- })
- .collect()
- })
+ TransformBlock::ExcerptHeader {
+ starts_new_buffer, ..
+ } => {
+ if *starts_new_buffer {
+ "path header block".into()
+ } else {
+ "collapsed context".into()
+ }
+ }
+ };
+
+ Some((row, name))
+ }),
+ )
+ });
+
+ div().into_any()
+ });
+ blocks
}
}
@@ -39,17 +39,15 @@ impl<'a> CommitAvatar<'a> {
let avatar_url = CommitAvatarAsset::new(remote.clone(), self.sha);
- let element = cx.with_element_context(|cx| {
- match cx.use_cached_asset::<CommitAvatarAsset>(&avatar_url) {
- // Loading or no avatar found
- None | Some(None) => Icon::new(IconName::Person)
- .color(Color::Muted)
- .into_element()
- .into_any(),
- // Found
- Some(Some(url)) => Avatar::new(url.to_string()).into_element().into_any(),
- }
- });
+ let element = match cx.use_cached_asset::<CommitAvatarAsset>(&avatar_url) {
+ // Loading or no avatar found
+ None | Some(None) => Icon::new(IconName::Person)
+ .color(Color::Muted)
+ .into_element()
+ .into_any(),
+ // Found
+ Some(Some(url)) => Avatar::new(url.to_string()).into_element().into_any(),
+ };
Some(element)
}
}
@@ -4,7 +4,7 @@ use super::{
};
use crate::{EditorStyle, GutterDimensions};
use collections::{Bound, HashMap, HashSet};
-use gpui::{AnyElement, ElementContext, Pixels};
+use gpui::{AnyElement, Pixels, WindowContext};
use language::{BufferSnapshot, Chunk, Patch, Point};
use multi_buffer::{Anchor, ExcerptId, ExcerptRange, ToPoint as _};
use parking_lot::Mutex;
@@ -82,7 +82,7 @@ pub enum BlockStyle {
}
pub struct BlockContext<'a, 'b> {
- pub context: &'b mut ElementContext<'a>,
+ pub context: &'b mut WindowContext<'a>,
pub anchor_x: Pixels,
pub max_width: Pixels,
pub gutter_dimensions: &'b GutterDimensions,
@@ -934,7 +934,7 @@ impl BlockDisposition {
}
impl<'a> Deref for BlockContext<'a, '_> {
- type Target = ElementContext<'a>;
+ type Target = WindowContext<'a>;
fn deref(&self) -> &Self::Target {
self.context
@@ -23,12 +23,11 @@ use git::{blame::BlameEntry, diff::DiffHunkStatus, Oid};
use gpui::{
anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, Bounds, ClipboardItem,
- ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementContext,
- ElementInputHandler, Entity, Hitbox, Hsla, InteractiveElement, IntoElement,
- ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
- ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size, Stateful,
- StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, TextStyleRefinement, View,
- ViewContext, WeakView, WindowContext,
+ ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Hitbox,
+ Hsla, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
+ MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent,
+ ShapedLine, SharedString, Size, Stateful, StatefulInteractiveElement, Style, Styled, TextRun,
+ TextStyle, TextStyleRefinement, View, ViewContext, WeakView, WindowContext,
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
@@ -367,7 +366,7 @@ impl EditorElement {
register_action(view, cx, Editor::open_active_item_in_terminal)
}
- fn register_key_listeners(&self, cx: &mut ElementContext, layout: &EditorLayout) {
+ fn register_key_listeners(&self, cx: &mut WindowContext, layout: &EditorLayout) {
let position_map = layout.position_map.clone();
cx.on_key_event({
let editor = self.editor.clone();
@@ -691,7 +690,7 @@ impl EditorElement {
snapshot: &EditorSnapshot,
start_row: u32,
end_row: u32,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> (
Vec<(PlayerColor, Vec<SelectionLayout>)>,
BTreeMap<u32, bool>,
@@ -819,7 +818,7 @@ impl EditorElement {
scroll_pixel_position: gpui::Point<Pixels>,
line_height: Pixels,
line_layouts: &[LineWithInvisibles],
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Vec<FoldLayout> {
snapshot
.folds_in_range(visible_anchor_range.clone())
@@ -887,7 +886,7 @@ impl EditorElement {
line_height: Pixels,
em_width: Pixels,
autoscroll_containing_element: bool,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Vec<CursorLayout> {
let mut autoscroll_bounds = None;
let cursor_layouts = self.editor.update(cx, |editor, cx| {
@@ -993,7 +992,7 @@ impl EditorElement {
color: self.style.background,
is_top_row: cursor_position.row() == 0,
});
- cx.with_element_context(|cx| cursor.layout(content_origin, cursor_name, cx));
+ cursor.layout(content_origin, cursor_name, cx);
cursors.push(cursor);
}
}
@@ -1013,7 +1012,7 @@ impl EditorElement {
bounds: Bounds<Pixels>,
scroll_position: gpui::Point<f32>,
rows_per_page: f32,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<ScrollbarLayout> {
let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
let show_scrollbars = match scrollbar_settings.show {
@@ -1082,7 +1081,7 @@ impl EditorElement {
gutter_settings: crate::editor_settings::Gutter,
scroll_pixel_position: gpui::Point<Pixels>,
gutter_hitbox: &Hitbox,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Vec<Option<AnyElement>> {
let mut indicators = self.editor.update(cx, |editor, cx| {
editor.render_fold_indicators(
@@ -1155,7 +1154,7 @@ impl EditorElement {
content_origin: gpui::Point<Pixels>,
scroll_pixel_position: gpui::Point<Pixels>,
line_height: Pixels,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<AnyElement> {
if !self
.editor
@@ -1220,7 +1219,7 @@ impl EditorElement {
line_height: Pixels,
gutter_hitbox: &Hitbox,
max_width: Option<Pixels>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<Vec<AnyElement>> {
if !self
.editor
@@ -1285,7 +1284,7 @@ impl EditorElement {
scroll_pixel_position: gpui::Point<Pixels>,
gutter_dimensions: &GutterDimensions,
gutter_hitbox: &Hitbox,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<AnyElement> {
let mut active = false;
let mut button = None;
@@ -1372,7 +1371,7 @@ impl EditorElement {
active_rows: &BTreeMap<u32, bool>,
newest_selection_head: Option<DisplayPoint>,
snapshot: &EditorSnapshot,
- cx: &ElementContext,
+ cx: &WindowContext,
) -> (
Vec<Option<ShapedLine>>,
Vec<Option<(FoldStatus, BufferRow, bool)>>,
@@ -1465,7 +1464,7 @@ impl EditorElement {
rows: Range<u32>,
line_number_layouts: &[Option<ShapedLine>],
snapshot: &EditorSnapshot,
- cx: &ElementContext,
+ cx: &WindowContext,
) -> Vec<LineWithInvisibles> {
if rows.start >= rows.end {
return Vec::new();
@@ -1530,7 +1529,7 @@ impl EditorElement {
text_x: Pixels,
line_height: Pixels,
line_layouts: &[LineWithInvisibles],
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Vec<BlockLayout> {
let mut block_id = 0;
let (fixed_blocks, non_fixed_blocks) = snapshot
@@ -1544,7 +1543,7 @@ impl EditorElement {
available_space: Size<AvailableSpace>,
block_id: usize,
block_row_start: u32,
- cx: &mut ElementContext| {
+ cx: &mut WindowContext| {
let mut element = match block {
TransformBlock::Custom(block) => {
let align_to = block
@@ -1865,7 +1864,7 @@ impl EditorElement {
hitbox: &Hitbox,
line_height: Pixels,
scroll_pixel_position: gpui::Point<Pixels>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
for block in blocks {
let mut origin = hitbox.origin
@@ -1893,7 +1892,7 @@ impl EditorElement {
scroll_pixel_position: gpui::Point<Pixels>,
line_layouts: &[LineWithInvisibles],
newest_selection_head: DisplayPoint,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> bool {
let max_height = cmp::min(
12. * line_height,
@@ -1933,7 +1932,7 @@ impl EditorElement {
true
}
- fn layout_mouse_context_menu(&self, cx: &mut ElementContext) -> Option<AnyElement> {
+ fn layout_mouse_context_menu(&self, cx: &mut WindowContext) -> Option<AnyElement> {
let mouse_context_menu = self.editor.read(cx).mouse_context_menu.as_ref()?;
let mut element = deferred(
anchored()
@@ -1961,7 +1960,7 @@ impl EditorElement {
line_layouts: &[LineWithInvisibles],
line_height: Pixels,
em_width: Pixels,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
struct MeasuredHoverPopover {
element: AnyElement,
@@ -2021,7 +2020,7 @@ impl EditorElement {
}
overall_height += HOVER_POPOVER_GAP;
- fn draw_occluder(width: Pixels, origin: gpui::Point<Pixels>, cx: &mut ElementContext) {
+ fn draw_occluder(width: Pixels, origin: gpui::Point<Pixels>, cx: &mut WindowContext) {
let mut occlusion = div()
.size_full()
.occlude()
@@ -2067,7 +2066,7 @@ impl EditorElement {
}
}
- fn paint_background(&self, layout: &EditorLayout, cx: &mut ElementContext) {
+ fn paint_background(&self, layout: &EditorLayout, cx: &mut WindowContext) {
cx.paint_layer(layout.hitbox.bounds, |cx| {
let scroll_top = layout.position_map.snapshot.scroll_position().y;
let gutter_bg = cx.theme().colors().editor_gutter_background;
@@ -2188,7 +2187,7 @@ impl EditorElement {
})
}
- fn paint_gutter(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+ fn paint_gutter(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
let line_height = layout.position_map.line_height;
let scroll_position = layout.position_map.snapshot.scroll_position();
@@ -2236,7 +2235,7 @@ impl EditorElement {
})
}
- fn paint_diff_hunks(layout: &EditorLayout, cx: &mut ElementContext) {
+ fn paint_diff_hunks(layout: &EditorLayout, cx: &mut WindowContext) {
if layout.display_hunks.is_empty() {
return;
}
@@ -2342,7 +2341,7 @@ impl EditorElement {
})
}
- fn paint_blamed_display_rows(&self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+ fn paint_blamed_display_rows(&self, layout: &mut EditorLayout, cx: &mut WindowContext) {
let Some(blamed_display_rows) = layout.blamed_display_rows.take() else {
return;
};
@@ -2354,7 +2353,7 @@ impl EditorElement {
})
}
- fn paint_text(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+ fn paint_text(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
cx.with_content_mask(
Some(ContentMask {
bounds: layout.text_hitbox.bounds,
@@ -2386,7 +2385,7 @@ impl EditorElement {
fn paint_highlights(
&mut self,
layout: &mut EditorLayout,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> SmallVec<[Range<DisplayPoint>; 32]> {
cx.paint_layer(layout.text_hitbox.bounds, |cx| {
let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
@@ -2428,7 +2427,7 @@ impl EditorElement {
&mut self,
invisible_display_ranges: &[Range<DisplayPoint>],
layout: &EditorLayout,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let whitespace_setting = self
.editor
@@ -2451,7 +2450,7 @@ impl EditorElement {
}
}
- fn paint_redactions(&mut self, layout: &EditorLayout, cx: &mut ElementContext) {
+ fn paint_redactions(&mut self, layout: &EditorLayout, cx: &mut WindowContext) {
if layout.redacted_ranges.is_empty() {
return;
}
@@ -2475,13 +2474,13 @@ impl EditorElement {
});
}
- fn paint_cursors(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+ fn paint_cursors(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
for cursor in &mut layout.cursors {
cursor.paint(layout.content_origin, cx);
}
}
- fn paint_scrollbar(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+ fn paint_scrollbar(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
let Some(scrollbar_layout) = layout.scrollbar_layout.as_ref() else {
return;
};
@@ -2617,7 +2616,7 @@ impl EditorElement {
&self,
layout: &EditorLayout,
scrollbar_layout: &ScrollbarLayout,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
self.editor.update(cx, |editor, cx| {
if !editor.is_singleton(cx)
@@ -2775,7 +2774,7 @@ impl EditorElement {
corner_radius: Pixels,
line_end_overshoot: Pixels,
layout: &EditorLayout,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let start_row = layout.visible_display_row_range.start;
let end_row = layout.visible_display_row_range.end;
@@ -2824,7 +2823,7 @@ impl EditorElement {
}
}
- fn paint_folds(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+ fn paint_folds(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
if layout.folds.is_empty() {
return;
}
@@ -2855,7 +2854,7 @@ impl EditorElement {
})
}
- fn paint_inline_blame(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+ fn paint_inline_blame(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
if let Some(mut inline_blame) = layout.inline_blame.take() {
cx.paint_layer(layout.text_hitbox.bounds, |cx| {
inline_blame.paint(cx);
@@ -2863,19 +2862,19 @@ impl EditorElement {
}
}
- fn paint_blocks(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+ fn paint_blocks(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
for mut block in layout.blocks.drain(..) {
block.element.paint(cx);
}
}
- fn paint_mouse_context_menu(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+ fn paint_mouse_context_menu(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
if let Some(mouse_context_menu) = layout.mouse_context_menu.as_mut() {
mouse_context_menu.paint(cx);
}
}
- fn paint_scroll_wheel_listener(&mut self, layout: &EditorLayout, cx: &mut ElementContext) {
+ fn paint_scroll_wheel_listener(&mut self, layout: &EditorLayout, cx: &mut WindowContext) {
cx.on_mouse_event({
let position_map = layout.position_map.clone();
let editor = self.editor.clone();
@@ -2925,7 +2924,7 @@ impl EditorElement {
});
}
- fn paint_mouse_listeners(&mut self, layout: &EditorLayout, cx: &mut ElementContext) {
+ fn paint_mouse_listeners(&mut self, layout: &EditorLayout, cx: &mut WindowContext) {
self.paint_scroll_wheel_listener(layout, cx);
cx.on_mouse_event({
@@ -3042,7 +3041,7 @@ fn render_inline_blame_entry(
blame_entry: BlameEntry,
style: &EditorStyle,
workspace: Option<WeakView<Workspace>>,
- cx: &mut ElementContext<'_>,
+ cx: &mut WindowContext<'_>,
) -> AnyElement {
let relative_timestamp = blame_entry_relative_timestamp(&blame_entry, cx);
@@ -3073,7 +3072,7 @@ fn render_blame_entry(
style: &EditorStyle,
last_used_color: &mut Option<(PlayerColor, Oid)>,
editor: View<Editor>,
- cx: &mut ElementContext<'_>,
+ cx: &mut WindowContext<'_>,
) -> AnyElement {
let mut sha_color = cx
.theme()
@@ -3286,7 +3285,7 @@ impl LineWithInvisibles {
content_origin: gpui::Point<Pixels>,
whitespace_setting: ShowWhitespaceSetting,
selection_ranges: &[Range<DisplayPoint>],
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let line_height = layout.position_map.line_height;
let line_y =
@@ -3318,7 +3317,7 @@ impl LineWithInvisibles {
row: u32,
line_height: Pixels,
whitespace_setting: ShowWhitespaceSetting,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let allowed_invisibles_regions = match whitespace_setting {
ShowWhitespaceSetting::None => return,
@@ -3365,7 +3364,7 @@ impl Element for EditorElement {
type RequestLayoutState = ();
type PrepaintState = EditorLayout;
- fn request_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, ()) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (gpui::LayoutId, ()) {
self.editor.update(cx, |editor, cx| {
editor.set_style(self.style.clone(), cx);
@@ -3375,36 +3374,31 @@ impl Element for EditorElement {
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.with_element_context(|cx| cx.request_layout(&style, None))
+ 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.with_element_context(|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()
- },
- )
+ 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.with_element_context(|cx| cx.request_layout(&style, None))
+ cx.request_layout(&style, None)
}
};
@@ -3416,7 +3410,7 @@ impl Element for EditorElement {
&mut self,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Self::PrepaintState {
let text_style = TextStyleRefinement {
font_size: Some(self.style.text.font_size),
@@ -3847,13 +3841,12 @@ impl Element for EditorElement {
bounds: Bounds<gpui::Pixels>,
_: &mut Self::RequestLayoutState,
layout: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let focus_handle = self.editor.focus_handle(cx);
let key_context = self.editor.read(cx).key_context(cx);
cx.set_focus_handle(&focus_handle);
cx.set_key_context(key_context);
- cx.set_view_id(self.editor.entity_id());
cx.handle_input(
&focus_handle,
ElementInputHandler::new(bounds, self.editor.clone()),
@@ -4206,7 +4199,7 @@ impl CursorLayout {
&mut self,
origin: gpui::Point<Pixels>,
cursor_name: Option<CursorName>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
if let Some(cursor_name) = cursor_name {
let bounds = self.bounds(origin);
@@ -4236,7 +4229,7 @@ impl CursorLayout {
}
}
- pub fn paint(&mut self, origin: gpui::Point<Pixels>, cx: &mut ElementContext) {
+ pub fn paint(&mut self, origin: gpui::Point<Pixels>, cx: &mut WindowContext) {
let bounds = self.bounds(origin);
//Draw background or border quad
@@ -4280,7 +4273,7 @@ pub struct HighlightedRangeLine {
}
impl HighlightedRange {
- pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut ElementContext) {
+ pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
self.paint_lines(self.start_y, &self.lines[0..1], bounds, cx);
self.paint_lines(
@@ -4299,7 +4292,7 @@ impl HighlightedRange {
start_y: Pixels,
lines: &[HighlightedRangeLine],
_bounds: Bounds<Pixels>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
if lines.is_empty() {
return;
@@ -4416,7 +4409,7 @@ mod tests {
editor_tests::{init_test, update_test_language_settings},
Editor, MultiBuffer,
};
- use gpui::TestAppContext;
+ use gpui::{TestAppContext, VisualTestContext};
use language::language_settings;
use log::info;
use std::num::NonZeroU32;
@@ -4437,18 +4430,16 @@ mod tests {
let layouts = cx
.update_window(*window, |_, cx| {
- cx.with_element_context(|cx| {
- element
- .layout_line_numbers(
- 0..6,
- (0..6).map(Some),
- &Default::default(),
- Some(DisplayPoint::new(0, 0)),
- &snapshot,
- cx,
- )
- .0
- })
+ element
+ .layout_line_numbers(
+ 0..6,
+ (0..6).map(Some),
+ &Default::default(),
+ Some(DisplayPoint::new(0, 0)),
+ &snapshot,
+ cx,
+ )
+ .0
})
.unwrap();
assert_eq!(layouts.len(), 6);
@@ -4487,9 +4478,9 @@ mod tests {
let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
Editor::new(EditorMode::Full, buffer, None, cx)
});
+ let cx = &mut VisualTestContext::from_window(*window, cx);
let editor = window.root(cx).unwrap();
let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
- let mut element = EditorElement::new(&editor, style);
window
.update(cx, |editor, cx| {
@@ -4503,20 +4494,10 @@ mod tests {
});
})
.unwrap();
- let state = cx
- .update_window(window.into(), |_view, cx| {
- cx.with_element_context(|cx| {
- element.prepaint(
- Bounds {
- origin: point(px(500.), px(500.)),
- size: size(px(500.), px(500.)),
- },
- &mut (),
- cx,
- )
- })
- })
- .unwrap();
+
+ let (_, state) = cx.draw(point(px(500.), px(500.)), size(px(500.), px(500.)), |_| {
+ EditorElement::new(&editor, style)
+ });
assert_eq!(state.selections.len(), 1);
let local_selections = &state.selections[0].1;
@@ -4587,7 +4568,6 @@ mod tests {
});
let editor = window.root(cx).unwrap();
let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
- let mut element = EditorElement::new(&editor, style);
let _state = window.update(cx, |editor, cx| {
editor.cursor_shape = CursorShape::Block;
editor.change_selections(None, cx, |s| {
@@ -4598,20 +4578,9 @@ mod tests {
});
});
- let state = cx
- .update_window(window.into(), |_view, cx| {
- cx.with_element_context(|cx| {
- element.prepaint(
- Bounds {
- origin: point(px(500.), px(500.)),
- size: size(px(500.), px(500.)),
- },
- &mut (),
- cx,
- )
- })
- })
- .unwrap();
+ let (_, state) = cx.draw(point(px(500.), px(500.)), size(px(500.), px(500.)), |_| {
+ EditorElement::new(&editor, style)
+ });
assert_eq!(state.selections.len(), 1);
let local_selections = &state.selections[0].1;
assert_eq!(local_selections.len(), 2);
@@ -4640,6 +4609,7 @@ mod tests {
let buffer = MultiBuffer::build_simple("", cx);
Editor::new(EditorMode::Full, buffer, None, cx)
});
+ let cx = &mut VisualTestContext::from_window(*window, cx);
let editor = window.root(cx).unwrap();
let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
window
@@ -4662,22 +4632,9 @@ mod tests {
})
.unwrap();
- let mut element = EditorElement::new(&editor, style);
- let state = cx
- .update_window(window.into(), |_view, cx| {
- cx.with_element_context(|cx| {
- element.prepaint(
- Bounds {
- origin: point(px(500.), px(500.)),
- size: size(px(500.), px(500.)),
- },
- &mut (),
- cx,
- )
- })
- })
- .unwrap();
-
+ let (_, state) = cx.draw(point(px(500.), px(500.)), size(px(500.), px(500.)), |_| {
+ EditorElement::new(&editor, style)
+ });
assert_eq!(state.position_map.line_layouts.len(), 4);
assert_eq!(
state
@@ -4850,31 +4807,19 @@ mod tests {
let buffer = MultiBuffer::build_simple(&input_text, cx);
Editor::new(editor_mode, buffer, None, cx)
});
+ let cx = &mut VisualTestContext::from_window(*window, cx);
let editor = window.root(cx).unwrap();
let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
- let mut element = EditorElement::new(&editor, style);
window
.update(cx, |editor, cx| {
editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
editor.set_wrap_width(Some(editor_width), cx);
})
.unwrap();
- let layout_state = cx
- .update_window(window.into(), |_, cx| {
- cx.with_element_context(|cx| {
- element.prepaint(
- Bounds {
- origin: point(px(500.), px(500.)),
- size: size(px(500.), px(500.)),
- },
- &mut (),
- cx,
- )
- })
- })
- .unwrap();
-
- layout_state
+ let (_, state) = cx.draw(point(px(500.), px(500.)), size(px(500.), px(500.)), |_| {
+ EditorElement::new(&editor, style)
+ });
+ state
.position_map
.line_layouts
.iter()
@@ -1,10 +1,11 @@
use crate::{
- Action, AnyElement, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
- AvailableSpace, BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, Context, Empty,
- Entity, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Model, ModelContext,
- Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
- Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow,
- TextSystem, View, ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
+ Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, AvailableSpace,
+ BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, Context, DrawPhase, Drawable,
+ Element, Empty, Entity, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Model,
+ ModelContext, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
+ MouseUpEvent, Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher,
+ TestPlatform, TestWindow, TextSystem, View, ViewContext, VisualContext, WindowContext,
+ WindowHandle, WindowOptions,
};
use anyhow::{anyhow, bail};
use futures::{channel::oneshot, Stream, StreamExt};
@@ -725,21 +726,28 @@ impl VisualTestContext {
}
/// Draw an element to the window. Useful for simulating events or actions
- pub fn draw(
+ pub fn draw<E>(
&mut self,
origin: Point<Pixels>,
- space: Size<AvailableSpace>,
- f: impl FnOnce(&mut WindowContext) -> AnyElement,
- ) {
+ space: impl Into<Size<AvailableSpace>>,
+ f: impl FnOnce(&mut WindowContext) -> E,
+ ) -> (E::RequestLayoutState, E::PrepaintState)
+ where
+ E: Element,
+ {
self.update(|cx| {
- cx.with_element_context(|cx| {
- let mut element = f(cx);
- element.layout_as_root(space, cx);
- cx.with_absolute_element_offset(origin, |cx| element.prepaint(cx));
- element.paint(cx);
- });
+ cx.window.draw_phase = DrawPhase::Prepaint;
+ let mut element = Drawable::new(f(cx));
+ element.layout_as_root(space.into(), cx);
+ cx.with_absolute_element_offset(origin, |cx| element.prepaint(cx));
+ cx.window.draw_phase = DrawPhase::Paint;
+ let (request_layout_state, prepaint_state) = element.paint(cx);
+
+ cx.window.draw_phase = DrawPhase::None;
cx.refresh();
+
+ (request_layout_state, prepaint_state)
})
}
@@ -32,12 +32,12 @@
//! your own custom layout algorithm or rendering a code editor.
use crate::{
- util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementContext,
- ElementId, LayoutId, Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
+ util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementId, LayoutId,
+ Pixels, Point, Size, Style, ViewContext, WindowContext, ELEMENT_ARENA,
};
use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec;
-use std::{any::Any, fmt::Debug, mem, ops::DerefMut};
+use std::{any::Any, fmt::Debug, mem};
/// Implemented by types that participate in laying out and painting the contents of a window.
/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
@@ -54,7 +54,7 @@ pub trait Element: 'static + IntoElement {
/// Before an element can be painted, we need to know where it's going to be and how big it is.
/// Use this method to request a layout from Taffy and initialize the element's state.
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState);
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState);
/// After laying out an element, we need to commit its bounds to the current frame for hitbox
/// purposes. The state argument is the same state that was returned from [`Element::request_layout()`].
@@ -62,7 +62,7 @@ pub trait Element: 'static + IntoElement {
&mut self,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Self::PrepaintState;
/// Once layout has been completed, this method will be called to paint the element to the screen.
@@ -72,7 +72,7 @@ pub trait Element: 'static + IntoElement {
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
);
/// Convert this element into a dynamically-typed [`AnyElement`].
@@ -164,18 +164,13 @@ impl<C: RenderOnce> Element for Component<C> {
type RequestLayoutState = AnyElement;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
- let mut element = self
- .0
- .take()
- .unwrap()
- .render(cx.deref_mut())
- .into_any_element();
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ let mut element = self.0.take().unwrap().render(cx).into_any_element();
let layout_id = element.request_layout(cx);
(layout_id, element)
}
- fn prepaint(&mut self, _: Bounds<Pixels>, element: &mut AnyElement, cx: &mut ElementContext) {
+ fn prepaint(&mut self, _: Bounds<Pixels>, element: &mut AnyElement, cx: &mut WindowContext) {
element.prepaint(cx);
}
@@ -184,7 +179,7 @@ impl<C: RenderOnce> Element for Component<C> {
_: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
element.paint(cx)
}
@@ -205,16 +200,16 @@ pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
trait ElementObject {
fn inner_element(&mut self) -> &mut dyn Any;
- fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
+ fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
- fn prepaint(&mut self, cx: &mut ElementContext);
+ fn prepaint(&mut self, cx: &mut WindowContext);
- fn paint(&mut self, cx: &mut ElementContext);
+ fn paint(&mut self, cx: &mut WindowContext);
fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Size<Pixels>;
}
@@ -249,14 +244,14 @@ enum ElementDrawPhase<RequestLayoutState, PrepaintState> {
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
impl<E: Element> Drawable<E> {
- fn new(element: E) -> Self {
+ pub(crate) fn new(element: E) -> Self {
Drawable {
element,
phase: ElementDrawPhase::Start,
}
}
- fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
match mem::take(&mut self.phase) {
ElementDrawPhase::Start => {
let (layout_id, request_layout) = self.element.request_layout(cx);
@@ -270,7 +265,7 @@ impl<E: Element> Drawable<E> {
}
}
- fn prepaint(&mut self, cx: &mut ElementContext) {
+ pub(crate) fn prepaint(&mut self, cx: &mut WindowContext) {
match mem::take(&mut self.phase) {
ElementDrawPhase::RequestLayoutState {
layout_id,
@@ -296,7 +291,10 @@ impl<E: Element> Drawable<E> {
}
}
- fn paint(&mut self, cx: &mut ElementContext) -> E::RequestLayoutState {
+ pub(crate) fn paint(
+ &mut self,
+ cx: &mut WindowContext,
+ ) -> (E::RequestLayoutState, E::PrepaintState) {
match mem::take(&mut self.phase) {
ElementDrawPhase::PrepaintState {
node_id,
@@ -309,16 +307,16 @@ impl<E: Element> Drawable<E> {
self.element
.paint(bounds, &mut request_layout, &mut prepaint, cx);
self.phase = ElementDrawPhase::Painted;
- request_layout
+ (request_layout, prepaint)
}
_ => panic!("must call prepaint before paint"),
}
}
- fn layout_as_root(
+ pub(crate) fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Size<Pixels> {
if matches!(&self.phase, ElementDrawPhase::Start) {
self.request_layout(cx);
@@ -368,22 +366,22 @@ where
&mut self.element
}
- fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
Drawable::request_layout(self, cx)
}
- fn prepaint(&mut self, cx: &mut ElementContext) {
+ fn prepaint(&mut self, cx: &mut WindowContext) {
Drawable::prepaint(self, cx);
}
- fn paint(&mut self, cx: &mut ElementContext) {
+ fn paint(&mut self, cx: &mut WindowContext) {
Drawable::paint(self, cx);
}
fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Size<Pixels> {
Drawable::layout_as_root(self, available_space, cx)
}
@@ -411,18 +409,18 @@ impl AnyElement {
/// Request the layout ID of the element stored in this `AnyElement`.
/// Used for laying out child elements in a parent element.
- pub fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
+ pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
self.0.request_layout(cx)
}
/// Prepares the element to be painted by storing its bounds, giving it a chance to draw hitboxes and
/// request autoscroll before the final paint pass is confirmed.
- pub fn prepaint(&mut self, cx: &mut ElementContext) {
+ pub fn prepaint(&mut self, cx: &mut WindowContext) {
self.0.prepaint(cx)
}
/// Paints the element stored in this `AnyElement`.
- pub fn paint(&mut self, cx: &mut ElementContext) {
+ pub fn paint(&mut self, cx: &mut WindowContext) {
self.0.paint(cx)
}
@@ -430,13 +428,13 @@ impl AnyElement {
pub fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Size<Pixels> {
self.0.layout_as_root(available_space, cx)
}
/// Prepaints this element at the given absolute origin.
- pub fn prepaint_at(&mut self, origin: Point<Pixels>, cx: &mut ElementContext) {
+ pub fn prepaint_at(&mut self, origin: Point<Pixels>, cx: &mut WindowContext) {
cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
}
@@ -445,7 +443,7 @@ impl AnyElement {
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
self.layout_as_root(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
@@ -456,7 +454,7 @@ impl Element for AnyElement {
type RequestLayoutState = ();
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let layout_id = self.request_layout(cx);
(layout_id, ())
}
@@ -465,7 +463,7 @@ impl Element for AnyElement {
&mut self,
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
self.prepaint(cx)
}
@@ -475,7 +473,7 @@ impl Element for AnyElement {
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
self.paint(cx)
}
@@ -508,15 +506,15 @@ impl Element for Empty {
type RequestLayoutState = ();
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
- (cx.request_layout(&crate::Style::default(), None), ())
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ (cx.request_layout(&Style::default(), None), ())
}
fn prepaint(
&mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::RequestLayoutState,
- _cx: &mut ElementContext,
+ _cx: &mut WindowContext,
) {
}
@@ -525,7 +523,7 @@ impl Element for Empty {
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
- _cx: &mut ElementContext,
+ _cx: &mut WindowContext,
) {
}
}
@@ -2,8 +2,8 @@ use smallvec::SmallVec;
use taffy::style::{Display, Position};
use crate::{
- point, AnyElement, Bounds, Element, ElementContext, IntoElement, LayoutId, ParentElement,
- Pixels, Point, Size, Style,
+ point, AnyElement, Bounds, Element, IntoElement, LayoutId, ParentElement, Pixels, Point, Size,
+ Style, WindowContext,
};
/// The state that the anchored element element uses to track its children.
@@ -74,7 +74,7 @@ impl Element for Anchored {
fn request_layout(
&mut self,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> (crate::LayoutId, Self::RequestLayoutState) {
let child_layout_ids = self
.children
@@ -97,7 +97,7 @@ impl Element for Anchored {
&mut self,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
if request_layout.child_layout_ids.is_empty() {
return;
@@ -180,7 +180,7 @@ impl Element for Anchored {
_bounds: crate::Bounds<crate::Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
for child in &mut self.children {
child.paint(cx);
@@ -91,7 +91,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
fn request_layout(
&mut self,
- cx: &mut crate::ElementContext,
+ cx: &mut crate::WindowContext,
) -> (crate::LayoutId, Self::RequestLayoutState) {
cx.with_element_state(Some(self.id.clone()), |state, cx| {
let state = state.unwrap().unwrap_or_else(|| AnimationState {
@@ -138,7 +138,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
&mut self,
_bounds: crate::Bounds<crate::Pixels>,
element: &mut Self::RequestLayoutState,
- cx: &mut crate::ElementContext,
+ cx: &mut crate::WindowContext,
) -> Self::PrepaintState {
element.prepaint(cx);
}
@@ -148,7 +148,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
_bounds: crate::Bounds<crate::Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
- cx: &mut crate::ElementContext,
+ cx: &mut crate::WindowContext,
) {
element.paint(cx);
}
@@ -1,12 +1,12 @@
use refineable::Refineable as _;
-use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRefinement, Styled};
+use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
/// Construct a canvas element with the given paint callback.
/// Useful for adding short term custom drawing to a view.
pub fn canvas<T>(
- prepaint: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext) -> T,
- paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut ElementContext),
+ prepaint: impl 'static + FnOnce(Bounds<Pixels>, &mut WindowContext) -> T,
+ paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut WindowContext),
) -> Canvas<T> {
Canvas {
prepaint: Some(Box::new(prepaint)),
@@ -18,8 +18,8 @@ pub fn canvas<T>(
/// A canvas element, meant for accessing the low level paint API without defining a whole
/// custom element
pub struct Canvas<T> {
- prepaint: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext) -> T>>,
- paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut ElementContext)>>,
+ prepaint: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut WindowContext) -> T>>,
+ paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut WindowContext)>>,
style: StyleRefinement,
}
@@ -37,7 +37,7 @@ impl<T: 'static> Element for Canvas<T> {
fn request_layout(
&mut self,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> (crate::LayoutId, Self::RequestLayoutState) {
let mut style = Style::default();
style.refine(&self.style);
@@ -49,7 +49,7 @@ impl<T: 'static> Element for Canvas<T> {
&mut self,
bounds: Bounds<Pixels>,
_request_layout: &mut Style,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<T> {
Some(self.prepaint.take().unwrap()(bounds, cx))
}
@@ -59,7 +59,7 @@ impl<T: 'static> Element for Canvas<T> {
bounds: Bounds<Pixels>,
style: &mut Style,
prepaint: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let prepaint = prepaint.take().unwrap();
style.paint(bounds, cx, |cx| {
@@ -1,4 +1,4 @@
-use crate::{AnyElement, Bounds, Element, ElementContext, IntoElement, LayoutId, Pixels};
+use crate::{AnyElement, Bounds, Element, IntoElement, LayoutId, Pixels, WindowContext};
/// Builds a `Deferred` element, which delays the layout and paint of its child.
pub fn deferred(child: impl IntoElement) -> Deferred {
@@ -29,7 +29,7 @@ impl Element for Deferred {
type RequestLayoutState = ();
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, ()) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, ()) {
let layout_id = self.child.as_mut().unwrap().request_layout(cx);
(layout_id, ())
}
@@ -38,7 +38,7 @@ impl Element for Deferred {
&mut self,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let child = self.child.take().unwrap();
let element_offset = cx.element_offset();
@@ -50,7 +50,7 @@ impl Element for Deferred {
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
- _cx: &mut ElementContext,
+ _cx: &mut WindowContext,
) {
}
}
@@ -17,11 +17,11 @@
use crate::{
point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
- ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, Global, Hitbox,
- HitboxId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
- ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
- ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
- StyleRefinement, Styled, Task, TooltipId, View, Visibility, WindowContext,
+ ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, Global, Hitbox, HitboxId,
+ IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, ModifiersChangedEvent,
+ MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point,
+ Render, ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled, Task, TooltipId,
+ View, Visibility, WindowContext,
};
use collections::HashMap;
use refineable::Refineable;
@@ -1123,7 +1123,7 @@ impl Element for Div {
type RequestLayoutState = DivFrameState;
type PrepaintState = Option<Hitbox>;
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let mut child_layout_ids = SmallVec::new();
let layout_id = self.interactivity.request_layout(cx, |style, cx| {
cx.with_text_style(style.text_style().cloned(), |cx| {
@@ -1142,7 +1142,7 @@ impl Element for Div {
&mut self,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<Hitbox> {
let mut child_min = point(Pixels::MAX, Pixels::MAX);
let mut child_max = Point::default();
@@ -1197,7 +1197,7 @@ impl Element for Div {
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
self.interactivity
.paint(bounds, hitbox.as_ref(), cx, |_style, cx| {
@@ -1276,8 +1276,8 @@ impl Interactivity {
/// Layout this element according to this interactivity state's configured styles
pub fn request_layout(
&mut self,
- cx: &mut ElementContext,
- f: impl FnOnce(Style, &mut ElementContext) -> LayoutId,
+ cx: &mut WindowContext,
+ f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
) -> LayoutId {
cx.with_element_state::<InteractiveElementState, _>(
self.element_id.clone(),
@@ -1341,8 +1341,8 @@ impl Interactivity {
&mut self,
bounds: Bounds<Pixels>,
content_size: Size<Pixels>,
- cx: &mut ElementContext,
- f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut ElementContext) -> R,
+ cx: &mut WindowContext,
+ f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut WindowContext) -> R,
) -> R {
self.content_size = content_size;
cx.with_element_state::<InteractiveElementState, _>(
@@ -1406,7 +1406,7 @@ impl Interactivity {
&mut self,
bounds: Bounds<Pixels>,
style: &Style,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Point<Pixels> {
if let Some(scroll_offset) = self.scroll_offset.as_ref() {
if let Some(scroll_handle) = &self.tracked_scroll_handle {
@@ -1456,8 +1456,8 @@ impl Interactivity {
&mut self,
bounds: Bounds<Pixels>,
hitbox: Option<&Hitbox>,
- cx: &mut ElementContext,
- f: impl FnOnce(&Style, &mut ElementContext),
+ cx: &mut WindowContext,
+ f: impl FnOnce(&Style, &mut WindowContext),
) {
self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(cx));
cx.with_element_state::<InteractiveElementState, _>(
@@ -1482,7 +1482,7 @@ impl Interactivity {
return ((), element_state);
}
- style.paint(bounds, cx, |cx: &mut ElementContext| {
+ style.paint(bounds, cx, |cx: &mut WindowContext| {
cx.with_text_style(style.text_style().cloned(), |cx| {
cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
if let Some(hitbox) = hitbox {
@@ -1521,7 +1521,7 @@ impl Interactivity {
}
#[cfg(debug_assertions)]
- fn paint_debug_info(&mut self, hitbox: &Hitbox, style: &Style, cx: &mut ElementContext) {
+ fn paint_debug_info(&mut self, hitbox: &Hitbox, style: &Style, cx: &mut WindowContext) {
if self.element_id.is_some()
&& (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
&& hitbox.is_hovered(cx)
@@ -1530,7 +1530,7 @@ impl Interactivity {
let element_id = format!("{:?}", self.element_id.as_ref().unwrap());
let str_len = element_id.len();
- let render_debug_text = |cx: &mut ElementContext| {
+ let render_debug_text = |cx: &mut WindowContext| {
if let Some(text) = cx
.text_system()
.shape_text(
@@ -1629,7 +1629,7 @@ impl Interactivity {
&mut self,
hitbox: &Hitbox,
element_state: Option<&mut InteractiveElementState>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
// If this element can be focused, register a mouse down listener
// that will automatically transfer focus when hitting the element.
@@ -1712,11 +1712,11 @@ impl Interactivity {
let mut can_drop = true;
if let Some(predicate) = &can_drop_predicate {
- can_drop = predicate(drag.value.as_ref(), cx.deref_mut());
+ can_drop = predicate(drag.value.as_ref(), cx);
}
if can_drop {
- listener(drag.value.as_ref(), cx.deref_mut());
+ listener(drag.value.as_ref(), cx);
cx.refresh();
cx.stop_propagation();
}
@@ -1840,7 +1840,7 @@ impl Interactivity {
*was_hovered = is_hovered;
drop(was_hovered);
- hover_listener(&is_hovered, cx.deref_mut());
+ hover_listener(&is_hovered, cx);
}
});
}
@@ -1969,7 +1969,7 @@ impl Interactivity {
}
}
- fn paint_keyboard_listeners(&mut self, cx: &mut ElementContext) {
+ fn paint_keyboard_listeners(&mut self, cx: &mut WindowContext) {
let key_down_listeners = mem::take(&mut self.key_down_listeners);
let key_up_listeners = mem::take(&mut self.key_up_listeners);
let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
@@ -2004,7 +2004,7 @@ impl Interactivity {
}
}
- fn paint_hover_group_handler(&self, cx: &mut ElementContext) {
+ fn paint_hover_group_handler(&self, cx: &mut WindowContext) {
let group_hitbox = self
.group_hover_style
.as_ref()
@@ -2021,7 +2021,7 @@ impl Interactivity {
}
}
- fn paint_scroll_listener(&self, hitbox: &Hitbox, style: &Style, cx: &mut ElementContext) {
+ fn paint_scroll_listener(&self, hitbox: &Hitbox, style: &Style, cx: &mut WindowContext) {
if let Some(scroll_offset) = self.scroll_offset.clone() {
let overflow = style.overflow;
let line_height = cx.line_height();
@@ -2064,7 +2064,7 @@ impl Interactivity {
}
/// Compute the visual style for this element, based on the current bounds and the element's state.
- pub fn compute_style(&self, hitbox: Option<&Hitbox>, cx: &mut ElementContext) -> Style {
+ pub fn compute_style(&self, hitbox: Option<&Hitbox>, cx: &mut WindowContext) -> Style {
cx.with_element_state(self.element_id.clone(), |element_state, cx| {
let mut element_state =
element_state.map(|element_state| element_state.unwrap_or_default());
@@ -2078,7 +2078,7 @@ impl Interactivity {
&self,
hitbox: Option<&Hitbox>,
element_state: Option<&mut InteractiveElementState>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Style {
let mut style = Style::default();
style.refine(&self.base_style);
@@ -2119,7 +2119,7 @@ impl Interactivity {
if let Some(drag) = cx.active_drag.take() {
let mut can_drop = true;
if let Some(can_drop_predicate) = &self.can_drop_predicate {
- can_drop = can_drop_predicate(drag.value.as_ref(), cx.deref_mut());
+ can_drop = can_drop_predicate(drag.value.as_ref(), cx);
}
if can_drop {
@@ -2264,7 +2264,7 @@ where
type RequestLayoutState = E::RequestLayoutState;
type PrepaintState = E::PrepaintState;
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
self.element.request_layout(cx)
}
@@ -2272,7 +2272,7 @@ where
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> E::PrepaintState {
self.element.prepaint(bounds, state, cx)
}
@@ -2282,7 +2282,7 @@ where
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
self.element.paint(bounds, request_layout, prepaint, cx)
}
@@ -2347,7 +2347,7 @@ where
type RequestLayoutState = E::RequestLayoutState;
type PrepaintState = E::PrepaintState;
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
self.element.request_layout(cx)
}
@@ -2355,7 +2355,7 @@ where
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> E::PrepaintState {
self.element.prepaint(bounds, state, cx)
}
@@ -2365,7 +2365,7 @@ where
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
self.element.paint(bounds, request_layout, prepaint, cx);
}
@@ -3,9 +3,9 @@ use std::path::PathBuf;
use std::sync::Arc;
use crate::{
- point, px, size, AbsoluteLength, Asset, Bounds, DefiniteLength, DevicePixels, Element,
- ElementContext, Hitbox, ImageData, InteractiveElement, Interactivity, IntoElement, LayoutId,
- Length, Pixels, SharedUri, Size, StyleRefinement, Styled, SvgSize, UriOrPath, WindowContext,
+ point, px, size, AbsoluteLength, Asset, Bounds, DefiniteLength, DevicePixels, Element, Hitbox,
+ ImageData, InteractiveElement, Interactivity, IntoElement, LayoutId, Length, Pixels, SharedUri,
+ Size, StyleRefinement, Styled, SvgSize, UriOrPath, WindowContext,
};
use futures::{AsyncReadExt, Future};
use image::{ImageBuffer, ImageError};
@@ -232,7 +232,7 @@ impl Element for Img {
type RequestLayoutState = ();
type PrepaintState = Option<Hitbox>;
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let layout_id = self.interactivity.request_layout(cx, |mut style, cx| {
if let Some(data) = self.source.data(cx) {
let image_size = data.size();
@@ -260,7 +260,7 @@ impl Element for Img {
&mut self,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<Hitbox> {
self.interactivity
.prepaint(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
@@ -271,7 +271,7 @@ impl Element for Img {
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
hitbox: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let source = self.source.clone();
self.interactivity
@@ -319,7 +319,7 @@ impl InteractiveElement for Img {
}
impl ImageSource {
- fn data(&self, cx: &mut ElementContext) -> Option<Arc<ImageData>> {
+ fn data(&self, cx: &mut WindowContext) -> Option<Arc<ImageData>> {
match self {
ImageSource::Uri(_) | ImageSource::File(_) => {
let uri_or_path: UriOrPath = match self {
@@ -8,8 +8,8 @@
use crate::{
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges,
- Element, ElementContext, FocusHandle, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent,
- Size, Style, StyleRefinement, Styled, WindowContext,
+ Element, FocusHandle, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style,
+ StyleRefinement, Styled, WindowContext,
};
use collections::VecDeque;
use refineable::Refineable as _;
@@ -434,7 +434,7 @@ impl StateInner {
available_width: Option<Pixels>,
available_height: Pixels,
padding: &Edges<Pixels>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> LayoutItemsResponse {
let old_items = self.items.clone();
let mut measured_items = VecDeque::new();
@@ -609,7 +609,7 @@ impl StateInner {
bounds: Bounds<Pixels>,
padding: Edges<Pixels>,
autoscroll: bool,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Result<LayoutItemsResponse, ListOffset> {
cx.transact(|cx| {
let mut layout_response =
@@ -706,7 +706,7 @@ impl Element for List {
fn request_layout(
&mut self,
- cx: &mut crate::ElementContext,
+ cx: &mut crate::WindowContext,
) -> (crate::LayoutId, Self::RequestLayoutState) {
let layout_id = match self.sizing_behavior {
ListSizingBehavior::Infer => {
@@ -772,7 +772,7 @@ impl Element for List {
&mut self,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> ListPrepaintState {
let state = &mut *self.state.0.borrow_mut();
state.reset = false;
@@ -815,7 +815,7 @@ impl Element for List {
bounds: Bounds<crate::Pixels>,
_: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
- cx: &mut crate::ElementContext,
+ cx: &mut crate::WindowContext,
) {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
for item in &mut prepaint.layout.item_layouts {
@@ -951,11 +951,9 @@ mod test {
});
// Paint
- cx.draw(
- point(px(0.), px(0.)),
- size(px(100.), px(20.)).into(),
- |_| list(state.clone()).w_full().h_full().into_any(),
- );
+ cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_| {
+ list(state.clone()).w_full().h_full()
+ });
// Reset
state.reset(5);
@@ -1,7 +1,7 @@
use crate::{
- geometry::Negate as _, point, px, radians, size, Bounds, Element, ElementContext, Hitbox,
- InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString,
- Size, StyleRefinement, Styled, TransformationMatrix,
+ geometry::Negate as _, point, px, radians, size, Bounds, Element, Hitbox, InteractiveElement,
+ Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString, Size,
+ StyleRefinement, Styled, TransformationMatrix, WindowContext,
};
use util::ResultExt;
@@ -40,7 +40,7 @@ impl Element for Svg {
type RequestLayoutState = ();
type PrepaintState = Option<Hitbox>;
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let layout_id = self
.interactivity
.request_layout(cx, |style, cx| cx.request_layout(&style, None));
@@ -51,7 +51,7 @@ impl Element for Svg {
&mut self,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<Hitbox> {
self.interactivity
.prepaint(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
@@ -62,7 +62,7 @@ impl Element for Svg {
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) where
Self: Sized,
{
@@ -1,8 +1,7 @@
use crate::{
- ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementContext, ElementId,
- HighlightStyle, Hitbox, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
- Pixels, Point, SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine,
- TOOLTIP_DELAY,
+ ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementId, HighlightStyle,
+ Hitbox, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point,
+ SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine, TOOLTIP_DELAY,
};
use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard};
@@ -20,7 +19,7 @@ impl Element for &'static str {
type RequestLayoutState = TextState;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let mut state = TextState::default();
let layout_id = state.layout(SharedString::from(*self), None, cx);
(layout_id, state)
@@ -30,7 +29,7 @@ impl Element for &'static str {
&mut self,
_bounds: Bounds<Pixels>,
_text_state: &mut Self::RequestLayoutState,
- _cx: &mut ElementContext,
+ _cx: &mut WindowContext,
) {
}
@@ -39,7 +38,7 @@ impl Element for &'static str {
bounds: Bounds<Pixels>,
text_state: &mut TextState,
_: &mut (),
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
text_state.paint(bounds, self, cx)
}
@@ -65,7 +64,7 @@ impl Element for SharedString {
type RequestLayoutState = TextState;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let mut state = TextState::default();
let layout_id = state.layout(self.clone(), None, cx);
(layout_id, state)
@@ -75,7 +74,7 @@ impl Element for SharedString {
&mut self,
_bounds: Bounds<Pixels>,
_text_state: &mut Self::RequestLayoutState,
- _cx: &mut ElementContext,
+ _cx: &mut WindowContext,
) {
}
@@ -84,7 +83,7 @@ impl Element for SharedString {
bounds: Bounds<Pixels>,
text_state: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let text_str: &str = self.as_ref();
text_state.paint(bounds, text_str, cx)
@@ -151,7 +150,7 @@ impl Element for StyledText {
type RequestLayoutState = TextState;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let mut state = TextState::default();
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
(layout_id, state)
@@ -161,7 +160,7 @@ impl Element for StyledText {
&mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::RequestLayoutState,
- _cx: &mut ElementContext,
+ _cx: &mut WindowContext,
) {
}
@@ -170,7 +169,7 @@ impl Element for StyledText {
bounds: Bounds<Pixels>,
text_state: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
text_state.paint(bounds, &self.text, cx)
}
@@ -204,7 +203,7 @@ impl TextState {
&mut self,
text: SharedString,
runs: Option<Vec<TextRun>>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> LayoutId {
let text_style = cx.text_style();
let font_size = text_style.font_size.to_pixels(cx.rem_size());
@@ -279,7 +278,7 @@ impl TextState {
layout_id
}
- fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut ElementContext) {
+ fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
let element_state = self.lock();
let element_state = element_state
.as_ref()
@@ -405,7 +404,7 @@ impl Element for InteractiveText {
type RequestLayoutState = TextState;
type PrepaintState = Hitbox;
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
self.text.request_layout(cx)
}
@@ -413,7 +412,7 @@ impl Element for InteractiveText {
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Hitbox {
cx.with_element_state::<InteractiveTextState, _>(
Some(self.element_id.clone()),
@@ -442,7 +441,7 @@ impl Element for InteractiveText {
bounds: Bounds<Pixels>,
text_state: &mut Self::RequestLayoutState,
hitbox: &mut Hitbox,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
cx.with_element_state::<InteractiveTextState, _>(
Some(self.element_id.clone()),
@@ -5,9 +5,9 @@
//! elements with uniform height.
use crate::{
- point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext,
- ElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Render,
- ScrollHandle, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
+ point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementId, Hitbox,
+ InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Render, ScrollHandle, Size,
+ StyleRefinement, Styled, View, ViewContext, WindowContext,
};
use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -107,7 +107,7 @@ impl Element for UniformList {
type RequestLayoutState = UniformListFrameState;
type PrepaintState = Option<Hitbox>;
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let max_items = self.item_count;
let item_size = self.measure_item(None, cx);
let layout_id = self.interactivity.request_layout(cx, |style, cx| {
@@ -141,7 +141,7 @@ impl Element for UniformList {
&mut self,
bounds: Bounds<Pixels>,
frame_state: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<Hitbox> {
let style = self.interactivity.compute_style(None, cx);
let border = style.border_widths.to_pixels(cx.rem_size());
@@ -239,7 +239,7 @@ impl Element for UniformList {
bounds: Bounds<crate::Pixels>,
request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
self.interactivity
.paint(bounds, hitbox.as_ref(), cx, |_, cx| {
@@ -265,7 +265,7 @@ impl UniformList {
self
}
- fn measure_item(&self, list_width: Option<Pixels>, cx: &mut ElementContext) -> Size<Pixels> {
+ fn measure_item(&self, list_width: Option<Pixels>, cx: &mut WindowContext) -> Size<Pixels> {
if self.item_count == 0 {
return Size::default();
}
@@ -50,9 +50,8 @@
/// KeyBinding::new("cmd-k left", pane::SplitLeft, Some("Pane"))
///
use crate::{
- Action, ActionRegistry, DispatchPhase, ElementContext, EntityId, FocusId, KeyBinding,
- KeyContext, Keymap, KeymatchResult, Keystroke, KeystrokeMatcher, ModifiersChangedEvent,
- WindowContext,
+ Action, ActionRegistry, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, Keymap,
+ KeymatchResult, Keystroke, KeystrokeMatcher, ModifiersChangedEvent, WindowContext,
};
use collections::FxHashMap;
use smallvec::SmallVec;
@@ -107,8 +106,8 @@ impl ReusedSubtree {
}
}
-type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut ElementContext)>;
-type ModifiersChangedListener = Rc<dyn Fn(&ModifiersChangedEvent, &mut ElementContext)>;
+type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
+type ModifiersChangedListener = Rc<dyn Fn(&ModifiersChangedEvent, &mut WindowContext)>;
#[derive(Clone)]
pub(crate) struct DispatchActionListener {
@@ -2,9 +2,9 @@ use std::{iter, mem, ops::Range};
use crate::{
black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
- CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font, FontFeatures,
- FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size,
- SizeRefinement, Styled, TextRun,
+ CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font, FontFeatures, FontStyle, FontWeight,
+ Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size, SizeRefinement, Styled,
+ TextRun, WindowContext,
};
use collections::HashSet;
use refineable::Refineable;
@@ -391,8 +391,8 @@ impl Style {
pub fn paint(
&self,
bounds: Bounds<Pixels>,
- cx: &mut ElementContext,
- continuation: impl FnOnce(&mut ElementContext),
+ cx: &mut WindowContext,
+ continuation: impl FnOnce(&mut WindowContext),
) {
#[cfg(debug_assertions)]
if self.debug_below {
@@ -1,6 +1,6 @@
use crate::{
- black, fill, point, px, size, Bounds, ElementContext, Hsla, LineLayout, Pixels, Point, Result,
- SharedString, StrikethroughStyle, UnderlineStyle, WrapBoundary, WrappedLineLayout,
+ black, fill, point, px, size, Bounds, Hsla, LineLayout, Pixels, Point, Result, SharedString,
+ StrikethroughStyle, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
};
use derive_more::{Deref, DerefMut};
use smallvec::SmallVec;
@@ -48,7 +48,7 @@ impl ShapedLine {
&self,
origin: Point<Pixels>,
line_height: Pixels,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Result<()> {
paint_line(
origin,
@@ -86,7 +86,7 @@ impl WrappedLine {
&self,
origin: Point<Pixels>,
line_height: Pixels,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Result<()> {
paint_line(
origin,
@@ -107,7 +107,7 @@ fn paint_line(
line_height: Pixels,
decoration_runs: &[DecorationRun],
wrap_boundaries: &[WrapBoundary],
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Result<()> {
let line_bounds = Bounds::new(origin, size(layout.width, line_height));
cx.paint_layer(line_bounds, |cx| {
@@ -1,8 +1,8 @@
use crate::{
seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds, ContentMask, Element,
- ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement,
- LayoutId, Model, PaintIndex, Pixels, PrepaintStateIndex, Render, Style, StyleRefinement,
- TextStyle, ViewContext, VisualContext, WeakModel,
+ ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement, LayoutId, Model,
+ PaintIndex, Pixels, PrepaintStateIndex, Render, Style, StyleRefinement, TextStyle, ViewContext,
+ VisualContext, WeakModel, WindowContext,
};
use anyhow::{Context, Result};
use refineable::Refineable;
@@ -93,7 +93,7 @@ impl<V: Render> Element for View<V> {
type RequestLayoutState = AnyElement;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
cx.with_element_id(Some(ElementId::View(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);
@@ -105,7 +105,7 @@ impl<V: Render> Element for View<V> {
&mut self,
_: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
cx.set_view_id(self.entity_id());
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
@@ -118,7 +118,7 @@ impl<V: Render> Element for View<V> {
_: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
element.paint(cx)
@@ -220,7 +220,7 @@ impl<V> Eq for WeakView<V> {}
#[derive(Clone, Debug)]
pub struct AnyView {
model: AnyModel,
- render: fn(&AnyView, &mut ElementContext) -> AnyElement,
+ render: fn(&AnyView, &mut WindowContext) -> AnyElement,
cached_style: Option<StyleRefinement>,
}
@@ -279,7 +279,7 @@ impl Element for AnyView {
type RequestLayoutState = Option<AnyElement>;
type PrepaintState = Option<AnyElement>;
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
if let Some(style) = self.cached_style.as_ref() {
let mut root_style = Style::default();
root_style.refine(style);
@@ -298,7 +298,7 @@ impl Element for AnyView {
&mut self,
bounds: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<AnyElement> {
cx.set_view_id(self.entity_id());
if self.cached_style.is_some() {
@@ -359,7 +359,7 @@ impl Element for AnyView {
_bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
element: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
if self.cached_style.is_some() {
cx.with_element_state::<AnyViewState, _>(
@@ -408,7 +408,7 @@ impl IntoElement for AnyView {
/// A weak, dynamically-typed view handle that does not prevent the view from being released.
pub struct AnyWeakView {
model: AnyWeakModel,
- render: fn(&AnyView, &mut ElementContext) -> AnyElement,
+ render: fn(&AnyView, &mut WindowContext) -> AnyElement,
}
impl AnyWeakView {
@@ -447,11 +447,11 @@ impl std::fmt::Debug for AnyWeakView {
}
mod any_view {
- use crate::{AnyElement, AnyView, ElementContext, IntoElement, Render};
+ use crate::{AnyElement, AnyView, IntoElement, Render, WindowContext};
pub(crate) fn render<V: 'static + Render>(
view: &AnyView,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> AnyElement {
let view = view.clone().downcast::<V>().unwrap();
view.update(cx, |view, cx| view.render(cx).into_any_element())
@@ -1,32 +1,42 @@
use crate::{
- point, px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena,
- AsyncWindowContext, Bounds, Context, Corners, CursorStyle, DevicePixels,
- DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity,
- EntityId, EventEmitter, FileDropEvent, Flatten, Global, GlobalElementId, Hsla, KeyBinding,
- KeyDownEvent, KeyMatch, KeymatchResult, Keystroke, KeystrokeEvent, Model, ModelContext,
- Modifiers, ModifiersChangedEvent, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels,
- PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render,
- ScaledPixels, SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task,
- TextStyle, TextStyleRefinement, View, VisualContext, WeakView, WindowAppearance,
- WindowBackgroundAppearance, WindowOptions, WindowParams, WindowTextSystem,
+ hash, point, prelude::*, px, size, transparent_black, Action, AnyDrag, AnyElement, AnyTooltip,
+ AnyView, AppContext, Arena, Asset, AsyncWindowContext, AvailableSpace, Bounds, BoxShadow,
+ Context, Corners, CursorStyle, DevicePixels, DispatchActionListener, DispatchNodeId,
+ DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten,
+ FontId, Global, GlobalElementId, GlyphId, Hsla, ImageData, InputHandler, IsZero, KeyBinding,
+ KeyContext, KeyDownEvent, KeyEvent, KeyMatch, KeymatchResult, Keystroke, KeystrokeEvent,
+ LayoutId, LineLayoutIndex, Model, ModelContext, Modifiers, ModifiersChangedEvent,
+ MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels,
+ PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
+ PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
+ RenderSvgParams, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style,
+ SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement,
+ TransformationMatrix, Underline, UnderlineStyle, View, VisualContext, WeakView,
+ WindowAppearance, WindowBackgroundAppearance, WindowOptions, WindowParams, WindowTextSystem,
+ SUBPIXEL_VARIANTS,
};
use anyhow::{anyhow, Context as _, Result};
-use collections::FxHashSet;
+use collections::{FxHashMap, FxHashSet};
use derive_more::{Deref, DerefMut};
use futures::channel::oneshot;
+use futures::{future::Shared, FutureExt};
+#[cfg(target_os = "macos")]
+use media::core_video::CVImageBuffer;
use parking_lot::RwLock;
use refineable::Refineable;
use slotmap::SlotMap;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
- borrow::{Borrow, BorrowMut},
+ borrow::{Borrow, BorrowMut, Cow},
cell::{Cell, RefCell},
+ cmp,
fmt::{Debug, Display},
future::Future,
hash::{Hash, Hasher},
marker::PhantomData,
mem,
+ ops::Range,
rc::Rc,
sync::{
atomic::{AtomicUsize, Ordering::SeqCst},
@@ -34,12 +44,11 @@ use std::{
},
time::{Duration, Instant},
};
+use util::post_inc;
use util::{measure, ResultExt};
-mod element_cx;
mod prompts;
-pub use element_cx::*;
pub use prompts::*;
/// Represents the two different phases when dispatching events.
@@ -269,6 +278,192 @@ pub struct DismissEvent;
type FrameCallback = Box<dyn FnOnce(&mut WindowContext)>;
+pub(crate) type AnyMouseListener =
+ Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+
+#[derive(Clone)]
+pub(crate) struct CursorStyleRequest {
+ pub(crate) hitbox_id: HitboxId,
+ pub(crate) style: CursorStyle,
+}
+
+/// An identifier for a [Hitbox].
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+pub struct HitboxId(usize);
+
+impl HitboxId {
+ /// Checks if the hitbox with this id is currently hovered.
+ pub fn is_hovered(&self, cx: &WindowContext) -> bool {
+ cx.window.mouse_hit_test.0.contains(self)
+ }
+}
+
+/// A rectangular region that potentially blocks hitboxes inserted prior.
+/// See [WindowContext::insert_hitbox] for more details.
+#[derive(Clone, Debug, Deref)]
+pub struct Hitbox {
+ /// A unique identifier for the hitbox.
+ pub id: HitboxId,
+ /// The bounds of the hitbox.
+ #[deref]
+ pub bounds: Bounds<Pixels>,
+ /// The content mask when the hitbox was inserted.
+ pub content_mask: ContentMask<Pixels>,
+ /// Whether the hitbox occludes other hitboxes inserted prior.
+ pub opaque: bool,
+}
+
+impl Hitbox {
+ /// Checks if the hitbox is currently hovered.
+ pub fn is_hovered(&self, cx: &WindowContext) -> bool {
+ self.id.is_hovered(cx)
+ }
+}
+
+#[derive(Default, Eq, PartialEq)]
+pub(crate) struct HitTest(SmallVec<[HitboxId; 8]>);
+
+/// An identifier for a tooltip.
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+pub struct TooltipId(usize);
+
+impl TooltipId {
+ /// Checks if the tooltip is currently hovered.
+ pub fn is_hovered(&self, cx: &WindowContext) -> bool {
+ cx.window
+ .tooltip_bounds
+ .as_ref()
+ .map_or(false, |tooltip_bounds| {
+ tooltip_bounds.id == *self && tooltip_bounds.bounds.contains(&cx.mouse_position())
+ })
+ }
+}
+
+pub(crate) struct TooltipBounds {
+ id: TooltipId,
+ bounds: Bounds<Pixels>,
+}
+
+#[derive(Clone)]
+pub(crate) struct TooltipRequest {
+ id: TooltipId,
+ tooltip: AnyTooltip,
+}
+
+pub(crate) struct DeferredDraw {
+ priority: usize,
+ parent_node: DispatchNodeId,
+ element_id_stack: GlobalElementId,
+ text_style_stack: Vec<TextStyleRefinement>,
+ element: Option<AnyElement>,
+ absolute_offset: Point<Pixels>,
+ prepaint_range: Range<PrepaintStateIndex>,
+ paint_range: Range<PaintIndex>,
+}
+
+pub(crate) struct Frame {
+ pub(crate) focus: Option<FocusId>,
+ pub(crate) window_active: bool,
+ pub(crate) element_states: FxHashMap<(GlobalElementId, TypeId), ElementStateBox>,
+ accessed_element_states: Vec<(GlobalElementId, TypeId)>,
+ pub(crate) mouse_listeners: Vec<Option<AnyMouseListener>>,
+ pub(crate) dispatch_tree: DispatchTree,
+ pub(crate) scene: Scene,
+ pub(crate) hitboxes: Vec<Hitbox>,
+ pub(crate) deferred_draws: Vec<DeferredDraw>,
+ pub(crate) input_handlers: Vec<Option<PlatformInputHandler>>,
+ pub(crate) tooltip_requests: Vec<Option<TooltipRequest>>,
+ pub(crate) cursor_styles: Vec<CursorStyleRequest>,
+ #[cfg(any(test, feature = "test-support"))]
+ pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct PrepaintStateIndex {
+ hitboxes_index: usize,
+ tooltips_index: usize,
+ deferred_draws_index: usize,
+ dispatch_tree_index: usize,
+ accessed_element_states_index: usize,
+ line_layout_index: LineLayoutIndex,
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct PaintIndex {
+ scene_index: usize,
+ mouse_listeners_index: usize,
+ input_handlers_index: usize,
+ cursor_styles_index: usize,
+ accessed_element_states_index: usize,
+ line_layout_index: LineLayoutIndex,
+}
+
+impl Frame {
+ pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
+ Frame {
+ focus: None,
+ window_active: false,
+ element_states: FxHashMap::default(),
+ accessed_element_states: Vec::new(),
+ mouse_listeners: Vec::new(),
+ dispatch_tree,
+ scene: Scene::default(),
+ hitboxes: Vec::new(),
+ deferred_draws: Vec::new(),
+ input_handlers: Vec::new(),
+ tooltip_requests: Vec::new(),
+ cursor_styles: Vec::new(),
+
+ #[cfg(any(test, feature = "test-support"))]
+ debug_bounds: FxHashMap::default(),
+ }
+ }
+
+ pub(crate) fn clear(&mut self) {
+ self.element_states.clear();
+ self.accessed_element_states.clear();
+ self.mouse_listeners.clear();
+ self.dispatch_tree.clear();
+ self.scene.clear();
+ self.input_handlers.clear();
+ self.tooltip_requests.clear();
+ self.cursor_styles.clear();
+ self.hitboxes.clear();
+ self.deferred_draws.clear();
+ }
+
+ pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
+ let mut hit_test = HitTest::default();
+ for hitbox in self.hitboxes.iter().rev() {
+ let bounds = hitbox.bounds.intersect(&hitbox.content_mask.bounds);
+ if bounds.contains(&position) {
+ hit_test.0.push(hitbox.id);
+ if hitbox.opaque {
+ break;
+ }
+ }
+ }
+ hit_test
+ }
+
+ pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
+ self.focus
+ .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
+ .unwrap_or_default()
+ }
+
+ pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
+ for element_state_key in &self.accessed_element_states {
+ if let Some(element_state) = prev_frame.element_states.remove(element_state_key) {
+ self.element_states
+ .insert(element_state_key.clone(), element_state);
+ }
+ }
+
+ self.scene.finish();
+ }
+}
+
// Holds the state for a specific window.
#[doc(hidden)]
pub struct Window {
@@ -321,7 +516,7 @@ pub struct Window {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum DrawPhase {
None,
- Layout,
+ Prepaint,
Paint,
Focus,
}
@@ -1039,7 +1234,7 @@ impl<'a> WindowContext<'a> {
.push(Some(input_handler));
}
- self.with_element_context(|cx| cx.draw_roots());
+ self.draw_roots();
self.window.dirty_views.clear();
self.window
@@ -1123,6 +1318,1431 @@ impl<'a> WindowContext<'a> {
profiling::finish_frame!();
}
+ fn draw_roots(&mut self) {
+ self.window.draw_phase = DrawPhase::Prepaint;
+ self.window.tooltip_bounds.take();
+
+ // Layout all root elements.
+ let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any();
+ root_element.prepaint_as_root(Point::default(), self.window.viewport_size.into(), self);
+
+ let mut sorted_deferred_draws =
+ (0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
+ sorted_deferred_draws.sort_by_key(|ix| self.window.next_frame.deferred_draws[*ix].priority);
+ self.prepaint_deferred_draws(&sorted_deferred_draws);
+
+ let mut prompt_element = None;
+ let mut active_drag_element = None;
+ let mut tooltip_element = None;
+ if let Some(prompt) = self.window.prompt.take() {
+ let mut element = prompt.view.any_view().into_any();
+ element.prepaint_as_root(Point::default(), self.window.viewport_size.into(), self);
+ prompt_element = Some(element);
+ self.window.prompt = Some(prompt);
+ } else if let Some(active_drag) = self.app.active_drag.take() {
+ let mut element = active_drag.view.clone().into_any();
+ let offset = self.mouse_position() - active_drag.cursor_offset;
+ element.prepaint_as_root(offset, AvailableSpace::min_size(), self);
+ active_drag_element = Some(element);
+ self.app.active_drag = Some(active_drag);
+ } else {
+ tooltip_element = self.prepaint_tooltip();
+ }
+
+ self.window.mouse_hit_test = self.window.next_frame.hit_test(self.window.mouse_position);
+
+ // Now actually paint the elements.
+ self.window.draw_phase = DrawPhase::Paint;
+ root_element.paint(self);
+
+ self.paint_deferred_draws(&sorted_deferred_draws);
+
+ if let Some(mut prompt_element) = prompt_element {
+ prompt_element.paint(self)
+ } else if let Some(mut drag_element) = active_drag_element {
+ drag_element.paint(self);
+ } else if let Some(mut tooltip_element) = tooltip_element {
+ tooltip_element.paint(self);
+ }
+ }
+
+ fn prepaint_tooltip(&mut self) -> Option<AnyElement> {
+ let tooltip_request = self.window.next_frame.tooltip_requests.last().cloned()?;
+ let tooltip_request = tooltip_request.unwrap();
+ let mut element = tooltip_request.tooltip.view.clone().into_any();
+ let mouse_position = tooltip_request.tooltip.mouse_position;
+ let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self);
+
+ let mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
+ let window_bounds = Bounds {
+ origin: Point::default(),
+ size: self.viewport_size(),
+ };
+
+ if tooltip_bounds.right() > window_bounds.right() {
+ let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
+ if new_x >= Pixels::ZERO {
+ tooltip_bounds.origin.x = new_x;
+ } else {
+ tooltip_bounds.origin.x = cmp::max(
+ Pixels::ZERO,
+ tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
+ );
+ }
+ }
+
+ if tooltip_bounds.bottom() > window_bounds.bottom() {
+ let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
+ if new_y >= Pixels::ZERO {
+ tooltip_bounds.origin.y = new_y;
+ } else {
+ tooltip_bounds.origin.y = cmp::max(
+ Pixels::ZERO,
+ tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
+ );
+ }
+ }
+
+ self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.prepaint(cx));
+
+ self.window.tooltip_bounds = Some(TooltipBounds {
+ id: tooltip_request.id,
+ bounds: tooltip_bounds,
+ });
+ Some(element)
+ }
+
+ fn prepaint_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
+ assert_eq!(self.window.element_id_stack.len(), 0);
+
+ let mut deferred_draws = mem::take(&mut self.window.next_frame.deferred_draws);
+ for deferred_draw_ix in deferred_draw_indices {
+ let deferred_draw = &mut deferred_draws[*deferred_draw_ix];
+ self.window.element_id_stack = deferred_draw.element_id_stack.clone();
+ self.window.text_style_stack = deferred_draw.text_style_stack.clone();
+ self.window
+ .next_frame
+ .dispatch_tree
+ .set_active_node(deferred_draw.parent_node);
+
+ let prepaint_start = self.prepaint_index();
+ if let Some(element) = deferred_draw.element.as_mut() {
+ self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| {
+ element.prepaint(cx)
+ });
+ } else {
+ self.reuse_prepaint(deferred_draw.prepaint_range.clone());
+ }
+ let prepaint_end = self.prepaint_index();
+ deferred_draw.prepaint_range = prepaint_start..prepaint_end;
+ }
+ assert_eq!(
+ self.window.next_frame.deferred_draws.len(),
+ 0,
+ "cannot call defer_draw during deferred drawing"
+ );
+ self.window.next_frame.deferred_draws = deferred_draws;
+ self.window.element_id_stack.clear();
+ self.window.text_style_stack.clear();
+ }
+
+ fn paint_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
+ assert_eq!(self.window.element_id_stack.len(), 0);
+
+ let mut deferred_draws = mem::take(&mut self.window.next_frame.deferred_draws);
+ for deferred_draw_ix in deferred_draw_indices {
+ let mut deferred_draw = &mut deferred_draws[*deferred_draw_ix];
+ self.window.element_id_stack = deferred_draw.element_id_stack.clone();
+ self.window
+ .next_frame
+ .dispatch_tree
+ .set_active_node(deferred_draw.parent_node);
+
+ let paint_start = self.paint_index();
+ if let Some(element) = deferred_draw.element.as_mut() {
+ element.paint(self);
+ } else {
+ self.reuse_paint(deferred_draw.paint_range.clone());
+ }
+ let paint_end = self.paint_index();
+ deferred_draw.paint_range = paint_start..paint_end;
+ }
+ self.window.next_frame.deferred_draws = deferred_draws;
+ self.window.element_id_stack.clear();
+ }
+
+ pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex {
+ PrepaintStateIndex {
+ hitboxes_index: self.window.next_frame.hitboxes.len(),
+ tooltips_index: self.window.next_frame.tooltip_requests.len(),
+ deferred_draws_index: self.window.next_frame.deferred_draws.len(),
+ dispatch_tree_index: self.window.next_frame.dispatch_tree.len(),
+ accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
+ line_layout_index: self.window.text_system.layout_index(),
+ }
+ }
+
+ pub(crate) fn reuse_prepaint(&mut self, range: Range<PrepaintStateIndex>) {
+ let window = &mut self.window;
+ window.next_frame.hitboxes.extend(
+ window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
+ .iter()
+ .cloned(),
+ );
+ window.next_frame.tooltip_requests.extend(
+ window.rendered_frame.tooltip_requests
+ [range.start.tooltips_index..range.end.tooltips_index]
+ .iter_mut()
+ .map(|request| request.take()),
+ );
+ window.next_frame.accessed_element_states.extend(
+ window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
+ ..range.end.accessed_element_states_index]
+ .iter()
+ .cloned(),
+ );
+ window
+ .text_system
+ .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
+
+ let reused_subtree = window.next_frame.dispatch_tree.reuse_subtree(
+ range.start.dispatch_tree_index..range.end.dispatch_tree_index,
+ &mut window.rendered_frame.dispatch_tree,
+ );
+ window.next_frame.deferred_draws.extend(
+ window.rendered_frame.deferred_draws
+ [range.start.deferred_draws_index..range.end.deferred_draws_index]
+ .iter()
+ .map(|deferred_draw| DeferredDraw {
+ parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
+ element_id_stack: deferred_draw.element_id_stack.clone(),
+ text_style_stack: deferred_draw.text_style_stack.clone(),
+ priority: deferred_draw.priority,
+ element: None,
+ absolute_offset: deferred_draw.absolute_offset,
+ prepaint_range: deferred_draw.prepaint_range.clone(),
+ paint_range: deferred_draw.paint_range.clone(),
+ }),
+ );
+ }
+
+ pub(crate) fn paint_index(&self) -> PaintIndex {
+ PaintIndex {
+ scene_index: self.window.next_frame.scene.len(),
+ mouse_listeners_index: self.window.next_frame.mouse_listeners.len(),
+ input_handlers_index: self.window.next_frame.input_handlers.len(),
+ cursor_styles_index: self.window.next_frame.cursor_styles.len(),
+ accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
+ line_layout_index: self.window.text_system.layout_index(),
+ }
+ }
+
+ pub(crate) fn reuse_paint(&mut self, range: Range<PaintIndex>) {
+ let window = &mut self.window;
+
+ window.next_frame.cursor_styles.extend(
+ window.rendered_frame.cursor_styles
+ [range.start.cursor_styles_index..range.end.cursor_styles_index]
+ .iter()
+ .cloned(),
+ );
+ window.next_frame.input_handlers.extend(
+ window.rendered_frame.input_handlers
+ [range.start.input_handlers_index..range.end.input_handlers_index]
+ .iter_mut()
+ .map(|handler| handler.take()),
+ );
+ window.next_frame.mouse_listeners.extend(
+ window.rendered_frame.mouse_listeners
+ [range.start.mouse_listeners_index..range.end.mouse_listeners_index]
+ .iter_mut()
+ .map(|listener| listener.take()),
+ );
+ window.next_frame.accessed_element_states.extend(
+ window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
+ ..range.end.accessed_element_states_index]
+ .iter()
+ .cloned(),
+ );
+ window
+ .text_system
+ .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
+ window.next_frame.scene.replay(
+ range.start.scene_index..range.end.scene_index,
+ &window.rendered_frame.scene,
+ );
+ }
+
+ /// Push a text style onto the stack, and call a function with that style active.
+ /// Use [`AppContext::text_style`] to get the current, combined text style. This method
+ /// should only be called as part of element drawing.
+ pub fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
+ where
+ F: FnOnce(&mut Self) -> R,
+ {
+ debug_assert!(
+ matches!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint | DrawPhase::Paint
+ ),
+ "this method can only be called during request_layout, prepaint, or paint"
+ );
+ if let Some(style) = style {
+ self.window.text_style_stack.push(style);
+ let result = f(self);
+ self.window.text_style_stack.pop();
+ result
+ } else {
+ f(self)
+ }
+ }
+
+ /// Updates the cursor style at the platform level. This method should only be called
+ /// during the prepaint phase of element drawing.
+ pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+ self.window
+ .next_frame
+ .cursor_styles
+ .push(CursorStyleRequest {
+ hitbox_id: hitbox.id,
+ style,
+ });
+ }
+
+ /// Sets a tooltip to be rendered for the upcoming frame. This method should only be called
+ /// during the paint phase of element drawing.
+ pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during prepaint"
+ );
+ let id = TooltipId(post_inc(&mut self.window.next_tooltip_id.0));
+ self.window
+ .next_frame
+ .tooltip_requests
+ .push(Some(TooltipRequest { id, tooltip }));
+ id
+ }
+
+ /// Pushes the given element id onto the global stack and invokes the given closure
+ /// with a `GlobalElementId`, which disambiguates the given id in the context of its ancestor
+ /// ids. Because elements are discarded and recreated on each frame, the `GlobalElementId` is
+ /// used to associate state with identified elements across separate frames. This method should
+ /// only be called as part of element drawing.
+ pub fn with_element_id<R>(
+ &mut self,
+ id: Option<impl Into<ElementId>>,
+ f: impl FnOnce(&mut Self) -> R,
+ ) -> R {
+ debug_assert!(
+ matches!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint | DrawPhase::Paint
+ ),
+ "this method can only be called during request_layout, prepaint, or paint"
+ );
+ if let Some(id) = id.map(Into::into) {
+ let window = self.window_mut();
+ window.element_id_stack.push(id);
+ let result = f(self);
+ let window: &mut Window = self.borrow_mut();
+ window.element_id_stack.pop();
+ result
+ } else {
+ f(self)
+ }
+ }
+
+ /// Invoke the given function with the given content mask after intersecting it
+ /// with the current mask. This method should only be called during element drawing.
+ pub fn with_content_mask<R>(
+ &mut self,
+ mask: Option<ContentMask<Pixels>>,
+ f: impl FnOnce(&mut Self) -> R,
+ ) -> R {
+ debug_assert!(
+ matches!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint | DrawPhase::Paint
+ ),
+ "this method can only be called during request_layout, prepaint, or paint"
+ );
+ if let Some(mask) = mask {
+ let mask = mask.intersect(&self.content_mask());
+ self.window_mut().content_mask_stack.push(mask);
+ let result = f(self);
+ self.window_mut().content_mask_stack.pop();
+ result
+ } else {
+ f(self)
+ }
+ }
+
+ /// Updates the global element offset relative to the current offset. This is used to implement
+ /// scrolling. This method should only be called during the prepaint phase of element drawing.
+ pub fn with_element_offset<R>(
+ &mut self,
+ offset: Point<Pixels>,
+ f: impl FnOnce(&mut Self) -> R,
+ ) -> R {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during request_layout, or prepaint"
+ );
+
+ if offset.is_zero() {
+ return f(self);
+ };
+
+ let abs_offset = self.element_offset() + offset;
+ self.with_absolute_element_offset(abs_offset, f)
+ }
+
+ /// Updates the global element offset based on the given offset. This is used to implement
+ /// drag handles and other manual painting of elements. This method should only be called during
+ /// the prepaint phase of element drawing.
+ pub fn with_absolute_element_offset<R>(
+ &mut self,
+ offset: Point<Pixels>,
+ f: impl FnOnce(&mut Self) -> R,
+ ) -> R {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during request_layout, or prepaint"
+ );
+ self.window_mut().element_offset_stack.push(offset);
+ let result = f(self);
+ self.window_mut().element_offset_stack.pop();
+ result
+ }
+
+ /// Perform prepaint on child elements in a "retryable" manner, so that any side effects
+ /// of prepaints can be discarded before prepainting again. This is used to support autoscroll
+ /// where we need to prepaint children to detect the autoscroll bounds, then adjust the
+ /// element offset and prepaint again. See [`List`] for an example. This method should only be
+ /// called during the prepaint phase of element drawing.
+ pub fn transact<T, U>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, U>) -> Result<T, U> {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during prepaint"
+ );
+ let index = self.prepaint_index();
+ let result = f(self);
+ if result.is_err() {
+ self.window
+ .next_frame
+ .hitboxes
+ .truncate(index.hitboxes_index);
+ self.window
+ .next_frame
+ .tooltip_requests
+ .truncate(index.tooltips_index);
+ self.window
+ .next_frame
+ .deferred_draws
+ .truncate(index.deferred_draws_index);
+ self.window
+ .next_frame
+ .dispatch_tree
+ .truncate(index.dispatch_tree_index);
+ self.window
+ .next_frame
+ .accessed_element_states
+ .truncate(index.accessed_element_states_index);
+ self.window
+ .text_system
+ .truncate_layouts(index.line_layout_index);
+ }
+ result
+ }
+
+ /// When you call this method during [`prepaint`], containing elements will attempt to
+ /// scroll to cause the specified bounds to become visible. When they decide to autoscroll, they will call
+ /// [`prepaint`] again with a new set of bounds. See [`List`] for an example of an element
+ /// that supports this method being called on the elements it contains. This method should only be
+ /// called during the prepaint phase of element drawing.
+ pub fn request_autoscroll(&mut self, bounds: Bounds<Pixels>) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during prepaint"
+ );
+ self.window.requested_autoscroll = Some(bounds);
+ }
+
+ /// This method can be called from a containing element such as [`List`] to support the autoscroll behavior
+ /// described in [`request_autoscroll`].
+ pub fn take_autoscroll(&mut self) -> Option<Bounds<Pixels>> {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during prepaint"
+ );
+ self.window.requested_autoscroll.take()
+ }
+
+ /// Remove an asset from GPUI's cache
+ pub fn remove_cached_asset<A: Asset + 'static>(
+ &mut self,
+ source: &A::Source,
+ ) -> Option<A::Output> {
+ self.asset_cache.remove::<A>(source)
+ }
+
+ /// Asynchronously load an asset, if the asset hasn't finished loading this will return None.
+ /// Your view will be re-drawn once the asset has finished loading.
+ ///
+ /// Note that the multiple calls to this method will only result in one `Asset::load` call.
+ /// The results of that call will be cached, and returned on subsequent uses of this API.
+ ///
+ /// Use [Self::remove_cached_asset] to reload your asset.
+ pub fn use_cached_asset<A: Asset + 'static>(
+ &mut self,
+ source: &A::Source,
+ ) -> Option<A::Output> {
+ self.asset_cache.get::<A>(source).or_else(|| {
+ if let Some(asset) = self.use_asset::<A>(source) {
+ self.asset_cache
+ .insert::<A>(source.to_owned(), asset.clone());
+ Some(asset)
+ } else {
+ None
+ }
+ })
+ }
+
+ /// Asynchronously load an asset, if the asset hasn't finished loading this will return None.
+ /// Your view will be re-drawn once the asset has finished loading.
+ ///
+ /// Note that the multiple calls to this method will only result in one `Asset::load` call at a
+ /// time.
+ ///
+ /// This asset will not be cached by default, see [Self::use_cached_asset]
+ pub fn use_asset<A: Asset + 'static>(&mut self, source: &A::Source) -> Option<A::Output> {
+ let asset_id = (TypeId::of::<A>(), hash(source));
+ let mut is_first = false;
+ let task = self
+ .loading_assets
+ .remove(&asset_id)
+ .map(|boxed_task| *boxed_task.downcast::<Shared<Task<A::Output>>>().unwrap())
+ .unwrap_or_else(|| {
+ is_first = true;
+ let future = A::load(source.clone(), self);
+ let task = self.background_executor().spawn(future).shared();
+ task
+ });
+
+ task.clone().now_or_never().or_else(|| {
+ if is_first {
+ let parent_id = self.parent_view_id();
+ self.spawn({
+ let task = task.clone();
+ |mut cx| async move {
+ task.await;
+
+ cx.on_next_frame(move |cx| {
+ if let Some(parent_id) = parent_id {
+ cx.notify(parent_id)
+ } else {
+ cx.refresh()
+ }
+ });
+ }
+ })
+ .detach();
+ }
+
+ self.loading_assets.insert(asset_id, Box::new(task));
+
+ None
+ })
+ }
+
+ /// Obtain the current element offset. This method should only be called during the
+ /// prepaint phase of element drawing.
+ pub fn element_offset(&self) -> Point<Pixels> {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during prepaint"
+ );
+ self.window()
+ .element_offset_stack
+ .last()
+ .copied()
+ .unwrap_or_default()
+ }
+
+ /// Obtain the current content mask. This method should only be called during element drawing.
+ pub fn content_mask(&self) -> ContentMask<Pixels> {
+ debug_assert!(
+ matches!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint | DrawPhase::Paint
+ ),
+ "this method can only be called during prepaint, or paint"
+ );
+ self.window()
+ .content_mask_stack
+ .last()
+ .cloned()
+ .unwrap_or_else(|| ContentMask {
+ bounds: Bounds {
+ origin: Point::default(),
+ size: self.window().viewport_size,
+ },
+ })
+ }
+
+ /// Updates or initializes state for an element with the given id that lives across multiple
+ /// frames. If an element with this ID existed in the rendered frame, its state will be passed
+ /// to the given closure. The state returned by the closure will be stored so it can be referenced
+ /// when drawing the next frame. This method should only be called as part of element drawing.
+ pub fn with_element_state<S, R>(
+ &mut self,
+ element_id: Option<ElementId>,
+ f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
+ ) -> R
+ where
+ S: 'static,
+ {
+ debug_assert!(
+ matches!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint | DrawPhase::Paint
+ ),
+ "this method can only be called during request_layout, prepaint, or paint"
+ );
+ let id_is_none = element_id.is_none();
+ self.with_element_id(element_id, |cx| {
+ if id_is_none {
+ let (result, state) = f(None, cx);
+ debug_assert!(state.is_none(), "you must not return an element state when passing None for the element id");
+ result
+ } else {
+ let global_id = cx.window().element_id_stack.clone();
+ let key = (global_id, TypeId::of::<S>());
+ cx.window.next_frame.accessed_element_states.push(key.clone());
+
+ if let Some(any) = cx
+ .window_mut()
+ .next_frame
+ .element_states
+ .remove(&key)
+ .or_else(|| {
+ cx.window_mut()
+ .rendered_frame
+ .element_states
+ .remove(&key)
+ })
+ {
+ 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::anyhow!(
+ "invalid element state type for id, requested_type {:?}, actual type: {:?}",
+ std::any::type_name::<S>(),
+ type_name
+ )
+ }
+
+ #[cfg(not(debug_assertions))]
+ {
+ anyhow::anyhow!(
+ "invalid element state type for id, requested_type {:?}",
+ std::any::type_name::<S>(),
+ )
+ }
+ })
+ .unwrap();
+
+ // Actual: Option<AnyElement> <- View
+ // Requested: () <- AnyElement
+ let state = state_box
+ .take()
+ .expect("reentrant call to with_element_state for the same state type and element id");
+ let (result, state) = f(Some(Some(state)), cx);
+ state_box.replace(state.expect("you must return "));
+ cx.window_mut()
+ .next_frame
+ .element_states
+ .insert(key, ElementStateBox {
+ inner: state_box,
+ #[cfg(debug_assertions)]
+ type_name
+ });
+ result
+ } else {
+ let (result, state) = f(Some(None), cx);
+ cx.window_mut()
+ .next_frame
+ .element_states
+ .insert(key,
+ ElementStateBox {
+ inner: Box::new(Some(state.expect("you must return Some<State> when you pass some element id"))),
+ #[cfg(debug_assertions)]
+ type_name: std::any::type_name::<S>()
+ }
+
+ );
+ result
+ }
+ }
+ })
+ }
+
+ /// Defers the drawing of the given element, scheduling it to be painted on top of the currently-drawn tree
+ /// at a later time. The `priority` parameter determines the drawing order relative to other deferred elements,
+ /// with higher values being drawn on top.
+ ///
+ /// This method should only be called as part of the prepaint phase of element drawing.
+ pub fn defer_draw(
+ &mut self,
+ element: AnyElement,
+ absolute_offset: Point<Pixels>,
+ priority: usize,
+ ) {
+ let window = &mut self.window;
+ debug_assert_eq!(
+ window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during request_layout or prepaint"
+ );
+ let parent_node = window.next_frame.dispatch_tree.active_node_id().unwrap();
+ window.next_frame.deferred_draws.push(DeferredDraw {
+ parent_node,
+ element_id_stack: window.element_id_stack.clone(),
+ text_style_stack: window.text_style_stack.clone(),
+ priority,
+ element: Some(element),
+ absolute_offset,
+ prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(),
+ paint_range: PaintIndex::default()..PaintIndex::default(),
+ });
+ }
+
+ /// Creates a new painting layer for the specified bounds. A "layer" is a batch
+ /// of geometry that are non-overlapping and have the same draw order. This is typically used
+ /// for performance reasons.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn paint_layer<R>(&mut self, bounds: Bounds<Pixels>, f: impl FnOnce(&mut Self) -> R) -> R {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let content_mask = self.content_mask();
+ let clipped_bounds = bounds.intersect(&content_mask.bounds);
+ if !clipped_bounds.is_empty() {
+ self.window
+ .next_frame
+ .scene
+ .push_layer(clipped_bounds.scale(scale_factor));
+ }
+
+ let result = f(self);
+
+ if !clipped_bounds.is_empty() {
+ self.window.next_frame.scene.pop_layer();
+ }
+
+ result
+ }
+
+ /// Paint one or more drop shadows into the scene for the next frame at the current z-index.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn paint_shadows(
+ &mut self,
+ bounds: Bounds<Pixels>,
+ corner_radii: Corners<Pixels>,
+ shadows: &[BoxShadow],
+ ) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let content_mask = self.content_mask();
+ for shadow in shadows {
+ let mut shadow_bounds = bounds;
+ shadow_bounds.origin += shadow.offset;
+ shadow_bounds.dilate(shadow.spread_radius);
+ self.window.next_frame.scene.insert_primitive(Shadow {
+ order: 0,
+ blur_radius: shadow.blur_radius.scale(scale_factor),
+ bounds: shadow_bounds.scale(scale_factor),
+ content_mask: content_mask.scale(scale_factor),
+ corner_radii: corner_radii.scale(scale_factor),
+ color: shadow.color,
+ });
+ }
+ }
+
+ /// Paint one or more quads into the scene for the next frame at the current stacking context.
+ /// Quads are colored rectangular regions with an optional background, border, and corner radius.
+ /// see [`fill`](crate::fill), [`outline`](crate::outline), and [`quad`](crate::quad) to construct this type.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn paint_quad(&mut self, quad: PaintQuad) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let content_mask = self.content_mask();
+ self.window.next_frame.scene.insert_primitive(Quad {
+ order: 0,
+ pad: 0,
+ bounds: quad.bounds.scale(scale_factor),
+ content_mask: content_mask.scale(scale_factor),
+ background: quad.background,
+ border_color: quad.border_color,
+ corner_radii: quad.corner_radii.scale(scale_factor),
+ border_widths: quad.border_widths.scale(scale_factor),
+ });
+ }
+
+ /// Paint the given `Path` into the scene for the next frame at the current z-index.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let content_mask = self.content_mask();
+ path.content_mask = content_mask;
+ path.color = color.into();
+ self.window
+ .next_frame
+ .scene
+ .insert_primitive(path.scale(scale_factor));
+ }
+
+ /// Paint an underline into the scene for the next frame at the current z-index.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn paint_underline(
+ &mut self,
+ origin: Point<Pixels>,
+ width: Pixels,
+ style: &UnderlineStyle,
+ ) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let height = if style.wavy {
+ style.thickness * 3.
+ } else {
+ style.thickness
+ };
+ let bounds = Bounds {
+ origin,
+ size: size(width, height),
+ };
+ let content_mask = self.content_mask();
+
+ self.window.next_frame.scene.insert_primitive(Underline {
+ order: 0,
+ pad: 0,
+ bounds: bounds.scale(scale_factor),
+ content_mask: content_mask.scale(scale_factor),
+ color: style.color.unwrap_or_default(),
+ thickness: style.thickness.scale(scale_factor),
+ wavy: style.wavy,
+ });
+ }
+
+ /// Paint a strikethrough into the scene for the next frame at the current z-index.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn paint_strikethrough(
+ &mut self,
+ origin: Point<Pixels>,
+ width: Pixels,
+ style: &StrikethroughStyle,
+ ) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let height = style.thickness;
+ let bounds = Bounds {
+ origin,
+ size: size(width, height),
+ };
+ let content_mask = self.content_mask();
+
+ self.window.next_frame.scene.insert_primitive(Underline {
+ order: 0,
+ pad: 0,
+ bounds: bounds.scale(scale_factor),
+ content_mask: content_mask.scale(scale_factor),
+ thickness: style.thickness.scale(scale_factor),
+ color: style.color.unwrap_or_default(),
+ wavy: false,
+ });
+ }
+
+ /// Paints a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
+ ///
+ /// The y component of the origin is the baseline of the glyph.
+ /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or
+ /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem).
+ /// This method is only useful if you need to paint a single glyph that has already been shaped.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn paint_glyph(
+ &mut self,
+ origin: Point<Pixels>,
+ font_id: FontId,
+ glyph_id: GlyphId,
+ font_size: Pixels,
+ color: Hsla,
+ ) -> Result<()> {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let glyph_origin = origin.scale(scale_factor);
+ let subpixel_variant = Point {
+ x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
+ y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
+ };
+ let params = RenderGlyphParams {
+ font_id,
+ glyph_id,
+ font_size,
+ subpixel_variant,
+ scale_factor,
+ is_emoji: false,
+ };
+
+ let raster_bounds = self.text_system().raster_bounds(¶ms)?;
+ if !raster_bounds.is_zero() {
+ let tile =
+ self.window
+ .sprite_atlas
+ .get_or_insert_with(¶ms.clone().into(), &mut || {
+ let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
+ Ok((size, Cow::Owned(bytes)))
+ })?;
+ let bounds = Bounds {
+ origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
+ size: tile.bounds.size.map(Into::into),
+ };
+ let content_mask = self.content_mask().scale(scale_factor);
+ self.window
+ .next_frame
+ .scene
+ .insert_primitive(MonochromeSprite {
+ order: 0,
+ pad: 0,
+ bounds,
+ content_mask,
+ color,
+ tile,
+ transformation: TransformationMatrix::unit(),
+ });
+ }
+ Ok(())
+ }
+
+ /// Paints an emoji glyph into the scene for the next frame at the current z-index.
+ ///
+ /// The y component of the origin is the baseline of the glyph.
+ /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or
+ /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem).
+ /// This method is only useful if you need to paint a single emoji that has already been shaped.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn paint_emoji(
+ &mut self,
+ origin: Point<Pixels>,
+ font_id: FontId,
+ glyph_id: GlyphId,
+ font_size: Pixels,
+ ) -> Result<()> {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let glyph_origin = origin.scale(scale_factor);
+ let params = RenderGlyphParams {
+ font_id,
+ glyph_id,
+ font_size,
+ // We don't render emojis with subpixel variants.
+ subpixel_variant: Default::default(),
+ scale_factor,
+ is_emoji: true,
+ };
+
+ let raster_bounds = self.text_system().raster_bounds(¶ms)?;
+ if !raster_bounds.is_zero() {
+ let tile =
+ self.window
+ .sprite_atlas
+ .get_or_insert_with(¶ms.clone().into(), &mut || {
+ let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
+ Ok((size, Cow::Owned(bytes)))
+ })?;
+ let bounds = Bounds {
+ origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
+ size: tile.bounds.size.map(Into::into),
+ };
+ let content_mask = self.content_mask().scale(scale_factor);
+
+ self.window
+ .next_frame
+ .scene
+ .insert_primitive(PolychromeSprite {
+ order: 0,
+ grayscale: false,
+ bounds,
+ corner_radii: Default::default(),
+ content_mask,
+ tile,
+ });
+ }
+ Ok(())
+ }
+
+ /// Paint a monochrome SVG into the scene for the next frame at the current stacking context.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn paint_svg(
+ &mut self,
+ bounds: Bounds<Pixels>,
+ path: SharedString,
+ transformation: TransformationMatrix,
+ color: Hsla,
+ ) -> Result<()> {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let bounds = bounds.scale(scale_factor);
+ // Render the SVG at twice the size to get a higher quality result.
+ let params = RenderSvgParams {
+ path,
+ size: bounds
+ .size
+ .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
+ };
+
+ let tile =
+ self.window
+ .sprite_atlas
+ .get_or_insert_with(¶ms.clone().into(), &mut || {
+ let bytes = self.svg_renderer.render(¶ms)?;
+ Ok((params.size, Cow::Owned(bytes)))
+ })?;
+ let content_mask = self.content_mask().scale(scale_factor);
+
+ self.window
+ .next_frame
+ .scene
+ .insert_primitive(MonochromeSprite {
+ order: 0,
+ pad: 0,
+ bounds,
+ content_mask,
+ color,
+ tile,
+ transformation,
+ });
+
+ Ok(())
+ }
+
+ /// Paint an image into the scene for the next frame at the current z-index.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn paint_image(
+ &mut self,
+ bounds: Bounds<Pixels>,
+ corner_radii: Corners<Pixels>,
+ data: Arc<ImageData>,
+ grayscale: bool,
+ ) -> Result<()> {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let bounds = bounds.scale(scale_factor);
+ let params = RenderImageParams { image_id: data.id };
+
+ let tile = self
+ .window
+ .sprite_atlas
+ .get_or_insert_with(¶ms.clone().into(), &mut || {
+ Ok((data.size(), Cow::Borrowed(data.as_bytes())))
+ })?;
+ let content_mask = self.content_mask().scale(scale_factor);
+ let corner_radii = corner_radii.scale(scale_factor);
+
+ self.window
+ .next_frame
+ .scene
+ .insert_primitive(PolychromeSprite {
+ order: 0,
+ grayscale,
+ bounds,
+ content_mask,
+ corner_radii,
+ tile,
+ });
+ Ok(())
+ }
+
+ /// Paint a surface into the scene for the next frame at the current z-index.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ #[cfg(target_os = "macos")]
+ pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVImageBuffer) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ let scale_factor = self.scale_factor();
+ let bounds = bounds.scale(scale_factor);
+ let content_mask = self.content_mask().scale(scale_factor);
+ self.window
+ .next_frame
+ .scene
+ .insert_primitive(crate::Surface {
+ order: 0,
+ bounds,
+ content_mask,
+ image_buffer,
+ });
+ }
+
+ #[must_use]
+ /// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which
+ /// layout is being requested, along with the layout ids of any children. This method is called during
+ /// calls to the [`Element::request_layout`] trait method and enables any element to participate in layout.
+ ///
+ /// This method should only be called as part of the request_layout or prepaint phase of element drawing.
+ pub fn request_layout(
+ &mut self,
+ style: &Style,
+ children: impl IntoIterator<Item = LayoutId>,
+ ) -> LayoutId {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during request_layout, or prepaint"
+ );
+
+ self.app.layout_id_buffer.clear();
+ self.app.layout_id_buffer.extend(children);
+ let rem_size = self.rem_size();
+
+ self.window.layout_engine.as_mut().unwrap().request_layout(
+ style,
+ rem_size,
+ &self.app.layout_id_buffer,
+ )
+ }
+
+ /// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
+ /// this variant takes a function that is invoked during layout so you can use arbitrary logic to
+ /// determine the element's size. One place this is used internally is when measuring text.
+ ///
+ /// The given closure is invoked at layout time with the known dimensions and available space and
+ /// returns a `Size`.
+ ///
+ /// This method should only be called as part of the request_layout or prepaint phase of element drawing.
+ pub fn request_measured_layout<
+ F: FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
+ + 'static,
+ >(
+ &mut self,
+ style: Style,
+ measure: F,
+ ) -> LayoutId {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during request_layout, or prepaint"
+ );
+
+ let rem_size = self.rem_size();
+ self.window
+ .layout_engine
+ .as_mut()
+ .unwrap()
+ .request_measured_layout(style, rem_size, measure)
+ }
+
+ /// Compute the layout for the given id within the given available space.
+ /// This method is called for its side effect, typically by the framework prior to painting.
+ /// After calling it, you can request the bounds of the given layout node id or any descendant.
+ ///
+ /// This method should only be called as part of the prepaint phase of element drawing.
+ pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during request_layout, or prepaint"
+ );
+
+ let mut layout_engine = self.window.layout_engine.take().unwrap();
+ layout_engine.compute_layout(layout_id, available_space, self);
+ self.window.layout_engine = Some(layout_engine);
+ }
+
+ /// Obtain the bounds computed for the given LayoutId relative to the window. This method will usually be invoked by
+ /// GPUI itself automatically in order to pass your element its `Bounds` automatically.
+ ///
+ /// This method should only be called as part of element drawing.
+ pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during request_layout, prepaint, or paint"
+ );
+
+ let mut bounds = self
+ .window
+ .layout_engine
+ .as_mut()
+ .unwrap()
+ .layout_bounds(layout_id)
+ .map(Into::into);
+ bounds.origin += self.element_offset();
+ bounds
+ }
+
+ /// This method should be called during `prepaint`. You can use
+ /// the returned [Hitbox] during `paint` or in an event handler
+ /// to determine whether the inserted hitbox was the topmost.
+ ///
+ /// This method should only be called as part of the prepaint phase of element drawing.
+ pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during prepaint"
+ );
+
+ let content_mask = self.content_mask();
+ let window = &mut self.window;
+ let id = window.next_hitbox_id;
+ window.next_hitbox_id.0 += 1;
+ let hitbox = Hitbox {
+ id,
+ bounds,
+ content_mask,
+ opaque,
+ };
+ window.next_frame.hitboxes.push(hitbox.clone());
+ hitbox
+ }
+
+ /// Sets the key context for the current element. This context will be used to translate
+ /// keybindings into actions.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn set_key_context(&mut self, context: KeyContext) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+ self.window
+ .next_frame
+ .dispatch_tree
+ .set_key_context(context);
+ }
+
+ /// Sets the focus handle for the current element. This handle will be used to manage focus state
+ /// and keyboard event dispatch for the element.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+ self.window
+ .next_frame
+ .dispatch_tree
+ .set_focus_id(focus_handle.id);
+ }
+
+ /// Sets the view id for the current element, which will be used to manage view caching.
+ ///
+ /// This method should only be called as part of element prepaint. We plan on removing this
+ /// method eventually when we solve some issues that require us to construct editor elements
+ /// directly instead of always using editors via views.
+ pub fn set_view_id(&mut self, view_id: EntityId) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint,
+ "this method can only be called during prepaint"
+ );
+ self.window.next_frame.dispatch_tree.set_view_id(view_id);
+ }
+
+ /// Get the last view id for the current element
+ pub fn parent_view_id(&mut self) -> Option<EntityId> {
+ self.window.next_frame.dispatch_tree.parent_view_id()
+ }
+
+ /// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the
+ /// platform to receive textual input with proper integration with concerns such
+ /// as IME interactions. This handler will be active for the upcoming frame until the following frame is
+ /// rendered.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ ///
+ /// [element_input_handler]: crate::ElementInputHandler
+ pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ if focus_handle.is_focused(self) {
+ let cx = self.to_async();
+ self.window
+ .next_frame
+ .input_handlers
+ .push(Some(PlatformInputHandler::new(cx, Box::new(input_handler))));
+ }
+ }
+
+ /// Register a mouse event listener on the window for the next frame. The type of event
+ /// is determined by the first parameter of the given listener. When the next frame is rendered
+ /// the listener will be cleared.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn on_mouse_event<Event: MouseEvent>(
+ &mut self,
+ mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static,
+ ) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ self.window.next_frame.mouse_listeners.push(Some(Box::new(
+ move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_>| {
+ if let Some(event) = event.downcast_ref() {
+ handler(event, phase, cx)
+ }
+ },
+ )));
+ }
+
+ /// Register a key event listener on the window for the next frame. The type of event
+ /// is determined by the first parameter of the given listener. When the next frame is rendered
+ /// the listener will be cleared.
+ ///
+ /// This is a fairly low-level method, so prefer using event handlers on elements unless you have
+ /// a specific need to register a global listener.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn on_key_event<Event: KeyEvent>(
+ &mut self,
+ listener: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static,
+ ) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ self.window.next_frame.dispatch_tree.on_key_event(Rc::new(
+ move |event: &dyn Any, phase, cx: &mut WindowContext<'_>| {
+ if let Some(event) = event.downcast_ref::<Event>() {
+ listener(event, phase, cx)
+ }
+ },
+ ));
+ }
+
+ /// Register a modifiers changed event listener on the window for the next frame.
+ ///
+ /// This is a fairly low-level method, so prefer using event handlers on elements unless you have
+ /// a specific need to register a global listener.
+ ///
+ /// This method should only be called as part of the paint phase of element drawing.
+ pub fn on_modifiers_changed(
+ &mut self,
+ listener: impl Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static,
+ ) {
+ debug_assert_eq!(
+ self.window.draw_phase,
+ DrawPhase::Paint,
+ "this method can only be called during paint"
+ );
+
+ self.window
+ .next_frame
+ .dispatch_tree
+ .on_modifiers_changed(Rc::new(
+ move |event: &ModifiersChangedEvent, cx: &mut WindowContext<'_>| {
+ listener(event, cx)
+ },
+ ));
+ }
+
fn reset_cursor_style(&self) {
// Set the cursor only if we're the active window.
if self.is_window_active() {
@@ -1,1554 +0,0 @@
-//! The element context is the main interface for interacting with the frame during a paint.
-//!
-//! Elements are hierarchical and with a few exceptions the context accumulates state in a stack
-//! as it processes all of the elements in the frame. The methods that interact with this stack
-//! are generally marked with `with_*`, and take a callback to denote the region of code that
-//! should be executed with that state.
-//!
-//! The other main interface is the `paint_*` family of methods, which push basic drawing commands
-//! to the GPU. Everything in a GPUI app is drawn with these methods.
-//!
-//! There are also several internal methods that GPUI uses, such as [`ElementContext::with_element_state`]
-//! to call the paint and layout methods on elements. These have been included as they're often useful
-//! for taking manual control of the layouting or painting of specialized elements.
-
-use std::{
- any::{Any, TypeId},
- borrow::{Borrow, BorrowMut, Cow},
- cmp, mem,
- ops::Range,
- rc::Rc,
- sync::Arc,
-};
-
-use anyhow::Result;
-use collections::FxHashMap;
-use derive_more::{Deref, DerefMut};
-use futures::{future::Shared, FutureExt};
-#[cfg(target_os = "macos")]
-use media::core_video::CVImageBuffer;
-use smallvec::SmallVec;
-use util::post_inc;
-
-use crate::{
- hash, point, prelude::*, px, size, AnyElement, AnyTooltip, AppContext, Asset, AvailableSpace,
- Bounds, BoxShadow, ContentMask, Corners, CursorStyle, DevicePixels, DispatchNodeId,
- DispatchPhase, DispatchTree, DrawPhase, ElementId, ElementStateBox, EntityId, FocusHandle,
- FocusId, FontId, GlobalElementId, GlyphId, Hsla, ImageData, InputHandler, IsZero, KeyContext,
- KeyEvent, LayoutId, LineLayoutIndex, ModifiersChangedEvent, MonochromeSprite, MouseEvent,
- PaintQuad, Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad,
- RenderGlyphParams, RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size,
- StrikethroughStyle, Style, Task, TextStyleRefinement, TransformationMatrix, Underline,
- UnderlineStyle, Window, WindowContext, SUBPIXEL_VARIANTS,
-};
-
-pub(crate) type AnyMouseListener =
- Box<dyn FnMut(&dyn Any, DispatchPhase, &mut ElementContext) + 'static>;
-
-#[derive(Clone)]
-pub(crate) struct CursorStyleRequest {
- pub(crate) hitbox_id: HitboxId,
- pub(crate) style: CursorStyle,
-}
-
-/// An identifier for a [Hitbox].
-#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
-pub struct HitboxId(usize);
-
-impl HitboxId {
- /// Checks if the hitbox with this id is currently hovered.
- pub fn is_hovered(&self, cx: &WindowContext) -> bool {
- cx.window.mouse_hit_test.0.contains(self)
- }
-}
-
-/// A rectangular region that potentially blocks hitboxes inserted prior.
-/// See [ElementContext::insert_hitbox] for more details.
-#[derive(Clone, Debug, Deref)]
-pub struct Hitbox {
- /// A unique identifier for the hitbox.
- pub id: HitboxId,
- /// The bounds of the hitbox.
- #[deref]
- pub bounds: Bounds<Pixels>,
- /// The content mask when the hitbox was inserted.
- pub content_mask: ContentMask<Pixels>,
- /// Whether the hitbox occludes other hitboxes inserted prior.
- pub opaque: bool,
-}
-
-impl Hitbox {
- /// Checks if the hitbox is currently hovered.
- pub fn is_hovered(&self, cx: &WindowContext) -> bool {
- self.id.is_hovered(cx)
- }
-}
-
-#[derive(Default, Eq, PartialEq)]
-pub(crate) struct HitTest(SmallVec<[HitboxId; 8]>);
-
-/// An identifier for a tooltip.
-#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
-pub struct TooltipId(usize);
-
-impl TooltipId {
- /// Checks if the tooltip is currently hovered.
- pub fn is_hovered(&self, cx: &WindowContext) -> bool {
- cx.window
- .tooltip_bounds
- .as_ref()
- .map_or(false, |tooltip_bounds| {
- tooltip_bounds.id == *self && tooltip_bounds.bounds.contains(&cx.mouse_position())
- })
- }
-}
-
-pub(crate) struct TooltipBounds {
- id: TooltipId,
- bounds: Bounds<Pixels>,
-}
-
-#[derive(Clone)]
-pub(crate) struct TooltipRequest {
- id: TooltipId,
- tooltip: AnyTooltip,
-}
-
-pub(crate) struct DeferredDraw {
- priority: usize,
- parent_node: DispatchNodeId,
- element_id_stack: GlobalElementId,
- text_style_stack: Vec<TextStyleRefinement>,
- element: Option<AnyElement>,
- absolute_offset: Point<Pixels>,
- prepaint_range: Range<PrepaintStateIndex>,
- paint_range: Range<PaintIndex>,
-}
-
-pub(crate) struct Frame {
- pub(crate) focus: Option<FocusId>,
- pub(crate) window_active: bool,
- pub(crate) element_states: FxHashMap<(GlobalElementId, TypeId), ElementStateBox>,
- accessed_element_states: Vec<(GlobalElementId, TypeId)>,
- pub(crate) mouse_listeners: Vec<Option<AnyMouseListener>>,
- pub(crate) dispatch_tree: DispatchTree,
- pub(crate) scene: Scene,
- pub(crate) hitboxes: Vec<Hitbox>,
- pub(crate) deferred_draws: Vec<DeferredDraw>,
- pub(crate) input_handlers: Vec<Option<PlatformInputHandler>>,
- pub(crate) tooltip_requests: Vec<Option<TooltipRequest>>,
- pub(crate) cursor_styles: Vec<CursorStyleRequest>,
- #[cfg(any(test, feature = "test-support"))]
- pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
-}
-
-#[derive(Clone, Default)]
-pub(crate) struct PrepaintStateIndex {
- hitboxes_index: usize,
- tooltips_index: usize,
- deferred_draws_index: usize,
- dispatch_tree_index: usize,
- accessed_element_states_index: usize,
- line_layout_index: LineLayoutIndex,
-}
-
-#[derive(Clone, Default)]
-pub(crate) struct PaintIndex {
- scene_index: usize,
- mouse_listeners_index: usize,
- input_handlers_index: usize,
- cursor_styles_index: usize,
- accessed_element_states_index: usize,
- line_layout_index: LineLayoutIndex,
-}
-
-impl Frame {
- pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
- Frame {
- focus: None,
- window_active: false,
- element_states: FxHashMap::default(),
- accessed_element_states: Vec::new(),
- mouse_listeners: Vec::new(),
- dispatch_tree,
- scene: Scene::default(),
- hitboxes: Vec::new(),
- deferred_draws: Vec::new(),
- input_handlers: Vec::new(),
- tooltip_requests: Vec::new(),
- cursor_styles: Vec::new(),
-
- #[cfg(any(test, feature = "test-support"))]
- debug_bounds: FxHashMap::default(),
- }
- }
-
- pub(crate) fn clear(&mut self) {
- self.element_states.clear();
- self.accessed_element_states.clear();
- self.mouse_listeners.clear();
- self.dispatch_tree.clear();
- self.scene.clear();
- self.input_handlers.clear();
- self.tooltip_requests.clear();
- self.cursor_styles.clear();
- self.hitboxes.clear();
- self.deferred_draws.clear();
- }
-
- pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
- let mut hit_test = HitTest::default();
- for hitbox in self.hitboxes.iter().rev() {
- let bounds = hitbox.bounds.intersect(&hitbox.content_mask.bounds);
- if bounds.contains(&position) {
- hit_test.0.push(hitbox.id);
- if hitbox.opaque {
- break;
- }
- }
- }
- hit_test
- }
-
- pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
- self.focus
- .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
- .unwrap_or_default()
- }
-
- pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
- for element_state_key in &self.accessed_element_states {
- if let Some(element_state) = prev_frame.element_states.remove(element_state_key) {
- self.element_states
- .insert(element_state_key.clone(), element_state);
- }
- }
-
- self.scene.finish();
- }
-}
-
-/// This context is used for assisting in the implementation of the element trait
-#[derive(Deref, DerefMut)]
-pub struct ElementContext<'a> {
- pub(crate) cx: WindowContext<'a>,
-}
-
-impl<'a> WindowContext<'a> {
- /// Convert this window context into an ElementContext in this callback.
- /// If you need to use this method, you're probably intermixing the imperative
- /// and declarative APIs, which is not recommended.
- pub fn with_element_context<R>(&mut self, f: impl FnOnce(&mut ElementContext) -> R) -> R {
- f(&mut ElementContext {
- cx: WindowContext::new(self.app, self.window),
- })
- }
-}
-
-impl<'a> Borrow<AppContext> for ElementContext<'a> {
- fn borrow(&self) -> &AppContext {
- self.cx.app
- }
-}
-
-impl<'a> BorrowMut<AppContext> for ElementContext<'a> {
- fn borrow_mut(&mut self) -> &mut AppContext {
- self.cx.borrow_mut()
- }
-}
-
-impl<'a> Borrow<WindowContext<'a>> for ElementContext<'a> {
- fn borrow(&self) -> &WindowContext<'a> {
- &self.cx
- }
-}
-
-impl<'a> BorrowMut<WindowContext<'a>> for ElementContext<'a> {
- fn borrow_mut(&mut self) -> &mut WindowContext<'a> {
- &mut self.cx
- }
-}
-
-impl<'a> Borrow<Window> for ElementContext<'a> {
- fn borrow(&self) -> &Window {
- self.cx.window
- }
-}
-
-impl<'a> BorrowMut<Window> for ElementContext<'a> {
- fn borrow_mut(&mut self) -> &mut Window {
- self.cx.borrow_mut()
- }
-}
-
-impl<'a> Context for ElementContext<'a> {
- type Result<T> = <WindowContext<'a> as Context>::Result<T>;
-
- fn new_model<T: 'static>(
- &mut self,
- build_model: impl FnOnce(&mut crate::ModelContext<'_, T>) -> T,
- ) -> Self::Result<crate::Model<T>> {
- self.cx.new_model(build_model)
- }
-
- fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
- self.cx.reserve_model()
- }
-
- fn insert_model<T: 'static>(
- &mut self,
- reservation: crate::Reservation<T>,
- build_model: impl FnOnce(&mut crate::ModelContext<'_, T>) -> T,
- ) -> Self::Result<crate::Model<T>> {
- self.cx.insert_model(reservation, build_model)
- }
-
- fn update_model<T, R>(
- &mut self,
- handle: &crate::Model<T>,
- update: impl FnOnce(&mut T, &mut crate::ModelContext<'_, T>) -> R,
- ) -> Self::Result<R>
- where
- T: 'static,
- {
- self.cx.update_model(handle, update)
- }
-
- fn read_model<T, R>(
- &self,
- handle: &crate::Model<T>,
- read: impl FnOnce(&T, &AppContext) -> R,
- ) -> Self::Result<R>
- where
- T: 'static,
- {
- self.cx.read_model(handle, read)
- }
-
- fn update_window<T, F>(&mut self, window: crate::AnyWindowHandle, f: F) -> Result<T>
- where
- F: FnOnce(crate::AnyView, &mut WindowContext<'_>) -> T,
- {
- self.cx.update_window(window, f)
- }
-
- fn read_window<T, R>(
- &self,
- window: &crate::WindowHandle<T>,
- read: impl FnOnce(crate::View<T>, &AppContext) -> R,
- ) -> Result<R>
- where
- T: 'static,
- {
- self.cx.read_window(window, read)
- }
-}
-
-impl<'a> VisualContext for ElementContext<'a> {
- fn new_view<V>(
- &mut self,
- build_view: impl FnOnce(&mut crate::ViewContext<'_, V>) -> V,
- ) -> Self::Result<crate::View<V>>
- where
- V: 'static + Render,
- {
- self.cx.new_view(build_view)
- }
-
- fn update_view<V: 'static, R>(
- &mut self,
- view: &crate::View<V>,
- update: impl FnOnce(&mut V, &mut crate::ViewContext<'_, V>) -> R,
- ) -> Self::Result<R> {
- self.cx.update_view(view, update)
- }
-
- fn replace_root_view<V>(
- &mut self,
- build_view: impl FnOnce(&mut crate::ViewContext<'_, V>) -> V,
- ) -> Self::Result<crate::View<V>>
- where
- V: 'static + Render,
- {
- self.cx.replace_root_view(build_view)
- }
-
- fn focus_view<V>(&mut self, view: &crate::View<V>) -> Self::Result<()>
- where
- V: crate::FocusableView,
- {
- self.cx.focus_view(view)
- }
-
- fn dismiss_view<V>(&mut self, view: &crate::View<V>) -> Self::Result<()>
- where
- V: crate::ManagedView,
- {
- self.cx.dismiss_view(view)
- }
-}
-
-impl<'a> ElementContext<'a> {
- pub(crate) fn draw_roots(&mut self) {
- self.window.draw_phase = DrawPhase::Layout;
- self.window.tooltip_bounds.take();
-
- // Layout all root elements.
- let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any();
- root_element.prepaint_as_root(Point::default(), self.window.viewport_size.into(), self);
-
- let mut sorted_deferred_draws =
- (0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
- sorted_deferred_draws.sort_by_key(|ix| self.window.next_frame.deferred_draws[*ix].priority);
- self.prepaint_deferred_draws(&sorted_deferred_draws);
-
- let mut prompt_element = None;
- let mut active_drag_element = None;
- let mut tooltip_element = None;
- if let Some(prompt) = self.window.prompt.take() {
- let mut element = prompt.view.any_view().into_any();
- element.prepaint_as_root(Point::default(), self.window.viewport_size.into(), self);
- prompt_element = Some(element);
- self.window.prompt = Some(prompt);
- } else if let Some(active_drag) = self.app.active_drag.take() {
- let mut element = active_drag.view.clone().into_any();
- let offset = self.mouse_position() - active_drag.cursor_offset;
- element.prepaint_as_root(offset, AvailableSpace::min_size(), self);
- active_drag_element = Some(element);
- self.app.active_drag = Some(active_drag);
- } else {
- tooltip_element = self.prepaint_tooltip();
- }
-
- self.window.mouse_hit_test = self.window.next_frame.hit_test(self.window.mouse_position);
-
- // Now actually paint the elements.
- self.window.draw_phase = DrawPhase::Paint;
- root_element.paint(self);
-
- self.paint_deferred_draws(&sorted_deferred_draws);
-
- if let Some(mut prompt_element) = prompt_element {
- prompt_element.paint(self)
- } else if let Some(mut drag_element) = active_drag_element {
- drag_element.paint(self);
- } else if let Some(mut tooltip_element) = tooltip_element {
- tooltip_element.paint(self);
- }
- }
-
- fn prepaint_tooltip(&mut self) -> Option<AnyElement> {
- let tooltip_request = self.window.next_frame.tooltip_requests.last().cloned()?;
- let tooltip_request = tooltip_request.unwrap();
- let mut element = tooltip_request.tooltip.view.clone().into_any();
- let mouse_position = tooltip_request.tooltip.mouse_position;
- let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self);
-
- let mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
- let window_bounds = Bounds {
- origin: Point::default(),
- size: self.viewport_size(),
- };
-
- if tooltip_bounds.right() > window_bounds.right() {
- let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
- if new_x >= Pixels::ZERO {
- tooltip_bounds.origin.x = new_x;
- } else {
- tooltip_bounds.origin.x = cmp::max(
- Pixels::ZERO,
- tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
- );
- }
- }
-
- if tooltip_bounds.bottom() > window_bounds.bottom() {
- let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
- if new_y >= Pixels::ZERO {
- tooltip_bounds.origin.y = new_y;
- } else {
- tooltip_bounds.origin.y = cmp::max(
- Pixels::ZERO,
- tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
- );
- }
- }
-
- self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.prepaint(cx));
-
- self.window.tooltip_bounds = Some(TooltipBounds {
- id: tooltip_request.id,
- bounds: tooltip_bounds,
- });
- Some(element)
- }
-
- fn prepaint_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
- assert_eq!(self.window.element_id_stack.len(), 0);
-
- let mut deferred_draws = mem::take(&mut self.window.next_frame.deferred_draws);
- for deferred_draw_ix in deferred_draw_indices {
- let deferred_draw = &mut deferred_draws[*deferred_draw_ix];
- self.window.element_id_stack = deferred_draw.element_id_stack.clone();
- self.window.text_style_stack = deferred_draw.text_style_stack.clone();
- self.window
- .next_frame
- .dispatch_tree
- .set_active_node(deferred_draw.parent_node);
-
- let prepaint_start = self.prepaint_index();
- if let Some(element) = deferred_draw.element.as_mut() {
- self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| {
- element.prepaint(cx)
- });
- } else {
- self.reuse_prepaint(deferred_draw.prepaint_range.clone());
- }
- let prepaint_end = self.prepaint_index();
- deferred_draw.prepaint_range = prepaint_start..prepaint_end;
- }
- assert_eq!(
- self.window.next_frame.deferred_draws.len(),
- 0,
- "cannot call defer_draw during deferred drawing"
- );
- self.window.next_frame.deferred_draws = deferred_draws;
- self.window.element_id_stack.clear();
- self.window.text_style_stack.clear();
- }
-
- fn paint_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
- assert_eq!(self.window.element_id_stack.len(), 0);
-
- let mut deferred_draws = mem::take(&mut self.window.next_frame.deferred_draws);
- for deferred_draw_ix in deferred_draw_indices {
- let mut deferred_draw = &mut deferred_draws[*deferred_draw_ix];
- self.window.element_id_stack = deferred_draw.element_id_stack.clone();
- self.window
- .next_frame
- .dispatch_tree
- .set_active_node(deferred_draw.parent_node);
-
- let paint_start = self.paint_index();
- if let Some(element) = deferred_draw.element.as_mut() {
- element.paint(self);
- } else {
- self.reuse_paint(deferred_draw.paint_range.clone());
- }
- let paint_end = self.paint_index();
- deferred_draw.paint_range = paint_start..paint_end;
- }
- self.window.next_frame.deferred_draws = deferred_draws;
- self.window.element_id_stack.clear();
- }
-
- pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex {
- PrepaintStateIndex {
- hitboxes_index: self.window.next_frame.hitboxes.len(),
- tooltips_index: self.window.next_frame.tooltip_requests.len(),
- deferred_draws_index: self.window.next_frame.deferred_draws.len(),
- dispatch_tree_index: self.window.next_frame.dispatch_tree.len(),
- accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
- line_layout_index: self.window.text_system.layout_index(),
- }
- }
-
- pub(crate) fn reuse_prepaint(&mut self, range: Range<PrepaintStateIndex>) {
- let window = &mut self.window;
- window.next_frame.hitboxes.extend(
- window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
- .iter()
- .cloned(),
- );
- window.next_frame.tooltip_requests.extend(
- window.rendered_frame.tooltip_requests
- [range.start.tooltips_index..range.end.tooltips_index]
- .iter_mut()
- .map(|request| request.take()),
- );
- window.next_frame.accessed_element_states.extend(
- window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
- ..range.end.accessed_element_states_index]
- .iter()
- .cloned(),
- );
- window
- .text_system
- .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
-
- let reused_subtree = window.next_frame.dispatch_tree.reuse_subtree(
- range.start.dispatch_tree_index..range.end.dispatch_tree_index,
- &mut window.rendered_frame.dispatch_tree,
- );
- window.next_frame.deferred_draws.extend(
- window.rendered_frame.deferred_draws
- [range.start.deferred_draws_index..range.end.deferred_draws_index]
- .iter()
- .map(|deferred_draw| DeferredDraw {
- parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
- element_id_stack: deferred_draw.element_id_stack.clone(),
- text_style_stack: deferred_draw.text_style_stack.clone(),
- priority: deferred_draw.priority,
- element: None,
- absolute_offset: deferred_draw.absolute_offset,
- prepaint_range: deferred_draw.prepaint_range.clone(),
- paint_range: deferred_draw.paint_range.clone(),
- }),
- );
- }
-
- pub(crate) fn paint_index(&self) -> PaintIndex {
- PaintIndex {
- scene_index: self.window.next_frame.scene.len(),
- mouse_listeners_index: self.window.next_frame.mouse_listeners.len(),
- input_handlers_index: self.window.next_frame.input_handlers.len(),
- cursor_styles_index: self.window.next_frame.cursor_styles.len(),
- accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
- line_layout_index: self.window.text_system.layout_index(),
- }
- }
-
- pub(crate) fn reuse_paint(&mut self, range: Range<PaintIndex>) {
- let window = &mut self.cx.window;
-
- window.next_frame.cursor_styles.extend(
- window.rendered_frame.cursor_styles
- [range.start.cursor_styles_index..range.end.cursor_styles_index]
- .iter()
- .cloned(),
- );
- window.next_frame.input_handlers.extend(
- window.rendered_frame.input_handlers
- [range.start.input_handlers_index..range.end.input_handlers_index]
- .iter_mut()
- .map(|handler| handler.take()),
- );
- window.next_frame.mouse_listeners.extend(
- window.rendered_frame.mouse_listeners
- [range.start.mouse_listeners_index..range.end.mouse_listeners_index]
- .iter_mut()
- .map(|listener| listener.take()),
- );
- window.next_frame.accessed_element_states.extend(
- window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
- ..range.end.accessed_element_states_index]
- .iter()
- .cloned(),
- );
- window
- .text_system
- .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
- window.next_frame.scene.replay(
- range.start.scene_index..range.end.scene_index,
- &window.rendered_frame.scene,
- );
- }
-
- /// Push a text style onto the stack, and call a function with that style active.
- /// Use [`AppContext::text_style`] to get the current, combined text style.
- pub fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
- where
- F: FnOnce(&mut Self) -> R,
- {
- if let Some(style) = style {
- self.window.text_style_stack.push(style);
- let result = f(self);
- self.window.text_style_stack.pop();
- result
- } else {
- f(self)
- }
- }
-
- /// Updates the cursor style at the platform level.
- pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
- self.window
- .next_frame
- .cursor_styles
- .push(CursorStyleRequest {
- hitbox_id: hitbox.id,
- style,
- });
- }
-
- /// Sets a tooltip to be rendered for the upcoming frame
- pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId {
- let id = TooltipId(post_inc(&mut self.window.next_tooltip_id.0));
- self.window
- .next_frame
- .tooltip_requests
- .push(Some(TooltipRequest { id, tooltip }));
- id
- }
-
- /// Pushes the given element id onto the global stack and invokes the given closure
- /// with a `GlobalElementId`, which disambiguates the given id in the context of its ancestor
- /// ids. Because elements are discarded and recreated on each frame, the `GlobalElementId` is
- /// used to associate state with identified elements across separate frames.
- pub fn with_element_id<R>(
- &mut self,
- id: Option<impl Into<ElementId>>,
- f: impl FnOnce(&mut Self) -> R,
- ) -> R {
- if let Some(id) = id.map(Into::into) {
- let window = self.window_mut();
- window.element_id_stack.push(id);
- let result = f(self);
- let window: &mut Window = self.borrow_mut();
- window.element_id_stack.pop();
- result
- } else {
- f(self)
- }
- }
-
- /// Invoke the given function with the given content mask after intersecting it
- /// with the current mask.
- pub fn with_content_mask<R>(
- &mut self,
- mask: Option<ContentMask<Pixels>>,
- f: impl FnOnce(&mut Self) -> R,
- ) -> R {
- if let Some(mask) = mask {
- let mask = mask.intersect(&self.content_mask());
- self.window_mut().content_mask_stack.push(mask);
- let result = f(self);
- self.window_mut().content_mask_stack.pop();
- result
- } else {
- f(self)
- }
- }
-
- /// Updates the global element offset relative to the current offset. This is used to implement
- /// scrolling.
- pub fn with_element_offset<R>(
- &mut self,
- offset: Point<Pixels>,
- f: impl FnOnce(&mut Self) -> R,
- ) -> R {
- if offset.is_zero() {
- return f(self);
- };
-
- let abs_offset = self.element_offset() + offset;
- self.with_absolute_element_offset(abs_offset, f)
- }
-
- /// Updates the global element offset based on the given offset. This is used to implement
- /// drag handles and other manual painting of elements.
- pub fn with_absolute_element_offset<R>(
- &mut self,
- offset: Point<Pixels>,
- f: impl FnOnce(&mut Self) -> R,
- ) -> R {
- self.window_mut().element_offset_stack.push(offset);
- let result = f(self);
- self.window_mut().element_offset_stack.pop();
- result
- }
-
- /// Perform prepaint on child elements in a "retryable" manner, so that any side effects
- /// of prepaints can be discarded before prepainting again. This is used to support autoscroll
- /// where we need to prepaint children to detect the autoscroll bounds, then adjust the
- /// element offset and prepaint again. See [`List`] for an example.
- pub fn transact<T, U>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, U>) -> Result<T, U> {
- let index = self.prepaint_index();
- let result = f(self);
- if result.is_err() {
- self.window
- .next_frame
- .hitboxes
- .truncate(index.hitboxes_index);
- self.window
- .next_frame
- .tooltip_requests
- .truncate(index.tooltips_index);
- self.window
- .next_frame
- .deferred_draws
- .truncate(index.deferred_draws_index);
- self.window
- .next_frame
- .dispatch_tree
- .truncate(index.dispatch_tree_index);
- self.window
- .next_frame
- .accessed_element_states
- .truncate(index.accessed_element_states_index);
- self.window
- .text_system
- .truncate_layouts(index.line_layout_index);
- }
- result
- }
-
- /// When you call this method during [`prepaint`], containing elements will attempt to
- /// scroll to cause the specified bounds to become visible. When they decide to autoscroll, they will call
- /// [`prepaint`] again with a new set of bounds. See [`List`] for an example of an element
- /// that supports this method being called on the elements it contains.
- pub fn request_autoscroll(&mut self, bounds: Bounds<Pixels>) {
- self.window.requested_autoscroll = Some(bounds);
- }
-
- /// This method can be called from a containing element such as [`List`] to support the autoscroll behavior
- /// described in [`request_autoscroll`].
- pub fn take_autoscroll(&mut self) -> Option<Bounds<Pixels>> {
- self.window.requested_autoscroll.take()
- }
-
- /// Remove an asset from GPUI's cache
- pub fn remove_cached_asset<A: Asset + 'static>(
- &mut self,
- source: &A::Source,
- ) -> Option<A::Output> {
- self.asset_cache.remove::<A>(source)
- }
-
- /// Asynchronously load an asset, if the asset hasn't finished loading this will return None.
- /// Your view will be re-drawn once the asset has finished loading.
- ///
- /// Note that the multiple calls to this method will only result in one `Asset::load` call.
- /// The results of that call will be cached, and returned on subsequent uses of this API.
- ///
- /// Use [Self::remove_cached_asset] to reload your asset.
- pub fn use_cached_asset<A: Asset + 'static>(
- &mut self,
- source: &A::Source,
- ) -> Option<A::Output> {
- self.asset_cache.get::<A>(source).or_else(|| {
- if let Some(asset) = self.use_asset::<A>(source) {
- self.asset_cache
- .insert::<A>(source.to_owned(), asset.clone());
- Some(asset)
- } else {
- None
- }
- })
- }
-
- /// Asynchronously load an asset, if the asset hasn't finished loading this will return None.
- /// Your view will be re-drawn once the asset has finished loading.
- ///
- /// Note that the multiple calls to this method will only result in one `Asset::load` call at a
- /// time.
- ///
- /// This asset will not be cached by default, see [Self::use_cached_asset]
- pub fn use_asset<A: Asset + 'static>(&mut self, source: &A::Source) -> Option<A::Output> {
- let asset_id = (TypeId::of::<A>(), hash(source));
- let mut is_first = false;
- let task = self
- .loading_assets
- .remove(&asset_id)
- .map(|boxed_task| *boxed_task.downcast::<Shared<Task<A::Output>>>().unwrap())
- .unwrap_or_else(|| {
- is_first = true;
- let future = A::load(source.clone(), self);
- let task = self.background_executor().spawn(future).shared();
- task
- });
-
- task.clone().now_or_never().or_else(|| {
- if is_first {
- let parent_id = self.parent_view_id();
- self.spawn({
- let task = task.clone();
- |mut cx| async move {
- task.await;
-
- cx.on_next_frame(move |cx| {
- if let Some(parent_id) = parent_id {
- cx.notify(parent_id)
- } else {
- cx.refresh()
- }
- });
- }
- })
- .detach();
- }
-
- self.loading_assets.insert(asset_id, Box::new(task));
-
- None
- })
- }
-
- /// Obtain the current element offset.
- pub fn element_offset(&self) -> Point<Pixels> {
- self.window()
- .element_offset_stack
- .last()
- .copied()
- .unwrap_or_default()
- }
-
- /// Obtain the current content mask.
- pub fn content_mask(&self) -> ContentMask<Pixels> {
- self.window()
- .content_mask_stack
- .last()
- .cloned()
- .unwrap_or_else(|| ContentMask {
- bounds: Bounds {
- origin: Point::default(),
- size: self.window().viewport_size,
- },
- })
- }
-
- /// The size of an em for the base font of the application. Adjusting this value allows the
- /// UI to scale, just like zooming a web page.
- pub fn rem_size(&self) -> Pixels {
- self.window().rem_size
- }
-
- /// Updates or initializes state for an element with the given id that lives across multiple
- /// frames. If an element with this ID existed in the rendered frame, its state will be passed
- /// to the given closure. The state returned by the closure will be stored so it can be referenced
- /// when drawing the next frame.
- pub fn with_element_state<S, R>(
- &mut self,
- element_id: Option<ElementId>,
- f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
- ) -> R
- where
- S: 'static,
- {
- let id_is_none = element_id.is_none();
- self.with_element_id(element_id, |cx| {
- if id_is_none {
- let (result, state) = f(None, cx);
- debug_assert!(state.is_none(), "you must not return an element state when passing None for the element id");
- result
- } else {
- let global_id = cx.window().element_id_stack.clone();
- let key = (global_id, TypeId::of::<S>());
- cx.window.next_frame.accessed_element_states.push(key.clone());
-
- if let Some(any) = cx
- .window_mut()
- .next_frame
- .element_states
- .remove(&key)
- .or_else(|| {
- cx.window_mut()
- .rendered_frame
- .element_states
- .remove(&key)
- })
- {
- 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::anyhow!(
- "invalid element state type for id, requested_type {:?}, actual type: {:?}",
- std::any::type_name::<S>(),
- type_name
- )
- }
-
- #[cfg(not(debug_assertions))]
- {
- anyhow::anyhow!(
- "invalid element state type for id, requested_type {:?}",
- std::any::type_name::<S>(),
- )
- }
- })
- .unwrap();
-
- // Actual: Option<AnyElement> <- View
- // Requested: () <- AnyElement
- let state = state_box
- .take()
- .expect("reentrant call to with_element_state for the same state type and element id");
- let (result, state) = f(Some(Some(state)), cx);
- state_box.replace(state.expect("you must return "));
- cx.window_mut()
- .next_frame
- .element_states
- .insert(key, ElementStateBox {
- inner: state_box,
- #[cfg(debug_assertions)]
- type_name
- });
- result
- } else {
- let (result, state) = f(Some(None), cx);
- cx.window_mut()
- .next_frame
- .element_states
- .insert(key,
- ElementStateBox {
- inner: Box::new(Some(state.expect("you must return Some<State> when you pass some element id"))),
- #[cfg(debug_assertions)]
- type_name: std::any::type_name::<S>()
- }
-
- );
- result
- }
- }
- })
- }
-
- /// Defers the drawing of the given element, scheduling it to be painted on top of the currently-drawn tree
- /// at a later time. The `priority` parameter determines the drawing order relative to other deferred elements,
- /// with higher values being drawn on top.
- pub fn defer_draw(
- &mut self,
- element: AnyElement,
- absolute_offset: Point<Pixels>,
- priority: usize,
- ) {
- let window = &mut self.cx.window;
- assert_eq!(
- window.draw_phase,
- DrawPhase::Layout,
- "defer_draw can only be called during request_layout or prepaint"
- );
- let parent_node = window.next_frame.dispatch_tree.active_node_id().unwrap();
- window.next_frame.deferred_draws.push(DeferredDraw {
- parent_node,
- element_id_stack: window.element_id_stack.clone(),
- text_style_stack: window.text_style_stack.clone(),
- priority,
- element: Some(element),
- absolute_offset,
- prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(),
- paint_range: PaintIndex::default()..PaintIndex::default(),
- });
- }
-
- /// Creates a new painting layer for the specified bounds. A "layer" is a batch
- /// of geometry that are non-overlapping and have the same draw order. This is typically used
- /// for performance reasons.
- pub fn paint_layer<R>(&mut self, bounds: Bounds<Pixels>, f: impl FnOnce(&mut Self) -> R) -> R {
- let scale_factor = self.scale_factor();
- let content_mask = self.content_mask();
- let clipped_bounds = bounds.intersect(&content_mask.bounds);
- if !clipped_bounds.is_empty() {
- self.window
- .next_frame
- .scene
- .push_layer(clipped_bounds.scale(scale_factor));
- }
-
- let result = f(self);
-
- if !clipped_bounds.is_empty() {
- self.window.next_frame.scene.pop_layer();
- }
-
- result
- }
-
- /// Paint one or more drop shadows into the scene for the next frame at the current z-index.
- pub fn paint_shadows(
- &mut self,
- bounds: Bounds<Pixels>,
- corner_radii: Corners<Pixels>,
- shadows: &[BoxShadow],
- ) {
- let scale_factor = self.scale_factor();
- let content_mask = self.content_mask();
- for shadow in shadows {
- let mut shadow_bounds = bounds;
- shadow_bounds.origin += shadow.offset;
- shadow_bounds.dilate(shadow.spread_radius);
- self.window.next_frame.scene.insert_primitive(Shadow {
- order: 0,
- blur_radius: shadow.blur_radius.scale(scale_factor),
- bounds: shadow_bounds.scale(scale_factor),
- content_mask: content_mask.scale(scale_factor),
- corner_radii: corner_radii.scale(scale_factor),
- color: shadow.color,
- });
- }
- }
-
- /// Paint one or more quads into the scene for the next frame at the current stacking context.
- /// Quads are colored rectangular regions with an optional background, border, and corner radius.
- /// see [`fill`](crate::fill), [`outline`](crate::outline), and [`quad`](crate::quad) to construct this type.
- pub fn paint_quad(&mut self, quad: PaintQuad) {
- let scale_factor = self.scale_factor();
- let content_mask = self.content_mask();
- self.window.next_frame.scene.insert_primitive(Quad {
- order: 0,
- pad: 0,
- bounds: quad.bounds.scale(scale_factor),
- content_mask: content_mask.scale(scale_factor),
- background: quad.background,
- border_color: quad.border_color,
- corner_radii: quad.corner_radii.scale(scale_factor),
- border_widths: quad.border_widths.scale(scale_factor),
- });
- }
-
- /// Paint the given `Path` into the scene for the next frame at the current z-index.
- pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
- let scale_factor = self.scale_factor();
- let content_mask = self.content_mask();
- path.content_mask = content_mask;
- path.color = color.into();
- self.window
- .next_frame
- .scene
- .insert_primitive(path.scale(scale_factor));
- }
-
- /// Paint an underline into the scene for the next frame at the current z-index.
- pub fn paint_underline(
- &mut self,
- origin: Point<Pixels>,
- width: Pixels,
- style: &UnderlineStyle,
- ) {
- let scale_factor = self.scale_factor();
- let height = if style.wavy {
- style.thickness * 3.
- } else {
- style.thickness
- };
- let bounds = Bounds {
- origin,
- size: size(width, height),
- };
- let content_mask = self.content_mask();
-
- self.window.next_frame.scene.insert_primitive(Underline {
- order: 0,
- pad: 0,
- bounds: bounds.scale(scale_factor),
- content_mask: content_mask.scale(scale_factor),
- color: style.color.unwrap_or_default(),
- thickness: style.thickness.scale(scale_factor),
- wavy: style.wavy,
- });
- }
-
- /// Paint a strikethrough into the scene for the next frame at the current z-index.
- pub fn paint_strikethrough(
- &mut self,
- origin: Point<Pixels>,
- width: Pixels,
- style: &StrikethroughStyle,
- ) {
- let scale_factor = self.scale_factor();
- let height = style.thickness;
- let bounds = Bounds {
- origin,
- size: size(width, height),
- };
- let content_mask = self.content_mask();
-
- self.window.next_frame.scene.insert_primitive(Underline {
- order: 0,
- pad: 0,
- bounds: bounds.scale(scale_factor),
- content_mask: content_mask.scale(scale_factor),
- thickness: style.thickness.scale(scale_factor),
- color: style.color.unwrap_or_default(),
- wavy: false,
- });
- }
-
- /// Paints a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
- ///
- /// The y component of the origin is the baseline of the glyph.
- /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or
- /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem).
- /// This method is only useful if you need to paint a single glyph that has already been shaped.
- pub fn paint_glyph(
- &mut self,
- origin: Point<Pixels>,
- font_id: FontId,
- glyph_id: GlyphId,
- font_size: Pixels,
- color: Hsla,
- ) -> Result<()> {
- let scale_factor = self.scale_factor();
- let glyph_origin = origin.scale(scale_factor);
- let subpixel_variant = Point {
- x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
- y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
- };
- let params = RenderGlyphParams {
- font_id,
- glyph_id,
- font_size,
- subpixel_variant,
- scale_factor,
- is_emoji: false,
- };
-
- let raster_bounds = self.text_system().raster_bounds(¶ms)?;
- if !raster_bounds.is_zero() {
- let tile =
- self.window
- .sprite_atlas
- .get_or_insert_with(¶ms.clone().into(), &mut || {
- let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
- Ok((size, Cow::Owned(bytes)))
- })?;
- let bounds = Bounds {
- origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
- size: tile.bounds.size.map(Into::into),
- };
- let content_mask = self.content_mask().scale(scale_factor);
- self.window
- .next_frame
- .scene
- .insert_primitive(MonochromeSprite {
- order: 0,
- pad: 0,
- bounds,
- content_mask,
- color,
- tile,
- transformation: TransformationMatrix::unit(),
- });
- }
- Ok(())
- }
-
- /// Paints an emoji glyph into the scene for the next frame at the current z-index.
- ///
- /// The y component of the origin is the baseline of the glyph.
- /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or
- /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem).
- /// This method is only useful if you need to paint a single emoji that has already been shaped.
- pub fn paint_emoji(
- &mut self,
- origin: Point<Pixels>,
- font_id: FontId,
- glyph_id: GlyphId,
- font_size: Pixels,
- ) -> Result<()> {
- let scale_factor = self.scale_factor();
- let glyph_origin = origin.scale(scale_factor);
- let params = RenderGlyphParams {
- font_id,
- glyph_id,
- font_size,
- // We don't render emojis with subpixel variants.
- subpixel_variant: Default::default(),
- scale_factor,
- is_emoji: true,
- };
-
- let raster_bounds = self.text_system().raster_bounds(¶ms)?;
- if !raster_bounds.is_zero() {
- let tile =
- self.window
- .sprite_atlas
- .get_or_insert_with(¶ms.clone().into(), &mut || {
- let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
- Ok((size, Cow::Owned(bytes)))
- })?;
- let bounds = Bounds {
- origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
- size: tile.bounds.size.map(Into::into),
- };
- let content_mask = self.content_mask().scale(scale_factor);
-
- self.window
- .next_frame
- .scene
- .insert_primitive(PolychromeSprite {
- order: 0,
- grayscale: false,
- bounds,
- corner_radii: Default::default(),
- content_mask,
- tile,
- });
- }
- Ok(())
- }
-
- /// Paint a monochrome SVG into the scene for the next frame at the current stacking context.
- pub fn paint_svg(
- &mut self,
- bounds: Bounds<Pixels>,
- path: SharedString,
- transformation: TransformationMatrix,
- color: Hsla,
- ) -> Result<()> {
- let scale_factor = self.scale_factor();
- let bounds = bounds.scale(scale_factor);
- // Render the SVG at twice the size to get a higher quality result.
- let params = RenderSvgParams {
- path,
- size: bounds
- .size
- .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
- };
-
- let tile =
- self.window
- .sprite_atlas
- .get_or_insert_with(¶ms.clone().into(), &mut || {
- let bytes = self.svg_renderer.render(¶ms)?;
- Ok((params.size, Cow::Owned(bytes)))
- })?;
- let content_mask = self.content_mask().scale(scale_factor);
-
- self.window
- .next_frame
- .scene
- .insert_primitive(MonochromeSprite {
- order: 0,
- pad: 0,
- bounds,
- content_mask,
- color,
- tile,
- transformation,
- });
-
- Ok(())
- }
-
- /// Paint an image into the scene for the next frame at the current z-index.
- pub fn paint_image(
- &mut self,
- bounds: Bounds<Pixels>,
- corner_radii: Corners<Pixels>,
- data: Arc<ImageData>,
- grayscale: bool,
- ) -> Result<()> {
- let scale_factor = self.scale_factor();
- let bounds = bounds.scale(scale_factor);
- let params = RenderImageParams { image_id: data.id };
-
- let tile = self
- .window
- .sprite_atlas
- .get_or_insert_with(¶ms.clone().into(), &mut || {
- Ok((data.size(), Cow::Borrowed(data.as_bytes())))
- })?;
- let content_mask = self.content_mask().scale(scale_factor);
- let corner_radii = corner_radii.scale(scale_factor);
-
- self.window
- .next_frame
- .scene
- .insert_primitive(PolychromeSprite {
- order: 0,
- grayscale,
- bounds,
- content_mask,
- corner_radii,
- tile,
- });
- Ok(())
- }
-
- /// Paint a surface into the scene for the next frame at the current z-index.
- #[cfg(target_os = "macos")]
- pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVImageBuffer) {
- let scale_factor = self.scale_factor();
- let bounds = bounds.scale(scale_factor);
- let content_mask = self.content_mask().scale(scale_factor);
- self.window
- .next_frame
- .scene
- .insert_primitive(crate::Surface {
- order: 0,
- bounds,
- content_mask,
- image_buffer,
- });
- }
-
- #[must_use]
- /// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which
- /// layout is being requested, along with the layout ids of any children. This method is called during
- /// calls to the `Element::layout` trait method and enables any element to participate in layout.
- pub fn request_layout(
- &mut self,
- style: &Style,
- children: impl IntoIterator<Item = LayoutId>,
- ) -> LayoutId {
- self.app.layout_id_buffer.clear();
- self.app.layout_id_buffer.extend(children);
- let rem_size = self.rem_size();
-
- self.cx
- .window
- .layout_engine
- .as_mut()
- .unwrap()
- .request_layout(style, rem_size, &self.cx.app.layout_id_buffer)
- }
-
- /// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
- /// this variant takes a function that is invoked during layout so you can use arbitrary logic to
- /// determine the element's size. One place this is used internally is when measuring text.
- ///
- /// The given closure is invoked at layout time with the known dimensions and available space and
- /// returns a `Size`.
- pub fn request_measured_layout<
- F: FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
- + 'static,
- >(
- &mut self,
- style: Style,
- measure: F,
- ) -> LayoutId {
- let rem_size = self.rem_size();
- self.window
- .layout_engine
- .as_mut()
- .unwrap()
- .request_measured_layout(style, rem_size, measure)
- }
-
- /// Compute the layout for the given id within the given available space.
- /// This method is called for its side effect, typically by the framework prior to painting.
- /// After calling it, you can request the bounds of the given layout node id or any descendant.
- pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
- let mut layout_engine = self.window.layout_engine.take().unwrap();
- layout_engine.compute_layout(layout_id, available_space, self);
- self.window.layout_engine = Some(layout_engine);
- }
-
- /// Obtain the bounds computed for the given LayoutId relative to the window. This method will usually be invoked by
- /// GPUI itself automatically in order to pass your element its `Bounds` automatically.
- pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
- let mut bounds = self
- .window
- .layout_engine
- .as_mut()
- .unwrap()
- .layout_bounds(layout_id)
- .map(Into::into);
- bounds.origin += self.element_offset();
- bounds
- }
-
- /// This method should be called during `prepaint`. You can use
- /// the returned [Hitbox] during `paint` or in an event handler
- /// to determine whether the inserted hitbox was the topmost.
- pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox {
- let content_mask = self.content_mask();
- let window = &mut self.window;
- let id = window.next_hitbox_id;
- window.next_hitbox_id.0 += 1;
- let hitbox = Hitbox {
- id,
- bounds,
- content_mask,
- opaque,
- };
- window.next_frame.hitboxes.push(hitbox.clone());
- hitbox
- }
-
- /// Sets the key context for the current element. This context will be used to translate
- /// keybindings into actions.
- pub fn set_key_context(&mut self, context: KeyContext) {
- self.window
- .next_frame
- .dispatch_tree
- .set_key_context(context);
- }
-
- /// Sets the focus handle for the current element. This handle will be used to manage focus state
- /// and keyboard event dispatch for the element.
- pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle) {
- self.window
- .next_frame
- .dispatch_tree
- .set_focus_id(focus_handle.id);
- }
-
- /// Sets the view id for the current element, which will be used to manage view caching.
- pub fn set_view_id(&mut self, view_id: EntityId) {
- self.window.next_frame.dispatch_tree.set_view_id(view_id);
- }
-
- /// Get the last view id for the current element
- pub fn parent_view_id(&mut self) -> Option<EntityId> {
- self.window.next_frame.dispatch_tree.parent_view_id()
- }
-
- /// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the
- /// platform to receive textual input with proper integration with concerns such
- /// as IME interactions. This handler will be active for the upcoming frame until the following frame is
- /// rendered.
- ///
- /// [element_input_handler]: crate::ElementInputHandler
- pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
- if focus_handle.is_focused(self) {
- let cx = self.to_async();
- self.window
- .next_frame
- .input_handlers
- .push(Some(PlatformInputHandler::new(cx, Box::new(input_handler))));
- }
- }
-
- /// Register a mouse event listener on the window for the next frame. The type of event
- /// is determined by the first parameter of the given listener. When the next frame is rendered
- /// the listener will be cleared.
- pub fn on_mouse_event<Event: MouseEvent>(
- &mut self,
- mut handler: impl FnMut(&Event, DispatchPhase, &mut ElementContext) + 'static,
- ) {
- self.window.next_frame.mouse_listeners.push(Some(Box::new(
- move |event: &dyn Any, phase: DispatchPhase, cx: &mut ElementContext<'_>| {
- if let Some(event) = event.downcast_ref() {
- handler(event, phase, cx)
- }
- },
- )));
- }
-
- /// Register a key event listener on the window for the next frame. The type of event
- /// is determined by the first parameter of the given listener. When the next frame is rendered
- /// the listener will be cleared.
- ///
- /// This is a fairly low-level method, so prefer using event handlers on elements unless you have
- /// a specific need to register a global listener.
- pub fn on_key_event<Event: KeyEvent>(
- &mut self,
- listener: impl Fn(&Event, DispatchPhase, &mut ElementContext) + 'static,
- ) {
- self.window.next_frame.dispatch_tree.on_key_event(Rc::new(
- move |event: &dyn Any, phase, cx: &mut ElementContext<'_>| {
- if let Some(event) = event.downcast_ref::<Event>() {
- listener(event, phase, cx)
- }
- },
- ));
- }
-
- /// Register a modifiers changed event listener on the window for the next frame.
- ///
- /// This is a fairly low-level method, so prefer using event handlers on elements unless you have
- /// a specific need to register a global listener.
- pub fn on_modifiers_changed(
- &mut self,
- listener: impl Fn(&ModifiersChangedEvent, &mut ElementContext) + 'static,
- ) {
- self.window
- .next_frame
- .dispatch_tree
- .on_modifiers_changed(Rc::new(
- move |event: &ModifiersChangedEvent, cx: &mut ElementContext<'_>| {
- listener(event, cx)
- },
- ));
- }
-}
@@ -155,7 +155,7 @@ impl FocusableView for ImageView {
impl Render for ImageView {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
- let checkered_background = |bounds: Bounds<Pixels>, _, cx: &mut ElementContext| {
+ let checkered_background = |bounds: Bounds<Pixels>, _, cx: &mut WindowContext| {
let square_size = 32.0;
let start_y = bounds.origin.y.0;
@@ -1,11 +1,11 @@
use editor::{CursorLayout, HighlightedRange, HighlightedRangeLine};
use gpui::{
- div, fill, point, px, relative, AnyElement, Bounds, DispatchPhase, Element, ElementContext,
- FocusHandle, Font, FontStyle, FontWeight, HighlightStyle, Hitbox, Hsla, InputHandler,
- InteractiveElement, Interactivity, IntoElement, LayoutId, Model, ModelContext,
- ModifiersChangedEvent, MouseButton, MouseMoveEvent, Pixels, Point, ShapedLine,
- StatefulInteractiveElement, StrikethroughStyle, Styled, TextRun, TextStyle, UnderlineStyle,
- WeakView, WhiteSpace, WindowContext, WindowTextSystem,
+ div, fill, point, px, relative, AnyElement, Bounds, DispatchPhase, Element, FocusHandle, Font,
+ FontStyle, FontWeight, HighlightStyle, Hitbox, Hsla, InputHandler, InteractiveElement,
+ Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton,
+ MouseMoveEvent, Pixels, Point, ShapedLine, StatefulInteractiveElement, StrikethroughStyle,
+ Styled, TextRun, TextStyle, UnderlineStyle, WeakView, WhiteSpace, WindowContext,
+ WindowTextSystem,
};
use itertools::Itertools;
use language::CursorShape;
@@ -85,7 +85,7 @@ impl LayoutCell {
origin: Point<Pixels>,
layout: &LayoutState,
_visible_bounds: Bounds<Pixels>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let pos = {
let point = self.point;
@@ -124,7 +124,7 @@ impl LayoutRect {
}
}
- fn paint(&self, origin: Point<Pixels>, layout: &LayoutState, cx: &mut ElementContext) {
+ fn paint(&self, origin: Point<Pixels>, layout: &LayoutState, cx: &mut WindowContext) {
let position = {
let alac_point = self.point;
point(
@@ -418,7 +418,7 @@ impl TerminalElement {
origin: Point<Pixels>,
mode: TermMode,
hitbox: &Hitbox,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
let focus = self.focus.clone();
let terminal = self.terminal.clone();
@@ -544,7 +544,7 @@ impl Element for TerminalElement {
type RequestLayoutState = ();
type PrepaintState = LayoutState;
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
self.interactivity.occlude_mouse();
let layout_id = self.interactivity.request_layout(cx, |mut style, cx| {
style.size.width = relative(1.).into();
@@ -560,7 +560,7 @@ impl Element for TerminalElement {
&mut self,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Self::PrepaintState {
self.interactivity
.prepaint(bounds, bounds.size, cx, |_, _, hitbox, cx| {
@@ -777,7 +777,7 @@ impl Element for TerminalElement {
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
layout: &mut Self::PrepaintState,
- cx: &mut ElementContext<'_>,
+ cx: &mut WindowContext<'_>,
) {
cx.paint_quad(fill(bounds, layout.background_color));
let origin = bounds.origin + Point::new(layout.gutter, px(0.));
@@ -2,9 +2,9 @@ use std::{cell::RefCell, rc::Rc};
use gpui::{
anchored, deferred, div, point, prelude::FluentBuilder, px, AnchorCorner, AnyElement, Bounds,
- DismissEvent, DispatchPhase, Element, ElementContext, ElementId, HitboxId, InteractiveElement,
- IntoElement, LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View,
- VisualContext, WindowContext,
+ DismissEvent, DispatchPhase, Element, ElementId, HitboxId, InteractiveElement, IntoElement,
+ LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext,
+ WindowContext,
};
use crate::prelude::*;
@@ -112,8 +112,8 @@ impl<M: ManagedView> PopoverMenu<M> {
fn with_element_state<R>(
&mut self,
- cx: &mut ElementContext,
- f: impl FnOnce(&mut Self, &mut PopoverMenuElementState<M>, &mut ElementContext) -> R,
+ cx: &mut WindowContext,
+ f: impl FnOnce(&mut Self, &mut PopoverMenuElementState<M>, &mut WindowContext) -> R,
) -> R {
cx.with_element_state::<PopoverMenuElementState<M>, _>(
Some(self.id.clone()),
@@ -173,7 +173,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
fn request_layout(
&mut self,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
self.with_element_state(cx, |this, element_state, cx| {
let mut menu_layout_id = None;
@@ -221,7 +221,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
&mut self,
_bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> Option<HitboxId> {
self.with_element_state(cx, |_this, element_state, cx| {
if let Some(child) = request_layout.child_element.as_mut() {
@@ -245,7 +245,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
_: Bounds<gpui::Pixels>,
request_layout: &mut Self::RequestLayoutState,
child_hitbox: &mut Option<HitboxId>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
self.with_element_state(cx, |_this, _element_state, cx| {
if let Some(mut child) = request_layout.child_element.take() {
@@ -2,9 +2,8 @@ use std::{cell::RefCell, rc::Rc};
use gpui::{
anchored, deferred, div, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase,
- Element, ElementContext, ElementId, Hitbox, InteractiveElement, IntoElement, LayoutId,
- ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext,
- WindowContext,
+ Element, ElementId, Hitbox, InteractiveElement, IntoElement, LayoutId, ManagedView,
+ MouseButton, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext,
};
pub struct RightClickMenu<M: ManagedView> {
@@ -41,8 +40,8 @@ impl<M: ManagedView> RightClickMenu<M> {
fn with_element_state<R>(
&mut self,
- cx: &mut ElementContext,
- f: impl FnOnce(&mut Self, &mut MenuHandleElementState<M>, &mut ElementContext) -> R,
+ cx: &mut WindowContext,
+ f: impl FnOnce(&mut Self, &mut MenuHandleElementState<M>, &mut WindowContext) -> R,
) -> R {
cx.with_element_state::<MenuHandleElementState<M>, _>(
Some(self.id.clone()),
@@ -89,19 +88,24 @@ impl<M> Default for MenuHandleElementState<M> {
}
}
-pub struct MenuHandleFrameState {
+pub struct RequestLayoutState {
child_layout_id: Option<LayoutId>,
child_element: Option<AnyElement>,
menu_element: Option<AnyElement>,
}
+pub struct PrepaintState {
+ hitbox: Hitbox,
+ child_bounds: Option<Bounds<Pixels>>,
+}
+
impl<M: ManagedView> Element for RightClickMenu<M> {
- type RequestLayoutState = MenuHandleFrameState;
- type PrepaintState = Hitbox;
+ type RequestLayoutState = RequestLayoutState;
+ type PrepaintState = PrepaintState;
fn request_layout(
&mut self,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
self.with_element_state(cx, |this, element_state, cx| {
let mut menu_layout_id = None;
@@ -137,7 +141,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
(
layout_id,
- MenuHandleFrameState {
+ RequestLayoutState {
child_element,
child_layout_id,
menu_element,
@@ -150,8 +154,8 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
&mut self,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
- ) -> Hitbox {
+ cx: &mut WindowContext,
+ ) -> PrepaintState {
cx.with_element_id(Some(self.id.clone()), |cx| {
let hitbox = cx.insert_hitbox(bounds, false);
@@ -163,7 +167,12 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
menu.prepaint(cx);
}
- hitbox
+ PrepaintState {
+ hitbox,
+ child_bounds: request_layout
+ .child_layout_id
+ .map(|layout_id| cx.layout_bounds(layout_id)),
+ }
})
}
@@ -171,8 +180,8 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
&mut self,
_bounds: Bounds<gpui::Pixels>,
request_layout: &mut Self::RequestLayoutState,
- hitbox: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ prepaint_state: &mut Self::PrepaintState,
+ cx: &mut WindowContext,
) {
self.with_element_state(cx, |this, element_state, cx| {
if let Some(mut child) = request_layout.child_element.take() {
@@ -191,10 +200,9 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
let attach = this.attach;
let menu = element_state.menu.clone();
let position = element_state.position.clone();
- let child_layout_id = request_layout.child_layout_id;
- let child_bounds = cx.layout_bounds(child_layout_id.unwrap());
+ let child_bounds = prepaint_state.child_bounds;
- let hitbox_id = hitbox.id;
+ let hitbox_id = prepaint_state.hitbox.id;
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == MouseButton::Right
@@ -219,7 +227,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
.detach();
cx.focus_view(&new_menu);
*menu.borrow_mut() = Some(new_menu);
- *position.borrow_mut() = if child_layout_id.is_some() {
+ *position.borrow_mut() = if let Some(child_bounds) = child_bounds {
if let Some(attach) = attach {
attach.corner(child_bounds)
} else {
@@ -2,9 +2,9 @@
pub use gpui::prelude::*;
pub use gpui::{
- div, px, relative, rems, AbsoluteLength, DefiniteLength, Div, Element, ElementContext,
- ElementId, InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString, Styled,
- ViewContext, WindowContext,
+ div, px, relative, rems, AbsoluteLength, DefiniteLength, Div, Element, ElementId,
+ InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString, Styled, ViewContext,
+ WindowContext,
};
pub use crate::clickable::*;
@@ -759,7 +759,7 @@ mod element {
fn layout_handle(
axis: Axis,
pane_bounds: Bounds<Pixels>,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> PaneAxisHandleLayout {
let handle_bounds = Bounds {
origin: pane_bounds.origin.apply_along(axis, |origin| {
@@ -797,7 +797,7 @@ mod element {
fn request_layout(
&mut self,
- cx: &mut ui::prelude::ElementContext,
+ cx: &mut ui::prelude::WindowContext,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
let mut style = Style::default();
style.flex_grow = 1.;
@@ -812,7 +812,7 @@ mod element {
&mut self,
bounds: Bounds<Pixels>,
_state: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) -> PaneAxisLayout {
let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
Some(self.basis.into()),
@@ -900,7 +900,7 @@ mod element {
bounds: gpui::Bounds<ui::prelude::Pixels>,
_: &mut Self::RequestLayoutState,
layout: &mut Self::PrepaintState,
- cx: &mut ui::prelude::ElementContext,
+ cx: &mut ui::prelude::WindowContext,
) {
for child in &mut layout.children {
child.element.paint(cx);
@@ -78,9 +78,9 @@ use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
pub use ui;
use ui::{
- div, h_flex, Context as _, Div, Element, ElementContext, FluentBuilder,
- InteractiveElement as _, IntoElement, Label, ParentElement as _, Pixels, SharedString,
- Styled as _, ViewContext, VisualContext as _, WindowContext,
+ div, h_flex, Context as _, Div, Element, FluentBuilder, InteractiveElement as _, IntoElement,
+ Label, ParentElement as _, Pixels, SharedString, Styled as _, ViewContext, VisualContext as _,
+ WindowContext,
};
use util::{maybe, ResultExt};
use uuid::Uuid;
@@ -4991,7 +4991,7 @@ impl Element for DisconnectedOverlay {
type RequestLayoutState = AnyElement;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let mut background = cx.theme().colors().elevated_surface_background;
background.fade_out(0.2);
let mut overlay = div()
@@ -5016,7 +5016,7 @@ impl Element for DisconnectedOverlay {
&mut self,
bounds: Bounds<Pixels>,
overlay: &mut Self::RequestLayoutState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
cx.insert_hitbox(bounds, true);
overlay.prepaint(cx);
@@ -5027,7 +5027,7 @@ impl Element for DisconnectedOverlay {
_: Bounds<Pixels>,
overlay: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
- cx: &mut ElementContext,
+ cx: &mut WindowContext,
) {
overlay.paint(cx)
}