diff --git a/assets/themes/dark.json b/assets/themes/dark.json index 5003d23d4d..794f4151d4 100644 --- a/assets/themes/dark.json +++ b/assets/themes/dark.json @@ -89,10 +89,6 @@ "top": 7 } }, - "margin": { - "bottom": 52, - "top": 52 - }, "shadow": { "blur": 16, "color": "#00000052", @@ -158,6 +154,13 @@ "right": 8 } }, + "modal": { + "margin": { + "bottom": 52, + "top": 52 + }, + "cursor": "Arrow" + }, "left_sidebar": { "width": 30, "background": "#1c1c1c", diff --git a/assets/themes/light.json b/assets/themes/light.json index 5a370f4aa2..2874c87d0c 100644 --- a/assets/themes/light.json +++ b/assets/themes/light.json @@ -89,10 +89,6 @@ "top": 7 } }, - "margin": { - "bottom": 52, - "top": 52 - }, "shadow": { "blur": 16, "color": "#0000001f", @@ -158,6 +154,13 @@ "right": 8 } }, + "modal": { + "margin": { + "bottom": 52, + "top": 52 + }, + "cursor": "Arrow" + }, "left_sidebar": { "width": 30, "background": "#f8f8f8", diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index eb69463353..28ef9e5af0 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -16,6 +16,7 @@ use gpui::{ PathBuilder, }, json::{self, ToJson}, + platform::CursorStyle, text_layout::{self, Line, RunStyle, TextLayoutCache}, AppContext, Axis, Border, Element, ElementBox, Event, EventContext, LayoutContext, MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle, @@ -329,6 +330,7 @@ impl EditorElement { let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.); cx.scene.push_layer(Some(bounds)); + cx.scene.push_cursor_style(bounds, CursorStyle::IBeam); for (range, color) in &layout.highlighted_ranges { self.paint_highlighted_range( diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 7fa68a7675..b8d9b5e5b1 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -161,29 +161,25 @@ impl View for GoToLine { self.max_point.row + 1 ); - Align::new( - ConstrainedBox::new( - Container::new( - Flex::new(Axis::Vertical) - .with_child( - Container::new(ChildView::new(&self.line_editor).boxed()) - .with_style(theme.input_editor.container) - .boxed(), - ) - .with_child( - Container::new(Label::new(label, theme.empty.label.clone()).boxed()) - .with_style(theme.empty.container) - .boxed(), - ) - .boxed(), - ) - .with_style(theme.container) - .boxed(), + ConstrainedBox::new( + Container::new( + Flex::new(Axis::Vertical) + .with_child( + Container::new(ChildView::new(&self.line_editor).boxed()) + .with_style(theme.input_editor.container) + .boxed(), + ) + .with_child( + Container::new(Label::new(label, theme.empty.label.clone()).boxed()) + .with_style(theme.empty.container) + .boxed(), + ) + .boxed(), ) - .with_max_width(500.0) + .with_style(theme.container) .boxed(), ) - .top() + .with_max_width(500.0) .named("go to line") } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 99c92b7412..5765cdf524 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -4,7 +4,7 @@ use crate::{ elements::ElementBox, executor::{self, Task}, keymap::{self, Binding, Keystroke}, - platform::{self, CursorStyle, Platform, PromptLevel, WindowOptions}, + platform::{self, Platform, PromptLevel, WindowOptions}, presenter::Presenter, util::post_inc, AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache, @@ -31,10 +31,7 @@ use std::{ path::{Path, PathBuf}, pin::Pin, rc::{self, Rc}, - sync::{ - atomic::{AtomicUsize, Ordering::SeqCst}, - Arc, Weak, - }, + sync::{Arc, Weak}, time::Duration, }; @@ -766,7 +763,6 @@ pub struct MutableAppContext { pending_global_notifications: HashSet, pending_flushes: usize, flushing_effects: bool, - next_cursor_style_handle_id: Arc, halt_action_dispatch: bool, } @@ -818,7 +814,6 @@ impl MutableAppContext { pending_global_notifications: HashSet::new(), pending_flushes: 0, flushing_effects: false, - next_cursor_style_handle_id: Default::default(), halt_action_dispatch: false, } } @@ -1949,16 +1944,6 @@ impl MutableAppContext { self.presenters_and_platform_windows = presenters; } - pub fn set_cursor_style(&mut self, style: CursorStyle) -> CursorStyleHandle { - self.platform.set_cursor_style(style); - let id = self.next_cursor_style_handle_id.fetch_add(1, SeqCst); - CursorStyleHandle { - id, - next_cursor_style_handle_id: self.next_cursor_style_handle_id.clone(), - platform: self.platform(), - } - } - fn handle_subscription_effect( &mut self, entity_id: usize, @@ -4452,20 +4437,6 @@ impl Drop for ElementStateHandle { } } -pub struct CursorStyleHandle { - id: usize, - next_cursor_style_handle_id: Arc, - platform: Arc, -} - -impl Drop for CursorStyleHandle { - fn drop(&mut self) { - if self.id + 1 == self.next_cursor_style_handle_id.load(SeqCst) { - self.platform.set_cursor_style(CursorStyle::Arrow); - } - } -} - #[must_use] pub enum Subscription { Subscription { diff --git a/crates/gpui/src/elements/container.rs b/crates/gpui/src/elements/container.rs index 711ab1f6a6..62f19636b7 100644 --- a/crates/gpui/src/elements/container.rs +++ b/crates/gpui/src/elements/container.rs @@ -1,17 +1,17 @@ -use pathfinder_geometry::rect::RectF; -use serde::Deserialize; -use serde_json::json; - use crate::{ color::Color, geometry::{ deserialize_vec2f, + rect::RectF, vector::{vec2f, Vector2F}, }, json::ToJson, + platform::CursorStyle, scene::{self, Border, Quad}, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext, SizeConstraint, }; +use serde::Deserialize; +use serde_json::json; #[derive(Clone, Copy, Debug, Default, Deserialize)] pub struct ContainerStyle { @@ -27,6 +27,8 @@ pub struct ContainerStyle { pub corner_radius: f32, #[serde(default)] pub shadow: Option, + #[serde(default)] + pub cursor: Option, } pub struct Container { @@ -128,6 +130,11 @@ impl Container { self } + pub fn with_cursor(mut self, style: CursorStyle) -> Self { + self.style.cursor = Some(style); + self + } + fn margin_size(&self) -> Vector2F { vec2f( self.style.margin.left + self.style.margin.right, @@ -205,6 +212,10 @@ impl Element for Container { }); } + if let Some(style) = self.style.cursor { + cx.scene.push_cursor_style(quad_bounds, style); + } + let child_origin = quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top); diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index 1d1e934d03..12166d45b5 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -5,8 +5,8 @@ use crate::{ vector::{vec2f, Vector2F}, }, platform::CursorStyle, - CursorStyleHandle, DebugContext, Element, ElementBox, ElementStateContext, ElementStateHandle, - Event, EventContext, LayoutContext, PaintContext, SizeConstraint, + DebugContext, Element, ElementBox, ElementStateContext, ElementStateHandle, Event, + EventContext, LayoutContext, PaintContext, SizeConstraint, }; use serde_json::json; @@ -25,7 +25,6 @@ pub struct MouseState { pub hovered: bool, pub clicked: bool, prev_drag_position: Option, - cursor_style_handle: Option, } impl MouseEventHandler { @@ -72,6 +71,14 @@ impl MouseEventHandler { self.padding = padding; self } + + fn hit_bounds(&self, bounds: RectF) -> RectF { + RectF::from_points( + bounds.origin() - vec2f(self.padding.left, self.padding.top), + bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom), + ) + .round_out() + } } impl Element for MouseEventHandler { @@ -93,6 +100,10 @@ impl Element for MouseEventHandler { _: &mut Self::LayoutState, cx: &mut PaintContext, ) -> Self::PaintState { + if let Some(cursor_style) = self.cursor_style { + cx.scene + .push_cursor_style(self.hit_bounds(bounds), cursor_style); + } self.child.paint(bounds.origin(), visible_bounds, cx); } @@ -105,19 +116,13 @@ impl Element for MouseEventHandler { _: &mut Self::PaintState, cx: &mut EventContext, ) -> bool { - let cursor_style = self.cursor_style; + let hit_bounds = self.hit_bounds(visible_bounds); let mouse_down_handler = self.mouse_down_handler.as_mut(); let click_handler = self.click_handler.as_mut(); let drag_handler = self.drag_handler.as_mut(); let handled_in_child = self.child.dispatch_event(event, cx); - let hit_bounds = RectF::from_points( - visible_bounds.origin() - vec2f(self.padding.left, self.padding.top), - visible_bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom), - ) - .round_out(); - self.state.update(cx, |state, cx| match event { Event::MouseMoved { position, @@ -127,16 +132,6 @@ impl Element for MouseEventHandler { let mouse_in = hit_bounds.contains_point(*position); if state.hovered != mouse_in { state.hovered = mouse_in; - if let Some(cursor_style) = cursor_style { - if !state.clicked { - if state.hovered { - state.cursor_style_handle = - Some(cx.set_cursor_style(cursor_style)); - } else { - state.cursor_style_handle = None; - } - } - } cx.notify(); return true; } @@ -160,9 +155,6 @@ impl Element for MouseEventHandler { state.prev_drag_position = None; if !handled_in_child && state.clicked { state.clicked = false; - if !state.hovered { - state.cursor_style_handle = None; - } cx.notify(); if let Some(handler) = click_handler { if hit_bounds.contains_point(*position) { diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 7e67d8b752..92530e26c3 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -21,6 +21,7 @@ use anyhow::{anyhow, Result}; use async_task::Runnable; pub use event::{Event, NavigationDirection}; use postage::oneshot; +use serde::Deserialize; use std::{ any::Any, path::{Path, PathBuf}, @@ -125,11 +126,12 @@ pub enum PromptLevel { Critical, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Deserialize)] pub enum CursorStyle { Arrow, ResizeLeftRight, PointingHand, + IBeam, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 5554a96dbb..fc1b7e59d8 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -583,6 +583,7 @@ impl platform::Platform for MacPlatform { CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor], CursorStyle::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor], CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor], + CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor], }; let _: () = msg_send![cursor, set]; } diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 793f41f487..85f21ba5cb 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -4,7 +4,7 @@ use crate::{ font_cache::FontCache, geometry::rect::RectF, json::{self, ToJson}, - platform::Event, + platform::{CursorStyle, Event}, text_layout::TextLayoutCache, Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox, ElementStateContext, Entity, FontSystem, ModelHandle, ReadModel, ReadView, Scene, @@ -22,6 +22,7 @@ pub struct Presenter { window_id: usize, pub(crate) rendered_views: HashMap, parents: HashMap, + cursor_styles: Vec<(RectF, CursorStyle)>, font_cache: Arc, text_layout_cache: TextLayoutCache, asset_cache: Arc, @@ -42,6 +43,7 @@ impl Presenter { window_id, rendered_views: cx.render_views(window_id, titlebar_height), parents: HashMap::new(), + cursor_styles: Default::default(), font_cache, text_layout_cache, asset_cache, @@ -118,6 +120,7 @@ impl Presenter { RectF::new(Vector2F::zero(), window_size), ); self.text_layout_cache.finish_frame(); + self.cursor_styles = scene.cursor_styles(); if let Some(event) = self.last_mouse_moved_event.clone() { self.dispatch_event(event, cx) @@ -171,8 +174,21 @@ impl Presenter { pub fn dispatch_event(&mut self, event: Event, cx: &mut MutableAppContext) { if let Some(root_view_id) = cx.root_view_id(self.window_id) { match event { - Event::MouseMoved { .. } => { + Event::MouseMoved { + position, + left_mouse_down, + } => { self.last_mouse_moved_event = Some(event.clone()); + + if !left_mouse_down { + cx.platform().set_cursor_style(CursorStyle::Arrow); + for (bounds, style) in self.cursor_styles.iter().rev() { + if bounds.contains_point(position) { + cx.platform().set_cursor_style(*style); + break; + } + } + } } Event::LeftMouseDragged { position } => { self.last_mouse_moved_event = Some(Event::MouseMoved { diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index 6c23a731bc..7c358b85a0 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -7,6 +7,7 @@ use crate::{ fonts::{FontId, GlyphId}, geometry::{rect::RectF, vector::Vector2F}, json::ToJson, + platform::CursorStyle, ImageData, }; @@ -32,6 +33,7 @@ pub struct Layer { image_glyphs: Vec, icons: Vec, paths: Vec, + cursor_styles: Vec<(RectF, CursorStyle)>, } #[derive(Default, Debug)] @@ -173,6 +175,13 @@ impl Scene { self.stacking_contexts.iter().flat_map(|s| &s.layers) } + pub fn cursor_styles(&self) -> Vec<(RectF, CursorStyle)> { + self.layers() + .flat_map(|layer| &layer.cursor_styles) + .copied() + .collect() + } + pub fn push_stacking_context(&mut self, clip_bounds: Option) { self.active_stacking_context_stack .push(self.stacking_contexts.len()); @@ -197,6 +206,10 @@ impl Scene { self.active_layer().push_quad(quad) } + pub fn push_cursor_style(&mut self, bounds: RectF, style: CursorStyle) { + self.active_layer().push_cursor_style(bounds, style); + } + pub fn push_image(&mut self, image: Image) { self.active_layer().push_image(image) } @@ -285,6 +298,7 @@ impl Layer { glyphs: Default::default(), icons: Default::default(), paths: Default::default(), + cursor_styles: Default::default(), } } @@ -302,6 +316,14 @@ impl Layer { self.quads.as_slice() } + fn push_cursor_style(&mut self, bounds: RectF, style: CursorStyle) { + if let Some(bounds) = bounds.intersection(self.clip_bounds.unwrap_or(bounds)) { + if can_draw(bounds) { + self.cursor_styles.push((bounds, style)); + } + } + } + fn push_underline(&mut self, underline: Underline) { if underline.width > 0. { self.underlines.push(underline); diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 4e4a7e92aa..0c90268dad 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -99,8 +99,6 @@ impl View for Picker { .constrained() .with_max_width(self.max_size.x()) .with_max_height(self.max_size.y()) - .aligned() - .top() .named("picker") } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index dd6a7a79c7..1299ed7fc4 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -43,6 +43,7 @@ pub struct Workspace { pub status_bar: StatusBar, pub toolbar: Toolbar, pub disconnected_overlay: ContainedText, + pub modal: ContainerStyle, } #[derive(Clone, Deserialize, Default)] diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e8048e0953..b74941e470 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1983,7 +1983,14 @@ impl View for Workspace { content.add_child(self.right_sidebar.render(&theme, cx)); content.boxed() }) - .with_children(self.modal.as_ref().map(|m| ChildView::new(m).boxed())) + .with_children(self.modal.as_ref().map(|m| { + ChildView::new(m) + .contained() + .with_style(theme.workspace.modal) + .aligned() + .top() + .boxed() + })) .flex(1.0, true) .boxed(), ) diff --git a/styles/src/styleTree/selectorModal.ts b/styles/src/styleTree/selectorModal.ts index b4dce880a7..8966de9fae 100644 --- a/styles/src/styleTree/selectorModal.ts +++ b/styles/src/styleTree/selectorModal.ts @@ -50,10 +50,6 @@ export default function selectorModal(theme: Theme): Object { top: 7, }, }, - margin: { - bottom: 52, - top: 52, - }, shadow: shadow(theme), }; } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 57de2f01f4..3deb18a2fb 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -75,6 +75,13 @@ export default function workspace(theme: Theme) { leaderBorderWidth: 2.0, tab, activeTab, + modal: { + margin: { + bottom: 52, + top: 52, + }, + cursor: "Arrow" + }, leftSidebar: { ...sidebar, border: border(theme, "primary", { right: true }),