mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 15:44:20 +03:00
Merge pull request #889 from zed-industries/cursor-style-revamp
Apply cursor styles during paint and make editor's cursor an I-Beam
This commit is contained in:
commit
ddc45eb24e
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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(
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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<TypeId>,
|
||||
pending_flushes: usize,
|
||||
flushing_effects: bool,
|
||||
next_cursor_style_handle_id: Arc<AtomicUsize>,
|
||||
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<T> Drop for ElementStateHandle<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CursorStyleHandle {
|
||||
id: usize,
|
||||
next_cursor_style_handle_id: Arc<AtomicUsize>,
|
||||
platform: Arc<dyn Platform>,
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -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<Shadow>,
|
||||
#[serde(default)]
|
||||
pub cursor: Option<CursorStyle>,
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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<Vector2F>,
|
||||
cursor_style_handle: Option<CursorStyleHandle>,
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -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)]
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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<usize, ElementBox>,
|
||||
parents: HashMap<usize, usize>,
|
||||
cursor_styles: Vec<(RectF, CursorStyle)>,
|
||||
font_cache: Arc<FontCache>,
|
||||
text_layout_cache: TextLayoutCache,
|
||||
asset_cache: Arc<AssetCache>,
|
||||
@ -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 {
|
||||
|
@ -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<ImageGlyph>,
|
||||
icons: Vec<Icon>,
|
||||
paths: Vec<Path>,
|
||||
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<RectF>) {
|
||||
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);
|
||||
|
@ -99,8 +99,6 @@ impl<D: PickerDelegate> View for Picker<D> {
|
||||
.constrained()
|
||||
.with_max_width(self.max_size.x())
|
||||
.with_max_height(self.max_size.y())
|
||||
.aligned()
|
||||
.top()
|
||||
.named("picker")
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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(),
|
||||
)
|
||||
|
@ -50,10 +50,6 @@ export default function selectorModal(theme: Theme): Object {
|
||||
top: 7,
|
||||
},
|
||||
},
|
||||
margin: {
|
||||
bottom: 52,
|
||||
top: 52,
|
||||
},
|
||||
shadow: shadow(theme),
|
||||
};
|
||||
}
|
||||
|
@ -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 }),
|
||||
|
Loading…
Reference in New Issue
Block a user