diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index 257cea3085..9f2db136bd 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -23,12 +23,11 @@ //! use crate::{ - point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext, - Bounds, ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, - IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, - ScrollWheelEvent, SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, - View, Visibility, WindowContext, + point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds, ClickEvent, + DispatchPhase, Element, ElementContext, ElementId, FocusHandle, IntoElement, IsZero, + KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent, + MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, + StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext, }; use collections::HashMap; diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index ed09901242..c16fb32a52 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -7,8 +7,8 @@ //! If all of your elements are the same height, see [`UniformList`] for a simpler API use crate::{ - point, px, AnyElement, AvailableSpace, BorrowAppContext, Bounds, ContentMask, DispatchPhase, - Element, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled, + point, px, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Element, + IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled, WindowContext, }; use collections::VecDeque; diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index 954460e856..929e9ebfb4 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -220,11 +220,6 @@ pub trait EventEmitter: 'static {} /// A helper trait for auto-implementing certain methods on contexts that /// can be used interchangeably. pub trait BorrowAppContext { - /// Run a closure with a text style pushed onto the context. - fn with_text_style(&mut self, style: Option, f: F) -> R - where - F: FnOnce(&mut Self) -> R; - /// Set a global value on the context. fn set_global(&mut self, global: T); } @@ -233,20 +228,6 @@ impl BorrowAppContext for C where C: BorrowMut, { - fn with_text_style(&mut self, style: Option, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - if let Some(style) = style { - self.borrow_mut().push_text_style(style); - let result = f(self); - self.borrow_mut().pop_text_style(); - result - } else { - f(self) - } - } - fn set_global(&mut self, global: G) { self.borrow_mut().set_global(global) } diff --git a/crates/gpui/src/style.rs b/crates/gpui/src/style.rs index 179da32070..c50cf34cfa 100644 --- a/crates/gpui/src/style.rs +++ b/crates/gpui/src/style.rs @@ -308,18 +308,6 @@ impl Style { } } - pub fn apply_text_style(&self, cx: &mut C, f: F) -> R - where - C: BorrowAppContext, - F: FnOnce(&mut C) -> R, - { - if self.text.is_some() { - cx.with_text_style(Some(self.text.clone()), f) - } else { - f(cx) - } - } - /// Paints the background of an element styled with this style. pub fn paint( &self, diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 9ed6087302..5bf40510f7 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1,16 +1,15 @@ use crate::{ - px, size, transparent_black, Action, AnyDrag, AnyTooltip, AnyView, AppContext, Arena, - AsyncWindowContext, AvailableSpace, Bounds, Context, Corners, CursorStyle, - DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, - EntityId, EventEmitter, FileDropEvent, Flatten, GlobalElementId, Hsla, KeyBinding, KeyContext, - KeyDownEvent, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent, - MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, - PlatformWindow, Point, PromptLevel, Render, ScaledPixels, Scene, SharedString, Size, - SubscriberSet, Subscription, TaffyLayoutEngine, Task, View, VisualContext, WeakView, - WindowBounds, WindowOptions, + px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext, + AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId, + DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, + GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, Model, + ModelContext, Modifiers, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, + PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render, ScaledPixels, + SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task, View, VisualContext, + WeakView, WindowBounds, WindowOptions, }; use anyhow::{anyhow, Context as _, Result}; -use collections::{FxHashMap, FxHashSet}; +use collections::FxHashSet; use derive_more::{Deref, DerefMut}; use futures::{ channel::{mpsc, oneshot}, @@ -97,7 +96,7 @@ impl DispatchPhase { } type AnyObserver = Box bool + 'static>; -type AnyMouseListener = Box; + type AnyWindowFocusListener = Box bool + 'static>; struct FocusEvent { @@ -286,16 +285,6 @@ pub struct Window { pub(crate) focus_invalidated: bool, } -struct RequestedInputHandler { - view_id: EntityId, - handler: Option, -} - -struct TooltipRequest { - view_id: EntityId, - tooltip: AnyTooltip, -} - pub(crate) struct ElementStateBox { pub(crate) inner: Box, pub(crate) parent_view_id: EntityId, @@ -303,116 +292,6 @@ pub(crate) struct ElementStateBox { pub(crate) type_name: &'static str, } -pub(crate) struct Frame { - focus: Option, - window_active: bool, - pub(crate) element_states: FxHashMap, - mouse_listeners: FxHashMap>, - pub(crate) dispatch_tree: DispatchTree, - pub(crate) scene: Scene, - pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds)>, - pub(crate) z_index_stack: StackingOrder, - pub(crate) next_stacking_order_id: u32, - pub(crate) next_root_z_index: u8, - pub(crate) content_mask_stack: Vec>, - pub(crate) element_offset_stack: Vec>, - requested_input_handler: Option, - tooltip_request: Option, - cursor_styles: FxHashMap, - requested_cursor_style: Option, - pub(crate) view_stack: Vec, - pub(crate) reused_views: FxHashSet, - - #[cfg(any(test, feature = "test-support"))] - pub(crate) debug_bounds: collections::FxHashMap>, -} - -impl Frame { - fn new(dispatch_tree: DispatchTree) -> Self { - Frame { - focus: None, - window_active: false, - element_states: FxHashMap::default(), - mouse_listeners: FxHashMap::default(), - dispatch_tree, - scene: Scene::default(), - depth_map: Vec::new(), - z_index_stack: StackingOrder::default(), - next_stacking_order_id: 0, - next_root_z_index: 0, - content_mask_stack: Vec::new(), - element_offset_stack: Vec::new(), - requested_input_handler: None, - tooltip_request: None, - cursor_styles: FxHashMap::default(), - requested_cursor_style: None, - view_stack: Vec::new(), - reused_views: FxHashSet::default(), - - #[cfg(any(test, feature = "test-support"))] - debug_bounds: FxHashMap::default(), - } - } - - fn clear(&mut self) { - self.element_states.clear(); - self.mouse_listeners.values_mut().for_each(Vec::clear); - self.dispatch_tree.clear(); - self.depth_map.clear(); - self.next_stacking_order_id = 0; - self.next_root_z_index = 0; - self.reused_views.clear(); - self.scene.clear(); - self.requested_input_handler.take(); - self.tooltip_request.take(); - self.cursor_styles.clear(); - self.requested_cursor_style.take(); - debug_assert_eq!(self.view_stack.len(), 0); - } - - fn focus_path(&self) -> SmallVec<[FocusId; 8]> { - self.focus - .map(|focus_id| self.dispatch_tree.focus_path(focus_id)) - .unwrap_or_default() - } - - fn finish(&mut self, prev_frame: &mut Self) { - // Reuse mouse listeners that didn't change since the last frame. - for (type_id, listeners) in &mut prev_frame.mouse_listeners { - let next_listeners = self.mouse_listeners.entry(*type_id).or_default(); - for (order, view_id, listener) in listeners.drain(..) { - if self.reused_views.contains(&view_id) { - next_listeners.push((order, view_id, listener)); - } - } - } - - // Reuse entries in the depth map that didn't change since the last frame. - for (order, view_id, bounds) in prev_frame.depth_map.drain(..) { - if self.reused_views.contains(&view_id) { - match self - .depth_map - .binary_search_by(|(level, _, _)| order.cmp(level)) - { - Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)), - } - } - } - - // Retain element states for views that didn't change since the last frame. - for (element_id, state) in prev_frame.element_states.drain() { - if self.reused_views.contains(&state.parent_view_id) { - self.element_states.entry(element_id).or_insert(state); - } - } - - // Reuse geometry that didn't change since the last frame. - self.scene - .reuse_views(&self.reused_views, &mut prev_frame.scene); - self.scene.finish(); - } -} - impl Window { pub(crate) fn new( handle: AnyWindowHandle, @@ -925,19 +804,6 @@ impl<'a> WindowContext<'a> { self.window.modifiers } - /// Updates the cursor style at the platform level. - pub fn set_cursor_style(&mut self, style: CursorStyle) { - let view_id = self.parent_view_id(); - self.window.next_frame.cursor_styles.insert(view_id, style); - self.window.next_frame.requested_cursor_style = Some(style); - } - - /// Sets a tooltip to be rendered for the upcoming frame - pub fn set_tooltip(&mut self, tooltip: AnyTooltip) { - let view_id = self.parent_view_id(); - self.window.next_frame.tooltip_request = Some(TooltipRequest { view_id, tooltip }); - } - /// Returns true if there is no opaque layer containing the given point /// on top of the given level. Layers whose level is an extension of the /// level are not considered to be on top of the level. @@ -979,48 +845,6 @@ impl<'a> WindowContext<'a> { &self.window.next_frame.z_index_stack } - pub(crate) fn reuse_view(&mut self) { - let view_id = self.parent_view_id(); - let grafted_view_ids = self - .window - .next_frame - .dispatch_tree - .reuse_view(view_id, &mut self.window.rendered_frame.dispatch_tree); - for view_id in grafted_view_ids { - assert!(self.window.next_frame.reused_views.insert(view_id)); - - // Reuse the previous input handler requested during painting of the reused view. - if self - .window - .rendered_frame - .requested_input_handler - .as_ref() - .map_or(false, |requested| requested.view_id == view_id) - { - self.window.next_frame.requested_input_handler = - self.window.rendered_frame.requested_input_handler.take(); - } - - // Reuse the tooltip previously requested during painting of the reused view. - if self - .window - .rendered_frame - .tooltip_request - .as_ref() - .map_or(false, |requested| requested.view_id == view_id) - { - self.window.next_frame.tooltip_request = - self.window.rendered_frame.tooltip_request.take(); - } - - // Reuse the cursor styles previously requested during painting of the reused view. - if let Some(style) = self.window.rendered_frame.cursor_styles.remove(&view_id) { - self.window.next_frame.cursor_styles.insert(view_id, style); - self.window.next_frame.requested_cursor_style = Some(style); - } - } - } - /// Draw pixels to the display for this window based on the contents of its scene. pub(crate) fn draw(&mut self) { self.window.dirty = false; diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index b7c6325751..9274082c5d 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -7,21 +7,144 @@ use std::{ }; use anyhow::Result; +use collections::{FxHashMap, FxHashSet}; use derive_more::{Deref, DerefMut}; use media::core_video::CVImageBuffer; +use smallvec::SmallVec; use util::post_inc; use crate::{ - prelude::*, size, AppContext, AvailableSpace, Bounds, BoxShadow, ContentMask, Corners, - DevicePixels, DispatchPhase, ElementId, ElementStateBox, EntityId, FocusHandle, FontId, - GlyphId, Hsla, ImageData, InputHandler, IsZero, KeyContext, KeyEvent, LayoutId, - MonochromeSprite, MouseEvent, PaintQuad, Path, Pixels, PlatformInputHandler, Point, - PolychromeSprite, Quad, RenderGlyphParams, RenderImageParams, RenderSvgParams, Shadow, - SharedString, Size, Style, Surface, Underline, UnderlineStyle, Window, WindowContext, + prelude::*, size, AnyTooltip, AppContext, AvailableSpace, Bounds, BoxShadow, ContentMask, + Corners, CursorStyle, DevicePixels, DispatchPhase, DispatchTree, ElementId, ElementStateBox, + EntityId, FocusHandle, FocusId, FontId, GlobalElementId, GlyphId, Hsla, ImageData, + InputHandler, IsZero, KeyContext, KeyEvent, LayoutId, MonochromeSprite, MouseEvent, PaintQuad, + Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams, + RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, StackingOrder, Style, + Surface, TextStyleRefinement, Underline, UnderlineStyle, Window, WindowContext, SUBPIXEL_VARIANTS, }; -use super::RequestedInputHandler; +type AnyMouseListener = Box; + +pub(crate) struct RequestedInputHandler { + pub(crate) view_id: EntityId, + pub(crate) handler: Option, +} + +pub(crate) struct TooltipRequest { + pub(crate) view_id: EntityId, + pub(crate) tooltip: AnyTooltip, +} + +pub(crate) struct Frame { + pub(crate) focus: Option, + pub(crate) window_active: bool, + pub(crate) element_states: FxHashMap, + pub(crate) mouse_listeners: FxHashMap>, + pub(crate) dispatch_tree: DispatchTree, + pub(crate) scene: Scene, + pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds)>, + pub(crate) z_index_stack: StackingOrder, + pub(crate) next_stacking_order_id: u32, + pub(crate) next_root_z_index: u8, + pub(crate) content_mask_stack: Vec>, + pub(crate) element_offset_stack: Vec>, + pub(crate) requested_input_handler: Option, + pub(crate) tooltip_request: Option, + pub(crate) cursor_styles: FxHashMap, + pub(crate) requested_cursor_style: Option, + pub(crate) view_stack: Vec, + pub(crate) reused_views: FxHashSet, + + #[cfg(any(test, feature = "test-support"))] + pub(crate) debug_bounds: collections::FxHashMap>, +} + +impl Frame { + pub(crate) fn new(dispatch_tree: DispatchTree) -> Self { + Frame { + focus: None, + window_active: false, + element_states: FxHashMap::default(), + mouse_listeners: FxHashMap::default(), + dispatch_tree, + scene: Scene::default(), + depth_map: Vec::new(), + z_index_stack: StackingOrder::default(), + next_stacking_order_id: 0, + next_root_z_index: 0, + content_mask_stack: Vec::new(), + element_offset_stack: Vec::new(), + requested_input_handler: None, + tooltip_request: None, + cursor_styles: FxHashMap::default(), + requested_cursor_style: None, + view_stack: Vec::new(), + reused_views: FxHashSet::default(), + + #[cfg(any(test, feature = "test-support"))] + debug_bounds: FxHashMap::default(), + } + } + + pub(crate) fn clear(&mut self) { + self.element_states.clear(); + self.mouse_listeners.values_mut().for_each(Vec::clear); + self.dispatch_tree.clear(); + self.depth_map.clear(); + self.next_stacking_order_id = 0; + self.next_root_z_index = 0; + self.reused_views.clear(); + self.scene.clear(); + self.requested_input_handler.take(); + self.tooltip_request.take(); + self.cursor_styles.clear(); + self.requested_cursor_style.take(); + debug_assert_eq!(self.view_stack.len(), 0); + } + + 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) { + // Reuse mouse listeners that didn't change since the last frame. + for (type_id, listeners) in &mut prev_frame.mouse_listeners { + let next_listeners = self.mouse_listeners.entry(*type_id).or_default(); + for (order, view_id, listener) in listeners.drain(..) { + if self.reused_views.contains(&view_id) { + next_listeners.push((order, view_id, listener)); + } + } + } + + // Reuse entries in the depth map that didn't change since the last frame. + for (order, view_id, bounds) in prev_frame.depth_map.drain(..) { + if self.reused_views.contains(&view_id) { + match self + .depth_map + .binary_search_by(|(level, _, _)| order.cmp(level)) + { + Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)), + } + } + } + + // Retain element states for views that didn't change since the last frame. + for (element_id, state) in prev_frame.element_states.drain() { + if self.reused_views.contains(&state.parent_view_id) { + self.element_states.entry(element_id).or_insert(state); + } + } + + // Reuse geometry that didn't change since the last frame. + self.scene + .reuse_views(&self.reused_views, &mut prev_frame.scene); + self.scene.finish(); + } +} /// This context is used for assisting in the implementation of the element trait #[derive(Deref, DerefMut)] @@ -169,6 +292,76 @@ impl<'a> VisualContext for ElementContext<'a> { } impl<'a> ElementContext<'a> { + pub(crate) fn reuse_view(&mut self) { + let view_id = self.parent_view_id(); + let grafted_view_ids = self + .cx + .window + .next_frame + .dispatch_tree + .reuse_view(view_id, &mut self.cx.window.rendered_frame.dispatch_tree); + for view_id in grafted_view_ids { + assert!(self.window.next_frame.reused_views.insert(view_id)); + + // Reuse the previous input handler requested during painting of the reused view. + if self + .window + .rendered_frame + .requested_input_handler + .as_ref() + .map_or(false, |requested| requested.view_id == view_id) + { + self.window.next_frame.requested_input_handler = + self.window.rendered_frame.requested_input_handler.take(); + } + + // Reuse the tooltip previously requested during painting of the reused view. + if self + .window + .rendered_frame + .tooltip_request + .as_ref() + .map_or(false, |requested| requested.view_id == view_id) + { + self.window.next_frame.tooltip_request = + self.window.rendered_frame.tooltip_request.take(); + } + + // Reuse the cursor styles previously requested during painting of the reused view. + if let Some(style) = self.window.rendered_frame.cursor_styles.remove(&view_id) { + self.window.next_frame.cursor_styles.insert(view_id, style); + self.window.next_frame.requested_cursor_style = Some(style); + } + } + } + + pub fn with_text_style(&mut self, style: Option, f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + if let Some(style) = style { + self.push_text_style(style); + let result = f(self); + self.pop_text_style(); + result + } else { + f(self) + } + } + + /// Updates the cursor style at the platform level. + pub fn set_cursor_style(&mut self, style: CursorStyle) { + let view_id = self.parent_view_id(); + self.window.next_frame.cursor_styles.insert(view_id, style); + self.window.next_frame.requested_cursor_style = Some(style); + } + + /// Sets a tooltip to be rendered for the upcoming frame + pub fn set_tooltip(&mut self, tooltip: AnyTooltip) { + let view_id = self.parent_view_id(); + self.window.next_frame.tooltip_request = Some(TooltipRequest { view_id, tooltip }); + } + /// 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 diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index cd35f9fdcc..c67fbfc4d0 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -365,7 +365,7 @@ impl TerminalElement { result } - fn compute_layout(&self, bounds: Bounds, cx: &mut WindowContext) -> LayoutState { + fn compute_layout(&self, bounds: Bounds, cx: &mut ElementContext) -> LayoutState { let settings = ThemeSettings::get_global(cx).clone(); let buffer_font_size = settings.buffer_font_size(cx);