diff --git a/crates/gpui2/src/platform.rs b/crates/gpui2/src/platform.rs index 7375f47939899d8d9902e400b337da723f96dc34..7090a2a242ae4ff9401c339665e4027c9c93c52c 100644 --- a/crates/gpui2/src/platform.rs +++ b/crates/gpui2/src/platform.rs @@ -472,13 +472,27 @@ pub enum PromptLevel { Critical, } +/// The style of the cursor (pointer) #[derive(Copy, Clone, Debug)] pub enum CursorStyle { Arrow, + IBeam, + Crosshair, + ClosedHand, + OpenHand, + PointingHand, + ResizeLeft, + ResizeRight, ResizeLeftRight, + ResizeUp, + ResizeDown, ResizeUpDown, - PointingHand, - IBeam, + DisappearingItem, + IBeamCursorForVerticalLayout, + OperationNotAllowed, + DragLink, + DragCopy, + ContextualMenu, } impl Default for CursorStyle { diff --git a/crates/gpui2/src/platform/mac/platform.rs b/crates/gpui2/src/platform/mac/platform.rs index 7065c02e8755cf226b532a694d34a0b0a436c95b..314f055811c57cde9c654294e2d39ed7f1cc3806 100644 --- a/crates/gpui2/src/platform/mac/platform.rs +++ b/crates/gpui2/src/platform/mac/platform.rs @@ -724,16 +724,35 @@ impl Platform for MacPlatform { } } + /// Match cursor style to one of the styles available + /// in macOS's [NSCursor](https://developer.apple.com/documentation/appkit/nscursor). fn set_cursor_style(&self, style: CursorStyle) { unsafe { let new_cursor: id = match style { CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor], - CursorStyle::ResizeLeftRight => { - msg_send![class!(NSCursor), resizeLeftRightCursor] - } - CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor], - CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor], CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor], + CursorStyle::Crosshair => msg_send![class!(NSCursor), crosshairCursor], + CursorStyle::ClosedHand => msg_send![class!(NSCursor), closedHandCursor], + CursorStyle::OpenHand => msg_send![class!(NSCursor), openHandCursor], + CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor], + CursorStyle::ResizeLeft => msg_send![class!(NSCursor), resizeLeftCursor], + CursorStyle::ResizeRight => msg_send![class!(NSCursor), resizeRightCursor], + CursorStyle::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor], + CursorStyle::ResizeUp => msg_send![class!(NSCursor), resizeUpCursor], + CursorStyle::ResizeDown => msg_send![class!(NSCursor), resizeDownCursor], + CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor], + CursorStyle::DisappearingItem => { + msg_send![class!(NSCursor), disappearingItemCursor] + } + CursorStyle::IBeamCursorForVerticalLayout => { + msg_send![class!(NSCursor), IBeamCursorForVerticalLayout] + } + CursorStyle::OperationNotAllowed => { + msg_send![class!(NSCursor), operationNotAllowedCursor] + } + CursorStyle::DragLink => msg_send![class!(NSCursor), dragLinkCursor], + CursorStyle::DragCopy => msg_send![class!(NSCursor), dragCopyCursor], + CursorStyle::ContextualMenu => msg_send![class!(NSCursor), contextualMenuCursor], }; let old_cursor: id = msg_send![class!(NSCursor), currentCursor]; diff --git a/crates/gpui2/src/styled.rs b/crates/gpui2/src/styled.rs index 77756154b58e1e0bd1c3bcbe709e0675c3bd6049..346c1a760d6a56105440bfd2a3a788497d103a66 100644 --- a/crates/gpui2/src/styled.rs +++ b/crates/gpui2/src/styled.rs @@ -101,6 +101,125 @@ pub trait Styled: Sized { self } + /// Sets cursor style when hovering over an element to `text`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_text(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::IBeam); + self + } + + /// Sets cursor style when hovering over an element to `move`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_move(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::ClosedHand); + self + } + + /// Sets cursor style when hovering over an element to `not-allowed`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_not_allowed(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::OperationNotAllowed); + self + } + + /// Sets cursor style when hovering over an element to `context-menu`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_context_menu(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::ContextualMenu); + self + } + + /// Sets cursor style when hovering over an element to `crosshair`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_crosshair(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::Crosshair); + self + } + + /// Sets cursor style when hovering over an element to `vertical-text`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_vertical_text(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::IBeamCursorForVerticalLayout); + self + } + + /// Sets cursor style when hovering over an element to `alias`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_alias(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::DragLink); + self + } + + /// Sets cursor style when hovering over an element to `copy`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_copy(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::DragCopy); + self + } + + /// Sets cursor style when hovering over an element to `no-drop`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_no_drop(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::OperationNotAllowed); + self + } + + /// Sets cursor style when hovering over an element to `grab`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_grab(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::OpenHand); + self + } + + /// Sets cursor style when hovering over an element to `grabbing`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_grabbing(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::ClosedHand); + self + } + + /// Sets cursor style when hovering over an element to `col-resize`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_col_resize(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::ResizeLeftRight); + self + } + + /// Sets cursor style when hovering over an element to `row-resize`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_row_resize(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::ResizeUpDown); + self + } + + /// Sets cursor style when hovering over an element to `n-resize`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_n_resize(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::ResizeUp); + self + } + + /// Sets cursor style when hovering over an element to `e-resize`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_e_resize(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::ResizeRight); + self + } + + /// Sets cursor style when hovering over an element to `s-resize`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_s_resize(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::ResizeDown); + self + } + + /// Sets cursor style when hovering over an element to `w-resize`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_w_resize(mut self) -> Self { + self.style().mouse_cursor = Some(CursorStyle::ResizeLeft); + self + } + /// Sets the whitespace of the element to `normal`. /// [Docs](https://tailwindcss.com/docs/whitespace#normal) fn whitespace_normal(mut self) -> Self { diff --git a/crates/storybook2/src/stories.rs b/crates/storybook2/src/stories.rs index 2d63d1d491a497f8d45c3ce6736a17bae0022fcf..f7ab3ef548984b32e70bc83a58ad38e64c188186 100644 --- a/crates/storybook2/src/stories.rs +++ b/crates/storybook2/src/stories.rs @@ -1,4 +1,5 @@ mod auto_height_editor; +mod cursor; mod focus; mod kitchen_sink; mod picker; @@ -7,6 +8,7 @@ mod text; mod z_index; pub use auto_height_editor::*; +pub use cursor::*; pub use focus::*; pub use kitchen_sink::*; pub use picker::*; diff --git a/crates/storybook2/src/stories/cursor.rs b/crates/storybook2/src/stories/cursor.rs new file mode 100644 index 0000000000000000000000000000000000000000..d160fa4f4a0c15e86e4a5b91c7f011d4b5005d6e --- /dev/null +++ b/crates/storybook2/src/stories/cursor.rs @@ -0,0 +1,112 @@ +use gpui::{Div, Render, Stateful}; +use story::Story; +use ui::prelude::*; + +pub struct CursorStory; + +impl Render for CursorStory { + type Element = Div; + + fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { + let all_cursors: [(&str, Box) -> Stateful
>); 19] = [ + ( + "cursor_default", + Box::new(|el: Stateful
| el.cursor_default()), + ), + ( + "cursor_pointer", + Box::new(|el: Stateful
| el.cursor_pointer()), + ), + ( + "cursor_text", + Box::new(|el: Stateful
| el.cursor_text()), + ), + ( + "cursor_move", + Box::new(|el: Stateful
| el.cursor_move()), + ), + ( + "cursor_not_allowed", + Box::new(|el: Stateful
| el.cursor_not_allowed()), + ), + ( + "cursor_context_menu", + Box::new(|el: Stateful
| el.cursor_context_menu()), + ), + ( + "cursor_crosshair", + Box::new(|el: Stateful
| el.cursor_crosshair()), + ), + ( + "cursor_vertical_text", + Box::new(|el: Stateful
| el.cursor_vertical_text()), + ), + ( + "cursor_alias", + Box::new(|el: Stateful
| el.cursor_alias()), + ), + ( + "cursor_copy", + Box::new(|el: Stateful
| el.cursor_copy()), + ), + ( + "cursor_no_drop", + Box::new(|el: Stateful
| el.cursor_no_drop()), + ), + ( + "cursor_grab", + Box::new(|el: Stateful
| el.cursor_grab()), + ), + ( + "cursor_grabbing", + Box::new(|el: Stateful
| el.cursor_grabbing()), + ), + ( + "cursor_col_resize", + Box::new(|el: Stateful
| el.cursor_col_resize()), + ), + ( + "cursor_row_resize", + Box::new(|el: Stateful
| el.cursor_row_resize()), + ), + ( + "cursor_n_resize", + Box::new(|el: Stateful
| el.cursor_n_resize()), + ), + ( + "cursor_e_resize", + Box::new(|el: Stateful
| el.cursor_e_resize()), + ), + ( + "cursor_s_resize", + Box::new(|el: Stateful
| el.cursor_s_resize()), + ), + ( + "cursor_w_resize", + Box::new(|el: Stateful
| el.cursor_w_resize()), + ), + ]; + + Story::container() + .flex() + .gap_1() + .child(Story::title("cursor")) + .children(all_cursors.map(|(name, apply_cursor)| { + div().gap_1().flex().text_color(gpui::white()).child( + div() + .flex() + .items_center() + .justify_center() + .id(name) + .map(apply_cursor) + .w_64() + .h_8() + .bg(gpui::red()) + .hover(|style| style.bg(gpui::blue())) + .active(|style| style.bg(gpui::green())) + .text_sm() + .child(Story::label(name)), + ) + })) + } +} diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index 4fe76ce878d7e9488013f7484581f0134f0e1c40..216762060d83870e14177449befc5a0079332148 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -17,6 +17,7 @@ pub enum ComponentStory { Button, Checkbox, ContextMenu, + Cursor, Disclosure, Focus, Icon, @@ -40,6 +41,7 @@ impl ComponentStory { Self::Button => cx.build_view(|_| ui::ButtonStory).into(), Self::Checkbox => cx.build_view(|_| ui::CheckboxStory).into(), Self::ContextMenu => cx.build_view(|_| ui::ContextMenuStory).into(), + Self::Cursor => cx.build_view(|_| crate::stories::CursorStory).into(), Self::Disclosure => cx.build_view(|_| ui::DisclosureStory).into(), Self::Focus => FocusStory::view(cx).into(), Self::Icon => cx.build_view(|_| ui::IconStory).into(), diff --git a/crates/theme2/src/one_themes.rs b/crates/theme2/src/one_themes.rs index 2f663618a686c841ec13c4cc083ae44619bfd19c..e1fb5f1bed21422f64eb210899b3850f7bb1c6d2 100644 --- a/crates/theme2/src/one_themes.rs +++ b/crates/theme2/src/one_themes.rs @@ -52,13 +52,13 @@ pub(crate) fn one_dark() -> Theme { element_hover: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 1.0), element_active: hsla(220.0 / 360., 11.8 / 100., 20.0 / 100., 1.0), element_selected: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0), - element_disabled: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0), + element_disabled: SystemColors::default().transparent, drop_target_background: hsla(220.0 / 360., 8.3 / 100., 21.4 / 100., 1.0), ghost_element_background: SystemColors::default().transparent, ghost_element_hover: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 1.0), ghost_element_active: hsla(220.0 / 360., 11.8 / 100., 20.0 / 100., 1.0), ghost_element_selected: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0), - ghost_element_disabled: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0), + ghost_element_disabled: SystemColors::default().transparent, text: hsla(221. / 360., 11. / 100., 86. / 100., 1.0), text_muted: hsla(218.0 / 360., 7. / 100., 46. / 100., 1.0), text_placeholder: hsla(220.0 / 360., 6.6 / 100., 44.5 / 100., 1.0), diff --git a/crates/ui2/src/components/button/button_like.rs b/crates/ui2/src/components/button/button_like.rs index 4161cf6c7fd01ee8d9502b1a06de9c211946e7cc..71aa31ced21e09fb69c1cda03a112bb9a2857c87 100644 --- a/crates/ui2/src/components/button/button_like.rs +++ b/crates/ui2/src/components/button/button_like.rs @@ -323,12 +323,14 @@ impl RenderOnce for ButtonLike { .id(self.id.clone()) .h(self.size.height()) .rounded_md() - .when(!self.disabled, |el| el.cursor_pointer()) .gap_1() .px_1() .bg(self.style.enabled(cx).background) - .hover(|hover| hover.bg(self.style.hovered(cx).background)) - .active(|active| active.bg(self.style.active(cx).background)) + .when(!self.disabled, |this| { + this.cursor_pointer() + .hover(|hover| hover.bg(self.style.hovered(cx).background)) + .active(|active| active.bg(self.style.active(cx).background)) + }) .when_some( self.on_click.filter(|_| !self.disabled), |this, on_click| { diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index e4ef1b1545b67653221871bea5fdf14a18ffa7ae..438ad396936e740fe0c1cc3518e7bdca9f02e941 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -7,7 +7,7 @@ use crate::{ use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; use gpui::{ - actions, overlay, prelude::*, Action, AnchorCorner, AnyWeakView, AppContext, + actions, overlay, prelude::*, rems, Action, AnchorCorner, AnyWeakView, AppContext, AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable, FocusableView, Model, Pixels, Point, PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext, @@ -26,7 +26,9 @@ use std::{ }, }; -use ui::{prelude::*, right_click_menu, Color, Icon, IconButton, IconElement, Tooltip}; +use ui::{ + h_stack, prelude::*, right_click_menu, Color, Icon, IconButton, IconElement, Label, Tooltip, +}; use ui::{v_stack, ContextMenu}; use util::truncate_and_remove_front; @@ -1554,47 +1556,43 @@ impl Pane { fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement { div() - .group("tab_bar") .id("tab_bar") + .group("tab_bar") .track_focus(&self.tab_bar_focus_handle) .w_full() + // 30px @ 16px/rem + .h(rems(1.875)) + .overflow_hidden() .flex() + .flex_none() .bg(cx.theme().colors().tab_bar_background) // Left Side .child( - div() - .relative() - .px_1() + h_stack() + .px_2() .flex() .flex_none() - .gap_2() + .gap_1() // Nav Buttons .child( - div() - .right_0() - .flex() - .items_center() - .gap_px() - .child( - div().border().border_color(gpui::red()).child( - IconButton::new("navigate_backward", Icon::ArrowLeft) - .on_click({ - let view = cx.view().clone(); - move |_, cx| view.update(cx, Self::navigate_backward) - }) - .disabled(!self.can_navigate_backward()), - ), - ) - .child( - div().border().border_color(gpui::red()).child( - IconButton::new("navigate_forward", Icon::ArrowRight) - .on_click({ - let view = cx.view().clone(); - move |_, cx| view.update(cx, Self::navigate_backward) - }) - .disabled(!self.can_navigate_forward()), - ), - ), + div().border().border_color(gpui::red()).child( + IconButton::new("navigate_backward", Icon::ArrowLeft) + .on_click({ + let view = cx.view().clone(); + move |_, cx| view.update(cx, Self::navigate_backward) + }) + .disabled(!self.can_navigate_backward()), + ), + ) + .child( + div().border().border_color(gpui::red()).child( + IconButton::new("navigate_forward", Icon::ArrowRight) + .on_click({ + let view = cx.view().clone(); + move |_, cx| view.update(cx, Self::navigate_backward) + }) + .disabled(!self.can_navigate_forward()), + ), ), ) .child( @@ -2186,8 +2184,11 @@ impl Render for Pane { .child(if let Some(item) = self.active_item() { div().flex().flex_1().child(item.to_any()) } else { - // todo!() - div().child("Empty Pane") + h_stack() + .items_center() + .size_full() + .justify_center() + .child(Label::new("Open a file or project to get started.").color(Color::Muted)) }) // enum MouseNavigationHandler {}