diff --git a/crates/collab_ui2/src/collab_panel.rs b/crates/collab_ui2/src/collab_panel.rs index ac7457abe0..cd7b2b9e9d 100644 --- a/crates/collab_ui2/src/collab_panel.rs +++ b/crates/collab_ui2/src/collab_panel.rs @@ -175,12 +175,12 @@ use editor::Editor; use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt}; use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ - actions, canvas, div, img, impl_actions, overlay, point, prelude::*, px, rems, serde_json, - size, Action, AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div, - EventEmitter, FocusHandle, Focusable, FocusableView, Hsla, InteractiveElement, IntoElement, - Length, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Quad, Render, - RenderOnce, ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task, View, - ViewContext, VisualContext, WeakView, + actions, canvas, div, fill, img, impl_actions, overlay, point, prelude::*, px, rems, + serde_json, size, Action, AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, + Div, EventEmitter, FocusHandle, Focusable, FocusableView, Hsla, InteractiveElement, + IntoElement, Length, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Quad, + Render, RenderOnce, ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task, + View, ViewContext, VisualContext, WeakView, }; use project::{Fs, Project}; use serde_derive::{Deserialize, Serialize}; @@ -2994,7 +2994,7 @@ fn render_tree_branch(is_last: bool, cx: &mut WindowContext) -> impl IntoElement let right = bounds.right(); let top = bounds.top(); - cx.paint_quad( + cx.paint_quad(fill( Bounds::from_corners( point(start_x, top), point( @@ -3002,18 +3002,12 @@ fn render_tree_branch(is_last: bool, cx: &mut WindowContext) -> impl IntoElement if is_last { start_y } else { bounds.bottom() }, ), ), - Default::default(), color, - Default::default(), - Hsla::transparent_black(), - ); - cx.paint_quad( + )); + cx.paint_quad(fill( Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)), - Default::default(), color, - Default::default(), - Hsla::transparent_black(), - ); + )); }) .w(width) .h(line_height) diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 307a95b70a..2b6db125da 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -23,13 +23,14 @@ use anyhow::Result; use collections::{BTreeMap, HashMap}; use git::diff::DiffHunkStatus; use gpui::{ - div, overlay, point, px, relative, size, transparent_black, Action, AnchorCorner, AnyElement, - AsyncWindowContext, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners, CursorStyle, - DispatchPhase, Edges, Element, ElementId, ElementInputHandler, Entity, EntityId, Hsla, - InteractiveBounds, InteractiveElement, IntoElement, LineLayout, ModifiersChangedEvent, - MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, RenderOnce, - ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement, - Style, Styled, TextRun, TextStyle, View, ViewContext, WeakView, WindowContext, WrappedLine, + div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action, + AnchorCorner, AnyElement, AsyncWindowContext, AvailableSpace, BorrowWindow, Bounds, + ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementId, + ElementInputHandler, Entity, EntityId, Hsla, InteractiveBounds, InteractiveElement, + IntoElement, LineLayout, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, + MouseUpEvent, ParentElement, Pixels, RenderOnce, ScrollWheelEvent, ShapedLine, SharedString, + Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, View, + ViewContext, WeakView, WindowContext, WrappedLine, }; use itertools::Itertools; use language::{language_settings::ShowWhitespaceSetting, Language}; @@ -620,20 +621,8 @@ impl EditorElement { let scroll_top = layout.position_map.snapshot.scroll_position().y * layout.position_map.line_height; let gutter_bg = cx.theme().colors().editor_gutter_background; - cx.paint_quad( - gutter_bounds, - Corners::default(), - gutter_bg, - Edges::default(), - transparent_black(), - ); - cx.paint_quad( - text_bounds, - Corners::default(), - self.style.background, - Edges::default(), - transparent_black(), - ); + cx.paint_quad(fill(gutter_bounds, gutter_bg)); + cx.paint_quad(fill(text_bounds, self.style.background)); if let EditorMode::Full = layout.mode { let mut active_rows = layout.active_rows.iter().peekable(); @@ -657,13 +646,7 @@ impl EditorElement { layout.position_map.line_height * (end_row - start_row + 1) as f32, ); let active_line_bg = cx.theme().colors().editor_active_line_background; - cx.paint_quad( - Bounds { origin, size }, - Corners::default(), - active_line_bg, - Edges::default(), - transparent_black(), - ); + cx.paint_quad(fill(Bounds { origin, size }, active_line_bg)); } } @@ -679,13 +662,7 @@ impl EditorElement { layout.position_map.line_height * highlighted_rows.len() as f32, ); let highlighted_line_bg = cx.theme().colors().editor_highlighted_line_background; - cx.paint_quad( - Bounds { origin, size }, - Corners::default(), - highlighted_line_bg, - Edges::default(), - transparent_black(), - ); + cx.paint_quad(fill(Bounds { origin, size }, highlighted_line_bg)); } let scroll_left = @@ -706,16 +683,13 @@ impl EditorElement { } else { cx.theme().colors().editor_wrap_guide }; - cx.paint_quad( + cx.paint_quad(fill( Bounds { origin: point(x, text_bounds.origin.y), size: size(px(1.), text_bounds.size.height), }, - Corners::default(), color, - Edges::default(), - transparent_black(), - ); + )); } } } @@ -812,13 +786,13 @@ impl EditorElement { let highlight_origin = bounds.origin + point(-width, start_y); let highlight_size = size(width * 2., end_y - start_y); let highlight_bounds = Bounds::new(highlight_origin, highlight_size); - cx.paint_quad( + cx.paint_quad(quad( highlight_bounds, Corners::all(1. * line_height), gpui::yellow(), // todo!("use the right color") Edges::default(), transparent_black(), - ); + )); continue; } @@ -845,13 +819,13 @@ impl EditorElement { let highlight_origin = bounds.origin + point(-width, start_y); let highlight_size = size(width * 2., end_y - start_y); let highlight_bounds = Bounds::new(highlight_origin, highlight_size); - cx.paint_quad( + cx.paint_quad(quad( highlight_bounds, Corners::all(1. * line_height), cx.theme().status().deleted, Edges::default(), transparent_black(), - ); + )); continue; } @@ -867,13 +841,13 @@ impl EditorElement { let highlight_origin = bounds.origin + point(-width, start_y); let highlight_size = size(width * 2., end_y - start_y); let highlight_bounds = Bounds::new(highlight_origin, highlight_size); - cx.paint_quad( + cx.paint_quad(quad( highlight_bounds, Corners::all(0.05 * line_height), color, // todo!("use the right color") Edges::default(), transparent_black(), - ); + )); } } @@ -1278,7 +1252,7 @@ impl EditorElement { let thumb_bounds = Bounds::from_corners(point(left, thumb_top), point(right, thumb_bottom)); if layout.show_scrollbars { - cx.paint_quad( + cx.paint_quad(quad( track_bounds, Corners::default(), cx.theme().colors().scrollbar_track_background, @@ -1289,7 +1263,7 @@ impl EditorElement { left: px(1.), }, cx.theme().colors().scrollbar_track_border, - ); + )); let scrollbar_settings = EditorSettings::get_global(cx).scrollbar; if layout.is_singleton && scrollbar_settings.selections { let start_anchor = Anchor::min(); @@ -1309,7 +1283,7 @@ impl EditorElement { end_y = start_y + px(1.); } let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y)); - cx.paint_quad( + cx.paint_quad(quad( bounds, Corners::default(), cx.theme().status().info, @@ -1320,7 +1294,7 @@ impl EditorElement { left: px(1.), }, cx.theme().colors().scrollbar_thumb_border, - ); + )); } } @@ -1352,7 +1326,7 @@ impl EditorElement { DiffHunkStatus::Modified => cx.theme().status().modified, DiffHunkStatus::Removed => cx.theme().status().deleted, }; - cx.paint_quad( + cx.paint_quad(quad( bounds, Corners::default(), color, @@ -1363,11 +1337,11 @@ impl EditorElement { left: px(1.), }, cx.theme().colors().scrollbar_thumb_border, - ); + )); } } - cx.paint_quad( + cx.paint_quad(quad( thumb_bounds, Corners::default(), cx.theme().colors().scrollbar_thumb_background, @@ -1378,7 +1352,7 @@ impl EditorElement { left: px(1.), }, cx.theme().colors().scrollbar_thumb_border, - ); + )); } let mouse_position = cx.mouse_position(); @@ -3085,23 +3059,13 @@ impl Cursor { }; //Draw background or border quad - if matches!(self.shape, CursorShape::Hollow) { - cx.paint_quad( - bounds, - Corners::default(), - transparent_black(), - Edges::all(px(1.)), - self.color, - ); + let cursor = if matches!(self.shape, CursorShape::Hollow) { + outline(bounds, self.color) } else { - cx.paint_quad( - bounds, - Corners::default(), - self.color, - Edges::default(), - transparent_black(), - ); - } + fill(bounds, self.color) + }; + + cx.paint_quad(cursor); if let Some(block_text) = &self.block_text { block_text.paint(self.origin + origin, self.line_height, cx); diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index ee2f42d2a2..f58435d7b9 100644 --- a/crates/gpui2/src/geometry.rs +++ b/crates/gpui2/src/geometry.rs @@ -1592,6 +1592,17 @@ impl Edges { } } +impl Into> for f32 { + fn into(self) -> Edges { + 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 Copy for Corners where T: Copy + Clone + Default + Debug {} +impl Into> for f32 { + fn into(self) -> Corners { + Corners { + top_left: self.into(), + top_right: self.into(), + bottom_right: self.into(), + bottom_left: self.into(), + } + } +} + +impl Into> for Pixels { + fn into(self) -> Corners { + 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 diff --git a/crates/gpui2/src/style.rs b/crates/gpui2/src/style.rs index 04f247d076..4031a35f75 100644 --- a/crates/gpui2/src/style.rs +++ b/crates/gpui2/src/style.rs @@ -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; @@ -348,13 +348,13 @@ 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(), - ); + )); }); } } diff --git a/crates/gpui2/src/text_system/line.rs b/crates/gpui2/src/text_system/line.rs index d62bee69c0..a58d77f585 100644 --- a/crates/gpui2/src/text_system/line.rs +++ b/crates/gpui2/src/text_system/line.rs @@ -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() { diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 77eb4e27be..554a6d6e6b 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1,15 +1,15 @@ 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, FocusEvent, 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, + 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, FocusEvent, 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; @@ -963,14 +963,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, - corner_radii: Corners, - background: impl Into, - border_widths: Edges, - border_color: impl Into, - ) { + /// 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(); @@ -979,12 +973,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), }, ); } @@ -2962,3 +2956,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, + corner_radii: Corners, + background: Hsla, + border_widths: Edges, + border_color: Hsla, +} + +impl PaintQuad { + /// Set the corner radii of the quad. + pub fn corner_radii(self, corner_radii: impl Into>) -> Self { + PaintQuad { + corner_radii: corner_radii.into(), + ..self + } + } + + /// Set the border widths of the quad. + pub fn border_widths(self, border_widths: impl Into>) -> Self { + PaintQuad { + border_widths: border_widths.into(), + ..self + } + } + + /// Set the border color of the quad. + pub fn border_color(self, border_color: impl Into) -> Self { + PaintQuad { + border_color: border_color.into(), + ..self + } + } + + /// Set the background color of the quad. + pub fn background(self, background: impl Into) -> Self { + PaintQuad { + background: background.into(), + ..self + } + } +} + +/// Create a quad with the given parameters. +pub fn quad( + bounds: Bounds, + corner_radii: impl Into>, + background: impl Into, + border_widths: impl Into>, + border_color: impl Into, +) -> 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>, background: impl Into) -> 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>, border_color: impl Into) -> PaintQuad { + PaintQuad { + bounds: bounds.into(), + corner_radii: (0.).into(), + background: transparent_black(), + border_widths: (1.).into(), + border_color: border_color.into(), + } +} diff --git a/crates/terminal_view2/src/terminal_element.rs b/crates/terminal_view2/src/terminal_element.rs index 56b5920aec..7358f2e1d7 100644 --- a/crates/terminal_view2/src/terminal_element.rs +++ b/crates/terminal_view2/src/terminal_element.rs @@ -1,9 +1,9 @@ use editor::{Cursor, HighlightedRange, HighlightedRangeLine}; use gpui::{ - black, div, point, px, red, relative, transparent_black, AnyElement, AsyncWindowContext, - AvailableSpace, Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font, - FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState, - IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels, + black, div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace, + Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font, FontStyle, + FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState, IntoElement, + LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels, PlatformInputHandler, Point, Rgba, ShapedLine, Size, StatefulInteractiveElement, Styled, TextRun, TextStyle, TextSystem, UnderlineStyle, WhiteSpace, WindowContext, }; @@ -133,13 +133,7 @@ impl LayoutRect { ) .into(); - cx.paint_quad( - Bounds::new(position, size), - Default::default(), - self.color, - Default::default(), - transparent_black(), - ); + cx.paint_quad(fill(Bounds::new(position, size), self.color)); } } @@ -775,13 +769,7 @@ impl Element for TerminalElement { let theme = cx.theme(); - cx.paint_quad( - bounds, - Default::default(), - layout.background_color, - Default::default(), - Hsla::default(), - ); + cx.paint_quad(fill(bounds, layout.background_color)); let origin = bounds.origin + Point::new(layout.gutter, px(0.)); let terminal_input_handler = TerminalInputHandler {