Merge branch 'main' into focus-handlers-on-draw

This commit is contained in:
Julia 2023-12-14 23:08:23 -05:00
commit 4be84f3db0
108 changed files with 4470 additions and 3168 deletions

View file

@ -1091,6 +1091,12 @@ impl AppContext {
pub fn has_active_drag(&self) -> bool {
self.active_drag.is_some()
}
pub fn active_drag<T: 'static>(&self) -> Option<&T> {
self.active_drag
.as_ref()
.and_then(|drag| drag.value.downcast_ref())
}
}
impl Context for AppContext {
@ -1241,6 +1247,7 @@ impl<G: 'static> DerefMut for GlobalLease<G> {
/// within the window or by dragging into the app from the underlying platform.
pub struct AnyDrag {
pub view: AnyView,
pub value: Box<dyn Any>,
pub cursor_offset: Point<Pixels>,
}

View file

@ -23,7 +23,7 @@ pub trait IntoElement: Sized {
self.into_element().into_any()
}
fn draw<T, R>(
fn draw_and_update_state<T, R>(
self,
origin: Point<Pixels>,
available_space: Size<T>,
@ -92,7 +92,7 @@ pub trait Element: 'static + IntoElement {
cx: &mut WindowContext,
) -> (LayoutId, Self::State);
fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
fn into_any(self) -> AnyElement {
AnyElement::new(self)
@ -150,8 +150,8 @@ impl<C: RenderOnce> Element for Component<C> {
}
}
fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
let element = state.rendered_element.take().unwrap();
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
let mut element = state.rendered_element.take().unwrap();
if let Some(element_id) = element.element_id() {
cx.with_element_state(element_id, |element_state, cx| {
let mut element_state = element_state.unwrap();
@ -420,7 +420,7 @@ impl AnyElement {
self.0.layout(cx)
}
pub fn paint(mut self, cx: &mut WindowContext) {
pub fn paint(&mut self, cx: &mut WindowContext) {
self.0.paint(cx)
}
@ -435,7 +435,7 @@ impl AnyElement {
/// Initializes this element and performs layout in the available space, then paints it at the given origin.
pub fn draw(
mut self,
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut WindowContext,
@ -465,8 +465,8 @@ impl Element for AnyElement {
(layout_id, ())
}
fn paint(self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
self.paint(cx);
fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
self.paint(cx)
}
}
@ -482,48 +482,37 @@ impl IntoElement for AnyElement {
}
}
// impl<V, E, F> Element for Option<F>
// where
// V: 'static,
// E: Element,
// F: FnOnce(&mut V, &mut WindowContext<'_, V>) -> E + 'static,
// {
// type State = Option<AnyElement>;
/// The empty element, which renders nothing.
pub type Empty = ();
// fn element_id(&self) -> Option<ElementId> {
// None
// }
impl IntoElement for () {
type Element = Self;
// fn layout(
// &mut self,
// _: Option<Self::State>,
// cx: &mut WindowContext,
// ) -> (LayoutId, Self::State) {
// let render = self.take().unwrap();
// let mut element = (render)(view_state, cx).into_any();
// let layout_id = element.layout(view_state, cx);
// (layout_id, Some(element))
// }
fn element_id(&self) -> Option<ElementId> {
None
}
// fn paint(
// self,
// _bounds: Bounds<Pixels>,
// rendered_element: &mut Self::State,
// cx: &mut WindowContext,
// ) {
// rendered_element.take().unwrap().paint(view_state, cx);
// }
// }
fn into_element(self) -> Self::Element {
self
}
}
// impl<V, E, F> RenderOnce for Option<F>
// where
// V: 'static,
// E: Element,
// F: FnOnce(&mut V, &mut WindowContext) -> E + 'static,
// {
// type Element = Self;
impl Element for () {
type State = ();
// fn render(self) -> Self::Element {
// self
// }
// }
fn layout(
&mut self,
_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
(cx.request_layout(&crate::Style::default(), None), ())
}
fn paint(
&mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::State,
_cx: &mut WindowContext,
) {
}
}

View file

@ -2,15 +2,15 @@ use refineable::Refineable as _;
use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
pub fn canvas(callback: impl 'static + FnOnce(Bounds<Pixels>, &mut WindowContext)) -> Canvas {
pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContext)) -> Canvas {
Canvas {
paint_callback: Box::new(callback),
paint_callback: Some(Box::new(callback)),
style: StyleRefinement::default(),
}
}
pub struct Canvas {
paint_callback: Box<dyn FnOnce(Bounds<Pixels>, &mut WindowContext)>,
paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut WindowContext)>>,
style: StyleRefinement,
}
@ -27,7 +27,7 @@ impl IntoElement for Canvas {
}
impl Element for Canvas {
type State = ();
type State = Style;
fn layout(
&mut self,
@ -37,11 +37,13 @@ impl Element for Canvas {
let mut style = Style::default();
style.refine(&self.style);
let layout_id = cx.request_layout(&style, []);
(layout_id, ())
(layout_id, style)
}
fn paint(self, bounds: Bounds<Pixels>, _: &mut (), cx: &mut WindowContext) {
(self.paint_callback)(bounds, cx)
fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut WindowContext) {
style.paint(bounds, cx, |cx| {
(self.paint_callback.take().unwrap())(&bounds, cx)
});
}
}

File diff suppressed because it is too large Load diff

View file

@ -81,11 +81,12 @@ impl Element for Img {
}
fn paint(
self,
&mut self,
bounds: Bounds<Pixels>,
element_state: &mut Self::State,
cx: &mut WindowContext,
) {
let source = self.source.clone();
self.interactivity.paint(
bounds,
bounds.size,
@ -94,7 +95,7 @@ impl Element for Img {
|style, _scroll_offset, cx| {
let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| {
match self.source {
match source {
ImageSource::Uri(uri) => {
let image_future = cx.image_cache.get(uri.clone());
if let Some(data) = image_future

View file

@ -257,7 +257,7 @@ impl Element for List {
}
fn paint(
self,
&mut self,
bounds: crate::Bounds<crate::Pixels>,
_state: &mut Self::State,
cx: &mut crate::WindowContext,
@ -385,7 +385,7 @@ impl Element for List {
// Paint the visible items
let mut item_origin = bounds.origin;
item_origin.y -= scroll_top.offset_in_item;
for mut item_element in item_elements {
for item_element in &mut item_elements {
let item_height = item_element.measure(available_item_space, cx).height;
item_element.draw(item_origin, available_item_space, cx);
item_origin.y += item_height;

View file

@ -81,7 +81,7 @@ impl Element for Overlay {
}
fn paint(
self,
&mut self,
bounds: crate::Bounds<crate::Pixels>,
element_state: &mut Self::State,
cx: &mut WindowContext,
@ -149,7 +149,7 @@ impl Element for Overlay {
cx.with_element_offset(desired.origin - bounds.origin, |cx| {
cx.break_content_mask(|cx| {
for child in self.children {
for child in &mut self.children {
child.paint(cx);
}
})

View file

@ -36,8 +36,12 @@ impl Element for Svg {
})
}
fn paint(self, bounds: Bounds<Pixels>, element_state: &mut Self::State, cx: &mut WindowContext)
where
fn paint(
&mut self,
bounds: Bounds<Pixels>,
element_state: &mut Self::State,
cx: &mut WindowContext,
) where
Self: Sized,
{
self.interactivity

View file

@ -6,7 +6,7 @@ use crate::{
use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard};
use smallvec::SmallVec;
use std::{cell::Cell, ops::Range, rc::Rc, sync::Arc};
use std::{cell::Cell, mem, ops::Range, rc::Rc, sync::Arc};
use util::ResultExt;
impl Element for &'static str {
@ -22,7 +22,7 @@ impl Element for &'static str {
(layout_id, state)
}
fn paint(self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
state.paint(bounds, self, cx)
}
}
@ -52,7 +52,7 @@ impl Element for SharedString {
(layout_id, state)
}
fn paint(self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
let text_str: &str = self.as_ref();
state.paint(bounds, text_str, cx)
}
@ -128,7 +128,7 @@ impl Element for StyledText {
(layout_id, state)
}
fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
state.paint(bounds, &self.text, cx)
}
}
@ -356,8 +356,8 @@ impl Element for InteractiveText {
}
}
fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
if let Some(click_listener) = self.click_listener {
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
if let Some(click_listener) = self.click_listener.take() {
if let Some(ix) = state
.text_state
.index_for_position(bounds, cx.mouse_position())
@ -374,13 +374,14 @@ impl Element for InteractiveText {
let text_state = state.text_state.clone();
let mouse_down = state.mouse_down_index.clone();
if let Some(mouse_down_index) = mouse_down.get() {
let clickable_ranges = mem::take(&mut self.clickable_ranges);
cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Bubble {
if let Some(mouse_up_index) =
text_state.index_for_position(bounds, event.position)
{
click_listener(
&self.clickable_ranges,
&clickable_ranges,
InteractiveTextClickEvent {
mouse_down_index,
mouse_up_index,

View file

@ -10,6 +10,7 @@ use taffy::style::Overflow;
/// uniform_list provides lazy rendering for a set of items that are of uniform height.
/// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
/// uniform_list will only render the visible subset of items.
#[track_caller]
pub fn uniform_list<I, R, V>(
view: View<V>,
id: I,
@ -42,6 +43,10 @@ where
interactivity: Interactivity {
element_id: Some(id.into()),
base_style: Box::new(base_style),
#[cfg(debug_assertions)]
location: Some(*core::panic::Location::caller()),
..Default::default()
},
scroll_handle: None,
@ -150,7 +155,7 @@ impl Element for UniformList {
}
fn paint(
self,
&mut self,
bounds: Bounds<crate::Pixels>,
element_state: &mut Self::State,
cx: &mut WindowContext,
@ -197,41 +202,41 @@ impl Element for UniformList {
);
cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
style.paint(bounds, cx);
style.paint(bounds, cx, |cx| {
if self.item_count > 0 {
if let Some(scroll_handle) = self.scroll_handle.clone() {
scroll_handle.0.borrow_mut().replace(ScrollHandleState {
item_height,
list_height: padded_bounds.size.height,
scroll_offset: shared_scroll_offset,
});
}
if self.item_count > 0 {
if let Some(scroll_handle) = self.scroll_handle.clone() {
scroll_handle.0.borrow_mut().replace(ScrollHandleState {
item_height,
list_height: padded_bounds.size.height,
scroll_offset: shared_scroll_offset,
let first_visible_element_ix =
(-scroll_offset.y / item_height).floor() as usize;
let last_visible_element_ix =
((-scroll_offset.y + padded_bounds.size.height) / item_height)
.ceil() as usize;
let visible_range = first_visible_element_ix
..cmp::min(last_visible_element_ix, self.item_count);
let mut items = (self.render_items)(visible_range.clone(), cx);
cx.with_z_index(1, |cx| {
let content_mask = ContentMask { bounds };
cx.with_content_mask(Some(content_mask), |cx| {
for (item, ix) in items.iter_mut().zip(visible_range) {
let item_origin = padded_bounds.origin
+ point(px(0.), item_height * ix + scroll_offset.y);
let available_space = size(
AvailableSpace::Definite(padded_bounds.size.width),
AvailableSpace::Definite(item_height),
);
item.draw(item_origin, available_space, cx);
}
});
});
}
let first_visible_element_ix =
(-scroll_offset.y / item_height).floor() as usize;
let last_visible_element_ix =
((-scroll_offset.y + padded_bounds.size.height) / item_height).ceil()
as usize;
let visible_range = first_visible_element_ix
..cmp::min(last_visible_element_ix, self.item_count);
let items = (self.render_items)(visible_range.clone(), cx);
cx.with_z_index(1, |cx| {
let content_mask = ContentMask { bounds };
cx.with_content_mask(Some(content_mask), |cx| {
for (item, ix) in items.into_iter().zip(visible_range) {
let item_origin = padded_bounds.origin
+ point(px(0.), item_height * ix + scroll_offset.y);
let available_space = size(
AvailableSpace::Definite(padded_bounds.size.width),
AvailableSpace::Definite(item_height),
);
item.draw(item_origin, available_space, cx);
}
});
});
}
});
})
},
);

View file

@ -1592,6 +1592,17 @@ impl Edges<Pixels> {
}
}
impl Into<Edges<Pixels>> for f32 {
fn into(self) -> Edges<Pixels> {
Edges {
top: self.into(),
right: self.into(),
bottom: self.into(),
left: self.into(),
}
}
}
/// Represents the corners of a box in a 2D space, such as border radius.
///
/// Each field represents the size of the corner on one side of the box: `top_left`, `top_right`, `bottom_right`, and `bottom_left`.
@ -1808,6 +1819,28 @@ where
impl<T> Copy for Corners<T> where T: Copy + Clone + Default + Debug {}
impl Into<Corners<Pixels>> for f32 {
fn into(self) -> Corners<Pixels> {
Corners {
top_left: self.into(),
top_right: self.into(),
bottom_right: self.into(),
bottom_left: self.into(),
}
}
}
impl Into<Corners<Pixels>> for Pixels {
fn into(self) -> Corners<Pixels> {
Corners {
top_left: self,
top_right: self,
bottom_right: self,
bottom_left: self,
}
}
}
/// Represents a length in pixels, the base unit of measurement in the UI framework.
///
/// `Pixels` is a value type that represents an absolute length in pixels, which is used

View file

@ -147,6 +147,7 @@ pub trait PlatformWindow {
fn appearance(&self) -> WindowAppearance;
fn display(&self) -> Rc<dyn PlatformDisplay>;
fn mouse_position(&self) -> Point<Pixels>;
fn modifiers(&self) -> Modifiers;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
fn clear_input_handler(&mut self);

View file

@ -187,6 +187,8 @@ impl MetalRenderer {
}
pub fn draw(&mut self, scene: &Scene) {
let start = std::time::Instant::now();
let layer = self.layer.clone();
let viewport_size = layer.drawable_size();
let viewport_size: Size<DevicePixels> = size(
@ -303,6 +305,10 @@ impl MetalRenderer {
command_buffer.commit();
self.sprite_atlas.clear_textures(AtlasTextureKind::Path);
let duration_since_start = start.elapsed();
println!("renderer draw: {:?}", duration_since_start);
command_buffer.wait_until_completed();
drawable.present();
}

View file

@ -9,9 +9,10 @@ use crate::{
use block::ConcreteBlock;
use cocoa::{
appkit::{
CGPoint, NSApplication, NSBackingStoreBuffered, NSFilenamesPboardType, NSPasteboard,
NSScreen, NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowButton,
NSWindowCollectionBehavior, NSWindowStyleMask, NSWindowTitleVisibility,
CGPoint, NSApplication, NSBackingStoreBuffered, NSEventModifierFlags,
NSFilenamesPboardType, NSPasteboard, NSScreen, NSView, NSViewHeightSizable,
NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
NSWindowStyleMask, NSWindowTitleVisibility,
},
base::{id, nil},
foundation::{
@ -744,6 +745,26 @@ impl PlatformWindow for MacWindow {
convert_mouse_position(position, self.content_size().height)
}
fn modifiers(&self) -> Modifiers {
unsafe {
let modifiers: NSEventModifierFlags = msg_send![class!(NSEvent), modifierFlags];
let control = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
let command = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask);
Modifiers {
control,
alt,
shift,
command,
function,
}
}
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}

View file

@ -79,6 +79,10 @@ impl PlatformWindow for TestWindow {
Point::default()
}
fn modifiers(&self) -> crate::Modifiers {
crate::Modifiers::default()
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}

View file

@ -17,6 +17,7 @@ pub type LayerId = u32;
pub type DrawOrder = u32;
pub(crate) struct SceneBuilder {
last_order: Option<(StackingOrder, LayerId)>,
layers_by_order: BTreeMap<StackingOrder, LayerId>,
splitter: BspSplitter<(PrimitiveKind, usize)>,
shadows: Vec<Shadow>,
@ -31,6 +32,7 @@ pub(crate) struct SceneBuilder {
impl Default for SceneBuilder {
fn default() -> Self {
SceneBuilder {
last_order: None,
layers_by_order: BTreeMap::new(),
splitter: BspSplitter::new(),
shadows: Vec::new(),
@ -52,6 +54,7 @@ impl SceneBuilder {
layer_z_values[*layer_id as usize] = ix as f32 / self.layers_by_order.len() as f32;
}
self.layers_by_order.clear();
self.last_order = None;
// Add all primitives to the BSP splitter to determine draw order
self.splitter.reset();
@ -156,14 +159,7 @@ impl SceneBuilder {
return;
}
let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
*layer_id
} else {
let next_id = self.layers_by_order.len() as LayerId;
self.layers_by_order.insert(order.clone(), next_id);
next_id
};
let layer_id = self.layer_id_for_order(order);
match primitive {
Primitive::Shadow(mut shadow) => {
shadow.order = layer_id;
@ -196,6 +192,24 @@ impl SceneBuilder {
}
}
}
fn layer_id_for_order(&mut self, order: &StackingOrder) -> u32 {
if let Some((last_order, last_layer_id)) = self.last_order.as_ref() {
if last_order == order {
return *last_layer_id;
}
};
let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
*layer_id
} else {
let next_id = self.layers_by_order.len() as LayerId;
self.layers_by_order.insert(order.clone(), next_id);
next_id
};
self.last_order = Some((order.clone(), layer_id));
layer_id
}
}
pub struct Scene {

View file

@ -1,9 +1,9 @@
use std::{iter, mem, ops::Range};
use crate::{
black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font,
FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
black, phi, point, quad, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds,
ContentMask, Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement,
Font, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
SharedString, Size, SizeRefinement, Styled, TextRun, WindowContext,
};
use collections::HashSet;
@ -14,6 +14,9 @@ pub use taffy::style::{
Overflow, Position,
};
#[cfg(debug_assertions)]
pub struct DebugBelow;
pub type StyleCascade = Cascade<Style>;
#[derive(Clone, Refineable, Debug)]
@ -107,7 +110,12 @@ pub struct Style {
/// The mouse cursor style shown when the mouse pointer is over an element.
pub mouse_cursor: Option<CursorStyle>,
pub z_index: Option<u32>,
pub z_index: Option<u8>,
#[cfg(debug_assertions)]
pub debug: bool,
#[cfg(debug_assertions)]
pub debug_below: bool,
}
impl Styled for StyleRefinement {
@ -334,7 +342,22 @@ impl Style {
}
/// Paints the background of an element styled with this style.
pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
pub fn paint(
&self,
bounds: Bounds<Pixels>,
cx: &mut WindowContext,
continuation: impl FnOnce(&mut WindowContext),
) {
#[cfg(debug_assertions)]
if self.debug_below {
cx.set_global(DebugBelow)
}
#[cfg(debug_assertions)]
if self.debug || cx.has_global::<DebugBelow>() {
cx.paint_quad(crate::outline(bounds, crate::red()));
}
let rem_size = cx.rem_size();
cx.with_z_index(0, |cx| {
@ -348,15 +371,24 @@ impl Style {
let background_color = self.background.as_ref().and_then(Fill::color);
if background_color.is_some() || self.is_border_visible() {
cx.with_z_index(1, |cx| {
cx.paint_quad(
cx.paint_quad(quad(
bounds,
self.corner_radii.to_pixels(bounds.size, rem_size),
background_color.unwrap_or_default(),
self.border_widths.to_pixels(rem_size),
self.border_color.unwrap_or_default(),
);
));
});
}
cx.with_z_index(2, |cx| {
continuation(cx);
});
#[cfg(debug_assertions)]
if self.debug_below {
cx.remove_global::<DebugBelow>();
}
}
fn is_border_visible(&self) -> bool {
@ -404,6 +436,11 @@ impl Default for Style {
text: TextStyleRefinement::default(),
mouse_cursor: None,
z_index: None,
#[cfg(debug_assertions)]
debug: false,
#[cfg(debug_assertions)]
debug_below: false,
}
}
}

View file

@ -12,7 +12,7 @@ pub trait Styled: Sized {
gpui2_macros::style_helpers!();
fn z_index(mut self, z_index: u32) -> Self {
fn z_index(mut self, z_index: u8) -> Self {
self.style().z_index = Some(z_index);
self
}
@ -633,4 +633,16 @@ pub trait Styled: Sized {
.line_height = Some(line_height.into());
self
}
#[cfg(debug_assertions)]
fn debug(mut self) -> Self {
self.style().debug = Some(true);
self
}
#[cfg(debug_assertions)]
fn debug_below(mut self) -> Self {
self.style().debug_below = Some(true);
self
}
}

View file

@ -1,7 +1,6 @@
use crate::{
black, point, px, size, transparent_black, BorrowWindow, Bounds, Corners, Edges, Hsla,
LineLayout, Pixels, Point, Result, SharedString, UnderlineStyle, WindowContext, WrapBoundary,
WrappedLineLayout,
black, fill, point, px, size, BorrowWindow, Bounds, Hsla, LineLayout, Pixels, Point, Result,
SharedString, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
};
use derive_more::{Deref, DerefMut};
use smallvec::SmallVec;
@ -109,16 +108,13 @@ fn paint_line(
if wraps.peek() == Some(&&WrapBoundary { run_ix, glyph_ix }) {
wraps.next();
if let Some((background_origin, background_color)) = current_background.as_mut() {
cx.paint_quad(
cx.paint_quad(fill(
Bounds {
origin: *background_origin,
size: size(glyph_origin.x - background_origin.x, line_height),
},
Corners::default(),
*background_color,
Edges::default(),
transparent_black(),
);
));
background_origin.x = origin.x;
background_origin.y += line_height;
}
@ -180,16 +176,13 @@ fn paint_line(
}
if let Some((background_origin, background_color)) = finished_background {
cx.paint_quad(
cx.paint_quad(fill(
Bounds {
origin: background_origin,
size: size(glyph_origin.x - background_origin.x, line_height),
},
Corners::default(),
background_color,
Edges::default(),
transparent_black(),
);
));
}
if let Some((underline_origin, underline_style)) = finished_underline {
@ -235,16 +228,13 @@ fn paint_line(
}
if let Some((background_origin, background_color)) = current_background.take() {
cx.paint_quad(
cx.paint_quad(fill(
Bounds {
origin: background_origin,
size: size(last_line_end_x - background_origin.x, line_height),
},
Corners::default(),
background_color,
Edges::default(),
transparent_black(),
);
));
}
if let Some((underline_start, underline_style)) = current_underline.take() {

View file

@ -7,6 +7,7 @@ use crate::{
use anyhow::{Context, Result};
use std::{
any::TypeId,
fmt,
hash::{Hash, Hasher},
};
@ -90,7 +91,7 @@ impl<V: Render> Element for View<V> {
(layout_id, Some(element))
}
fn paint(self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
element.take().unwrap().paint(cx);
}
}
@ -170,7 +171,7 @@ impl<V> Eq for WeakView<V> {}
pub struct AnyView {
model: AnyModel,
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
paint: fn(&AnyView, AnyElement, &mut WindowContext),
paint: fn(&AnyView, &mut AnyElement, &mut WindowContext),
}
impl AnyView {
@ -208,9 +209,20 @@ impl AnyView {
cx: &mut WindowContext,
) {
cx.with_absolute_element_offset(origin, |cx| {
let (layout_id, rendered_element) = (self.layout)(self, cx);
let start_time = std::time::Instant::now();
let (layout_id, mut rendered_element) = (self.layout)(self, cx);
let duration = start_time.elapsed();
println!("request layout: {:?}", duration);
let start_time = std::time::Instant::now();
cx.compute_layout(layout_id, available_space);
(self.paint)(self, rendered_element, cx);
let duration = start_time.elapsed();
println!("compute layout: {:?}", duration);
let start_time = std::time::Instant::now();
(self.paint)(self, &mut rendered_element, cx);
let duration = start_time.elapsed();
println!("paint: {:?}", duration);
})
}
}
@ -237,12 +249,12 @@ impl Element for AnyView {
(layout_id, Some(state))
}
fn paint(self, _: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
fn paint(&mut self, _: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
debug_assert!(
state.is_some(),
"state is None. Did you include an AnyView twice in the tree?"
);
(self.paint)(&self, state.take().unwrap(), cx)
(self.paint)(&self, state.as_mut().unwrap(), cx)
}
}
@ -273,7 +285,7 @@ impl IntoElement for AnyView {
pub struct AnyWeakView {
model: AnyWeakModel,
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
paint: fn(&AnyView, AnyElement, &mut WindowContext),
paint: fn(&AnyView, &mut AnyElement, &mut WindowContext),
}
impl AnyWeakView {
@ -297,6 +309,20 @@ impl<V: 'static + Render> From<WeakView<V>> for AnyWeakView {
}
}
impl PartialEq for AnyWeakView {
fn eq(&self, other: &Self) -> bool {
self.model == other.model
}
}
impl std::fmt::Debug for AnyWeakView {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AnyWeakView")
.field("entity_id", &self.model.entity_id)
.finish_non_exhaustive()
}
}
impl<T, E> Render for T
where
T: 'static + FnMut(&mut WindowContext) -> E,
@ -324,7 +350,7 @@ mod any_view {
pub(crate) fn paint<V: 'static + Render>(
_view: &AnyView,
element: AnyElement,
element: &mut AnyElement,
cx: &mut WindowContext,
) {
element.paint(cx);

View file

@ -1,18 +1,18 @@
use crate::{
key_dispatch::DispatchActionListener, px, size, Action, AnyDrag, AnyView, AppContext,
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId,
EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla, ImageData,
InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId, Model,
ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent, Path,
Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
key_dispatch::DispatchActionListener, px, size, transparent_black, Action, AnyDrag, AnyView,
AppContext, AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners,
CursorStyle, DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity,
EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId,
Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent,
Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
RenderSvgParams, ScaledPixels, Scene, SceneBuilder, Shadow, SharedString, Size, Style,
SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View,
VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
};
use anyhow::{anyhow, Context as _, Result};
use collections::HashMap;
use collections::FxHashMap;
use derive_more::{Deref, DerefMut};
use futures::{
channel::{mpsc, oneshot},
@ -38,12 +38,24 @@ use std::{
};
use util::ResultExt;
const ACTIVE_DRAG_Z_INDEX: u32 = 1;
const ACTIVE_DRAG_Z_INDEX: u8 = 1;
/// A global stacking order, which is created by stacking successive z-index values.
/// Each z-index will always be interpreted in the context of its parent z-index.
#[derive(Deref, DerefMut, Ord, PartialOrd, Eq, PartialEq, Clone, Default, Debug)]
pub struct StackingOrder(pub(crate) SmallVec<[u32; 16]>);
#[derive(Deref, DerefMut, Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
pub struct StackingOrder {
#[deref]
#[deref_mut]
z_indices: SmallVec<[u8; 64]>,
}
impl Default for StackingOrder {
fn default() -> Self {
StackingOrder {
z_indices: SmallVec::new(),
}
}
}
/// Represents the two different phases when dispatching events.
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
@ -235,12 +247,14 @@ pub struct Window {
blur_listeners: SubscriberSet<(), AnyObserver>,
default_prevented: bool,
mouse_position: Point<Pixels>,
modifiers: Modifiers,
requested_cursor_style: Option<CursorStyle>,
scale_factor: f32,
bounds: WindowBounds,
bounds_observers: SubscriberSet<(), AnyObserver>,
active: bool,
pub(crate) dirty: bool,
pub(crate) drawing: bool,
activation_observers: SubscriberSet<(), AnyObserver>,
pub(crate) focus: Option<FocusId>,
@ -257,8 +271,8 @@ pub(crate) struct ElementStateBox {
// #[derive(Default)]
pub(crate) struct Frame {
focus: Option<FocusId>,
pub(crate) element_states: HashMap<GlobalElementId, ElementStateBox>,
mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyMouseListener)>>,
pub(crate) element_states: FxHashMap<GlobalElementId, ElementStateBox>,
mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, AnyMouseListener)>>,
pub(crate) dispatch_tree: DispatchTree,
pub(crate) scene_builder: SceneBuilder,
pub(crate) depth_map: Vec<(StackingOrder, Bounds<Pixels>)>,
@ -271,8 +285,8 @@ impl Frame {
fn new(dispatch_tree: DispatchTree) -> Self {
Frame {
focus: None,
element_states: HashMap::default(),
mouse_listeners: HashMap::default(),
element_states: FxHashMap::default(),
mouse_listeners: FxHashMap::default(),
dispatch_tree,
scene_builder: SceneBuilder::default(),
z_index_stack: StackingOrder::default(),
@ -313,6 +327,7 @@ impl Window {
let display_id = platform_window.display().id();
let sprite_atlas = platform_window.sprite_atlas();
let mouse_position = platform_window.mouse_position();
let modifiers = platform_window.modifiers();
let content_size = platform_window.content_size();
let scale_factor = platform_window.scale_factor();
let bounds = platform_window.bounds();
@ -376,12 +391,14 @@ impl Window {
blur_listeners: SubscriberSet::new(),
default_prevented: true,
mouse_position,
modifiers,
requested_cursor_style: None,
scale_factor,
bounds,
bounds_observers: SubscriberSet::new(),
active: false,
dirty: false,
drawing: false,
activation_observers: SubscriberSet::new(),
focus: None,
@ -435,7 +452,9 @@ impl<'a> WindowContext<'a> {
/// Mark the window as dirty, scheduling it to be redrawn on the next frame.
pub fn notify(&mut self) {
self.window.dirty = true;
if !self.window.drawing {
self.window.dirty = true;
}
}
/// Close this window.
@ -805,7 +824,7 @@ impl<'a> WindowContext<'a> {
/// a specific need to register a global listener.
pub fn on_mouse_event<Event: 'static>(
&mut self,
handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static,
mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static,
) {
let order = self.window.next_frame.z_index_stack.clone();
self.window
@ -879,13 +898,18 @@ impl<'a> WindowContext<'a> {
self.window.mouse_position
}
/// The current state of the keyboard's modifiers
pub fn modifiers(&self) -> Modifiers {
self.window.modifiers
}
pub fn set_cursor_style(&mut self, style: CursorStyle) {
self.window.requested_cursor_style = Some(style)
}
/// Called during painting to invoke the given closure in a new stacking context. The given
/// z-index is interpreted relative to the previous call to `stack`.
pub fn with_z_index<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
pub fn with_z_index<R>(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R {
self.window.next_frame.z_index_stack.push(z_index);
let result = f(self);
self.window.next_frame.z_index_stack.pop();
@ -965,14 +989,8 @@ impl<'a> WindowContext<'a> {
/// 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.
pub fn paint_quad(
&mut self,
bounds: Bounds<Pixels>,
corner_radii: Corners<Pixels>,
background: impl Into<Hsla>,
border_widths: Edges<Pixels>,
border_color: impl Into<Hsla>,
) {
/// see [`fill`], [`outline`], and [`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();
@ -981,12 +999,12 @@ impl<'a> WindowContext<'a> {
&window.next_frame.z_index_stack,
Quad {
order: 0,
bounds: bounds.scale(scale_factor),
bounds: quad.bounds.scale(scale_factor),
content_mask: content_mask.scale(scale_factor),
background: background.into(),
border_color: border_color.into(),
corner_radii: corner_radii.scale(scale_factor),
border_widths: border_widths.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),
},
);
}
@ -1238,6 +1256,10 @@ impl<'a> WindowContext<'a> {
/// Draw pixels to the display for this window based on the contents of its scene.
pub(crate) fn draw(&mut self) -> Scene {
let t0 = std::time::Instant::now();
self.window.dirty = false;
self.window.drawing = true;
#[cfg(any(test, feature = "test-support"))]
{
self.window.focus_invalidated = false;
@ -1325,7 +1347,8 @@ impl<'a> WindowContext<'a> {
self.platform.set_cursor_style(cursor_style);
}
self.window.dirty = false;
self.window.drawing = false;
eprintln!("window draw: {:?}", t0.elapsed());
scene
}
@ -1342,16 +1365,34 @@ impl<'a> WindowContext<'a> {
// API for the mouse position can only occur on the main thread.
InputEvent::MouseMove(mouse_move) => {
self.window.mouse_position = mouse_move.position;
self.window.modifiers = mouse_move.modifiers;
InputEvent::MouseMove(mouse_move)
}
InputEvent::MouseDown(mouse_down) => {
self.window.mouse_position = mouse_down.position;
self.window.modifiers = mouse_down.modifiers;
InputEvent::MouseDown(mouse_down)
}
InputEvent::MouseUp(mouse_up) => {
self.window.mouse_position = mouse_up.position;
self.window.modifiers = mouse_up.modifiers;
InputEvent::MouseUp(mouse_up)
}
InputEvent::MouseExited(mouse_exited) => {
// todo!("Should we record that the mouse is outside of the window somehow? Or are these global pixels?")
self.window.modifiers = mouse_exited.modifiers;
InputEvent::MouseExited(mouse_exited)
}
InputEvent::ModifiersChanged(modifiers_changed) => {
self.window.modifiers = modifiers_changed.modifiers;
InputEvent::ModifiersChanged(modifiers_changed)
}
InputEvent::ScrollWheel(scroll_wheel) => {
self.window.mouse_position = scroll_wheel.position;
self.window.modifiers = scroll_wheel.modifiers;
InputEvent::ScrollWheel(scroll_wheel)
}
// Translate dragging and dropping of external files from the operating system
// to internal drag and drop events.
InputEvent::FileDrop(file_drop) => match file_drop {
@ -1359,6 +1400,7 @@ impl<'a> WindowContext<'a> {
self.window.mouse_position = position;
if self.active_drag.is_none() {
self.active_drag = Some(AnyDrag {
value: Box::new(files.clone()),
view: self.build_view(|_| files).into(),
cursor_offset: position,
});
@ -1394,7 +1436,7 @@ impl<'a> WindowContext<'a> {
click_count: 1,
}),
},
_ => event,
InputEvent::KeyDown(_) | InputEvent::KeyUp(_) => event,
};
if let Some(any_mouse_event) = event.mouse_event() {
@ -2190,7 +2232,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
&mut self.window_cx
}
pub fn with_z_index<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
pub fn with_z_index<R>(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R {
self.window.next_frame.z_index_stack.push(z_index);
let result = f(self);
self.window.next_frame.z_index_stack.pop();
@ -2327,10 +2369,12 @@ impl<'a, V: 'static> ViewContext<'a, V> {
}
pub fn notify(&mut self) {
self.window_cx.notify();
self.window_cx.app.push_effect(Effect::Notify {
emitter: self.view.model.entity_id,
});
if !self.window.drawing {
self.window_cx.notify();
self.window_cx.app.push_effect(Effect::Notify {
emitter: self.view.model.entity_id,
});
}
}
pub fn observe_window_bounds(
@ -2865,12 +2909,12 @@ impl AnyWindowHandle {
}
}
#[cfg(any(test, feature = "test-support"))]
impl From<SmallVec<[u32; 16]>> for StackingOrder {
fn from(small_vec: SmallVec<[u32; 16]>) -> Self {
StackingOrder(small_vec)
}
}
// #[cfg(any(test, feature = "test-support"))]
// impl From<SmallVec<[u32; 16]>> for StackingOrder {
// fn from(small_vec: SmallVec<[u32; 16]>) -> Self {
// StackingOrder(small_vec)
// }
// }
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ElementId {
@ -2946,3 +2990,85 @@ impl From<(&'static str, u64)> for ElementId {
ElementId::NamedInteger(name.into(), id as usize)
}
}
/// A rectangle, to be rendered on the screen by GPUI at the given position and size.
pub struct PaintQuad {
bounds: Bounds<Pixels>,
corner_radii: Corners<Pixels>,
background: Hsla,
border_widths: Edges<Pixels>,
border_color: Hsla,
}
impl PaintQuad {
/// Set the corner radii of the quad.
pub fn corner_radii(self, corner_radii: impl Into<Corners<Pixels>>) -> Self {
PaintQuad {
corner_radii: corner_radii.into(),
..self
}
}
/// Set the border widths of the quad.
pub fn border_widths(self, border_widths: impl Into<Edges<Pixels>>) -> Self {
PaintQuad {
border_widths: border_widths.into(),
..self
}
}
/// Set the border color of the quad.
pub fn border_color(self, border_color: impl Into<Hsla>) -> Self {
PaintQuad {
border_color: border_color.into(),
..self
}
}
/// Set the background color of the quad.
pub fn background(self, background: impl Into<Hsla>) -> Self {
PaintQuad {
background: background.into(),
..self
}
}
}
/// Create a quad with the given parameters.
pub fn quad(
bounds: Bounds<Pixels>,
corner_radii: impl Into<Corners<Pixels>>,
background: impl Into<Hsla>,
border_widths: impl Into<Edges<Pixels>>,
border_color: impl Into<Hsla>,
) -> PaintQuad {
PaintQuad {
bounds,
corner_radii: corner_radii.into(),
background: background.into(),
border_widths: border_widths.into(),
border_color: border_color.into(),
}
}
/// Create a filled quad with the given bounds and background color.
pub fn fill(bounds: impl Into<Bounds<Pixels>>, background: impl Into<Hsla>) -> PaintQuad {
PaintQuad {
bounds: bounds.into(),
corner_radii: (0.).into(),
background: background.into(),
border_widths: (0.).into(),
border_color: transparent_black(),
}
}
/// Create a rectangle outline with the given bounds, border color, and a 1px border width
pub fn outline(bounds: impl Into<Bounds<Pixels>>, border_color: impl Into<Hsla>) -> PaintQuad {
PaintQuad {
bounds: bounds.into(),
corner_radii: (0.).into(),
background: transparent_black(),
border_widths: (1.).into(),
border_color: border_color.into(),
}
}