diff --git a/crates/collab_ui2/src/collab_panel.rs b/crates/collab_ui2/src/collab_panel.rs index 95ca7cfd2545180fad9135a37fb80526e53ec030..298c7682eb663bdd470e80826de26f30d83ed807 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, AnyElement, 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, AnyElement, 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 307a95b70ac72ca7cb8062a3d892c5ecfb647555..2b6db125daed950c21536d876d0b22add51a5ec0 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/app.rs b/crates/gpui2/src/app.rs index 62ce6305ea7303362a5b6fb8889e63e18ca3fd68..18f688f179e34c6a272351a7bcf7cae9e5dc7bd3 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -1138,6 +1138,10 @@ impl AppContext { pub fn has_active_drag(&self) -> bool { self.active_drag.is_some() } + + pub fn active_drag(&self) -> Option { + self.active_drag.as_ref().map(|drag| drag.view.clone()) + } } impl Context for AppContext { diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index 226a477012a025474df2a58c70be56c10ef37fc3..e5ecd195baa14694bc65a15e9102d5cbd56be10a 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -482,48 +482,31 @@ impl IntoElement for AnyElement { } } -// impl Element for Option -// where -// V: 'static, -// E: Element, -// F: FnOnce(&mut V, &mut WindowContext<'_, V>) -> E + 'static, -// { -// type State = Option; - -// fn element_id(&self) -> Option { -// None -// } - -// fn layout( -// &mut self, -// _: Option, -// 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 paint( -// self, -// _bounds: Bounds, -// rendered_element: &mut Self::State, -// cx: &mut WindowContext, -// ) { -// rendered_element.take().unwrap().paint(view_state, cx); -// } -// } - -// impl RenderOnce for Option -// where -// V: 'static, -// E: Element, -// F: FnOnce(&mut V, &mut WindowContext) -> E + 'static, -// { -// type Element = Self; - -// fn render(self) -> Self::Element { -// self -// } -// } +/// The empty element, which renders nothing. +pub type Empty = (); + +impl IntoElement for () { + type Element = Self; + + fn element_id(&self) -> Option { + None + } + + fn into_element(self) -> Self::Element { + self + } +} + +impl Element for () { + type State = (); + + fn layout( + &mut self, + _state: Option, + cx: &mut WindowContext, + ) -> (LayoutId, Self::State) { + (cx.request_layout(&crate::Style::default(), None), ()) + } + + fn paint(self, _bounds: Bounds, _state: &mut Self::State, _cx: &mut WindowContext) {} +} diff --git a/crates/gpui2/src/elements/canvas.rs b/crates/gpui2/src/elements/canvas.rs index 287a3b4b5a38fdc0c7c90c75763bb9a0921dfb7e..b3afd335d41d4544267a9453e8ffedea5c990b18 100644 --- a/crates/gpui2/src/elements/canvas.rs +++ b/crates/gpui2/src/elements/canvas.rs @@ -2,7 +2,7 @@ use refineable::Refineable as _; use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext}; -pub fn canvas(callback: impl 'static + FnOnce(Bounds, &mut WindowContext)) -> Canvas { +pub fn canvas(callback: impl 'static + FnOnce(&Bounds, &mut WindowContext)) -> Canvas { Canvas { paint_callback: Box::new(callback), style: StyleRefinement::default(), @@ -10,7 +10,7 @@ pub fn canvas(callback: impl 'static + FnOnce(Bounds, &mut WindowContext } pub struct Canvas { - paint_callback: Box, &mut WindowContext)>, + paint_callback: Box, &mut WindowContext)>, style: StyleRefinement, } @@ -41,7 +41,7 @@ impl Element for Canvas { } fn paint(self, bounds: Bounds, _: &mut (), cx: &mut WindowContext) { - (self.paint_callback)(bounds, cx) + (self.paint_callback)(&bounds, cx) } } diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index dfbb5aff21bc10ff419cd311480dc592b8fbc077..1954e3086c6b8f2c2b7bd3ea705a76e33c895d6f 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -6,6 +6,7 @@ use crate::{ SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext, }; + use collections::HashMap; use refineable::Refineable; use smallvec::SmallVec; @@ -29,6 +30,11 @@ pub struct GroupStyle { pub style: Box, } +pub struct DragMoveEvent { + pub event: MouseMoveEvent, + pub drag: View, +} + pub trait InteractiveElement: Sized { fn interactivity(&mut self) -> &mut Interactivity; @@ -192,6 +198,34 @@ pub trait InteractiveElement: Sized { self } + fn on_drag_move( + mut self, + listener: impl Fn(&DragMoveEvent, &mut WindowContext) + 'static, + ) -> Self + where + W: Render, + { + self.interactivity().mouse_move_listeners.push(Box::new( + move |event, bounds, phase, cx| { + if phase == DispatchPhase::Capture + && bounds.drag_target_contains(&event.position, cx) + { + if let Some(view) = cx.active_drag().and_then(|view| view.downcast::().ok()) + { + (listener)( + &DragMoveEvent { + event: event.clone(), + drag: view, + }, + cx, + ); + } + } + }, + )); + self + } + fn on_scroll_wheel( mut self, listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static, @@ -403,7 +437,7 @@ pub trait StatefulInteractiveElement: InteractiveElement { self } - fn on_drag(mut self, listener: impl Fn(&mut WindowContext) -> View + 'static) -> Self + fn on_drag(mut self, constructor: impl Fn(&mut WindowContext) -> View + 'static) -> Self where Self: Sized, W: 'static + Render, @@ -413,7 +447,7 @@ pub trait StatefulInteractiveElement: InteractiveElement { "calling on_drag more than once on the same element is not supported" ); self.interactivity().drag_listener = Some(Box::new(move |cursor_offset, cx| AnyDrag { - view: listener(cx).into(), + view: constructor(cx).into(), cursor_offset, })); self @@ -493,11 +527,19 @@ pub type DragEventListener = Box; +#[track_caller] pub fn div() -> Div { - Div { + let mut div = Div { interactivity: Interactivity::default(), children: SmallVec::default(), + }; + + #[cfg(debug_assertions)] + { + div.interactivity.location = Some(*core::panic::Location::caller()); } + + div } pub struct Div { @@ -607,10 +649,7 @@ impl Element for Div { let z_index = style.z_index.unwrap_or(0); cx.with_z_index(z_index, |cx| { - cx.with_z_index(0, |cx| { - style.paint(bounds, cx); - }); - cx.with_z_index(1, |cx| { + style.paint(bounds, cx, |cx| { cx.with_text_style(style.text_style().cloned(), |cx| { cx.with_content_mask(style.overflow_mask(bounds), |cx| { cx.with_element_offset(scroll_offset, |cx| { @@ -620,7 +659,7 @@ impl Element for Div { }) }) }) - }) + }); }) }, ); @@ -678,6 +717,9 @@ pub struct Interactivity { pub drag_listener: Option, pub hover_listener: Option>, pub tooltip_builder: Option, + + #[cfg(debug_assertions)] + pub location: Option>, } #[derive(Clone, Debug)] @@ -737,6 +779,117 @@ impl Interactivity { ) { let style = self.compute_style(Some(bounds), element_state, cx); + #[cfg(debug_assertions)] + if self.element_id.is_some() + && (style.debug || style.debug_below || cx.has_global::()) + && bounds.contains(&cx.mouse_position()) + { + const FONT_SIZE: crate::Pixels = crate::Pixels(10.); + let element_id = format!("{:?}", self.element_id.unwrap()); + let str_len = element_id.len(); + + let render_debug_text = |cx: &mut WindowContext| { + if let Some(text) = cx + .text_system() + .shape_text( + &element_id, + FONT_SIZE, + &[cx.text_style().to_run(str_len)], + None, + ) + .ok() + .map(|mut text| text.pop()) + .flatten() + { + text.paint(bounds.origin, FONT_SIZE, cx).ok(); + + let text_bounds = crate::Bounds { + origin: bounds.origin, + size: text.size(FONT_SIZE), + }; + if self.location.is_some() + && text_bounds.contains(&cx.mouse_position()) + && cx.modifiers().command + { + let command_held = cx.modifiers().command; + cx.on_key_event({ + let text_bounds = text_bounds.clone(); + move |e: &crate::ModifiersChangedEvent, _phase, cx| { + if e.modifiers.command != command_held + && text_bounds.contains(&cx.mouse_position()) + { + cx.notify(); + } + } + }); + + let hovered = bounds.contains(&cx.mouse_position()); + cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { + if phase == DispatchPhase::Capture { + if bounds.contains(&event.position) != hovered { + cx.notify(); + } + } + }); + + cx.on_mouse_event({ + let location = self.location.clone().unwrap(); + let text_bounds = text_bounds.clone(); + move |e: &crate::MouseDownEvent, phase, cx| { + if text_bounds.contains(&e.position) && phase.capture() { + cx.stop_propagation(); + let Ok(dir) = std::env::current_dir() else { + return; + }; + + eprintln!( + "This element is created at:\n{}:{}:{}", + location.file(), + location.line(), + location.column() + ); + + std::process::Command::new("zed") + .arg(format!( + "{}/{}:{}:{}", + dir.to_string_lossy(), + location.file(), + location.line(), + location.column() + )) + .spawn() + .ok(); + } + } + }); + cx.paint_quad(crate::outline( + crate::Bounds { + origin: bounds.origin + + crate::point(crate::px(0.), FONT_SIZE - px(2.)), + size: crate::Size { + width: text_bounds.size.width, + height: crate::px(1.), + }, + }, + crate::red(), + )) + } + } + }; + + cx.with_z_index(1, |cx| { + cx.with_text_style( + Some(crate::TextStyleRefinement { + color: Some(crate::red()), + line_height: Some(FONT_SIZE.into()), + background_color: Some(crate::white()), + ..Default::default() + }), + render_debug_text, + ) + }); + } + if style .background .as_ref() @@ -1234,6 +1387,9 @@ impl Default for Interactivity { drag_listener: None, hover_listener: None, tooltip_builder: None, + + #[cfg(debug_assertions)] + location: None, } } } diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index d27b3bdb772ca7c382b3eb91ac6ba56ff4e23622..debd365c87da2da4fbf402dc6bf4188267c191ee 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -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( view: View, 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, @@ -197,41 +202,41 @@ impl Element for UniformList { ); cx.with_z_index(style.z_index.unwrap_or(0), |cx| { - style.paint(bounds, 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, + 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, + }); + } + + 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); + } + }); }); } - - 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); - } - }); - }); - } + }); }) }, ); diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index ee2f42d2a284e47e1f35b1f826383e8e889f0708..f58435d7b9a2e9b3fa48062d2e28ed21502c1ca1 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/platform.rs b/crates/gpui2/src/platform.rs index b9d5a9f2226a757160aea93aacf06c1ca8f0207e..e7ee5f9e29876193451610d54e89dce227c1f5ca 100644 --- a/crates/gpui2/src/platform.rs +++ b/crates/gpui2/src/platform.rs @@ -147,6 +147,7 @@ pub trait PlatformWindow { fn appearance(&self) -> WindowAppearance; fn display(&self) -> Rc; fn mouse_position(&self) -> Point; + fn modifiers(&self) -> Modifiers; fn as_any_mut(&mut self) -> &mut dyn Any; fn set_input_handler(&mut self, input_handler: Box); fn clear_input_handler(&mut self); diff --git a/crates/gpui2/src/platform/mac/window.rs b/crates/gpui2/src/platform/mac/window.rs index dcdf616ffedcb2d052987779be6b91dd2a3e8bc8..12189e198a76343ad9478ffad2ec550b9cd0e9f0 100644 --- a/crates/gpui2/src/platform/mac/window.rs +++ b/crates/gpui2/src/platform/mac/window.rs @@ -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 } diff --git a/crates/gpui2/src/platform/test/window.rs b/crates/gpui2/src/platform/test/window.rs index 5af990514f52aa3d2c17f57acca4e1e38d83d1c3..0f981d44783ed2434361d639234ebed2c1ceb85f 100644 --- a/crates/gpui2/src/platform/test/window.rs +++ b/crates/gpui2/src/platform/test/window.rs @@ -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 } diff --git a/crates/gpui2/src/style.rs b/crates/gpui2/src/style.rs index 04f247d07627ac6572fe55724f1eb7b29c76463a..115db997843fe3b94e3b31ec292cab02c599dc96 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; @@ -14,6 +14,9 @@ pub use taffy::style::{ Overflow, Position, }; +#[cfg(debug_assertions)] +pub struct DebugBelow; + pub type StyleCascade = Cascade