Detailed changes
@@ -7071,6 +7071,17 @@ dependencies = [
"workspace",
]
+[[package]]
+name = "quick_action_bar2"
+version = "0.1.0"
+dependencies = [
+ "editor2",
+ "gpui2",
+ "search2",
+ "ui2",
+ "workspace2",
+]
+
[[package]]
name = "quote"
version = "1.0.33"
@@ -11890,6 +11901,7 @@ dependencies = [
"postage",
"project2",
"project_panel2",
+ "quick_action_bar2",
"rand 0.8.5",
"regex",
"rope2",
@@ -89,6 +89,7 @@ members = [
"crates/project_panel",
"crates/project_panel2",
"crates/project_symbols",
+ "crates/quick_action_bar2",
"crates/recent_projects",
"crates/rope",
"crates/rpc",
@@ -32,7 +32,7 @@ use std::{
};
use text::Selection;
use theme::{ActiveTheme, Theme};
-use ui::{Color, Label};
+use ui::{h_stack, Color, Label};
use util::{paths::PathExt, paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
use workspace::{
item::{BreadcrumbText, FollowEvent, FollowableEvents, FollowableItemHandle},
@@ -586,28 +586,25 @@ impl Item for Editor {
fn tab_content(&self, detail: Option<usize>, cx: &WindowContext) -> AnyElement {
let theme = cx.theme();
- AnyElement::new(
- div()
- .flex()
- .flex_row()
- .items_center()
- .gap_2()
- .child(Label::new(self.title(cx).to_string()))
- .children(detail.and_then(|detail| {
- let path = path_for_buffer(&self.buffer, detail, false, cx)?;
- let description = path.to_string_lossy();
-
- Some(
- div().child(
- Label::new(util::truncate_and_trailoff(
- &description,
- MAX_TAB_TITLE_LEN,
- ))
- .color(Color::Muted),
- ),
- )
- })),
- )
+ let description = detail.and_then(|detail| {
+ let path = path_for_buffer(&self.buffer, detail, false, cx)?;
+ let description = path.to_string_lossy();
+ let description = description.trim();
+
+ if description.is_empty() {
+ return None;
+ }
+
+ Some(util::truncate_and_trailoff(&description, MAX_TAB_TITLE_LEN))
+ });
+
+ h_stack()
+ .gap_2()
+ .child(Label::new(self.title(cx).to_string()))
+ .when_some(description, |this, description| {
+ this.child(Label::new(description).color(Color::Muted))
+ })
+ .into_any_element()
}
fn for_each_project_item(
@@ -0,0 +1,22 @@
+[package]
+name = "quick_action_bar2"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/quick_action_bar.rs"
+doctest = false
+
+[dependencies]
+#assistant = { path = "../assistant" }
+editor = { package = "editor2", path = "../editor2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+search = { package = "search2", path = "../search2" }
+workspace = { package = "workspace2", path = "../workspace2" }
+ui = { package = "ui2", path = "../ui2" }
+
+[dev-dependencies]
+editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
@@ -0,0 +1,288 @@
+// use assistant::{assistant_panel::InlineAssist, AssistantPanel};
+use editor::Editor;
+
+use gpui::{
+ Action, Div, ElementId, EventEmitter, InteractiveElement, ParentElement, Render, Stateful,
+ Styled, Subscription, View, ViewContext, WeakView,
+};
+use search::BufferSearchBar;
+use ui::{prelude::*, ButtonSize, ButtonStyle, Icon, IconButton, IconSize, Tooltip};
+use workspace::{
+ item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
+};
+
+pub struct QuickActionBar {
+ buffer_search_bar: View<BufferSearchBar>,
+ active_item: Option<Box<dyn ItemHandle>>,
+ _inlay_hints_enabled_subscription: Option<Subscription>,
+ #[allow(unused)]
+ workspace: WeakView<Workspace>,
+}
+
+impl QuickActionBar {
+ pub fn new(buffer_search_bar: View<BufferSearchBar>, workspace: &Workspace) -> Self {
+ Self {
+ buffer_search_bar,
+ active_item: None,
+ _inlay_hints_enabled_subscription: None,
+ workspace: workspace.weak_handle(),
+ }
+ }
+
+ #[allow(dead_code)]
+ fn active_editor(&self) -> Option<View<Editor>> {
+ self.active_item
+ .as_ref()
+ .and_then(|item| item.downcast::<Editor>())
+ }
+}
+
+impl Render for QuickActionBar {
+ type Element = Stateful<Div>;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ let search_button = QuickActionBarButton::new(
+ "toggle buffer search",
+ Icon::MagnifyingGlass,
+ !self.buffer_search_bar.read(cx).is_dismissed(),
+ Box::new(search::buffer_search::Deploy { focus: false }),
+ "Buffer Search",
+ );
+ let assistant_button = QuickActionBarButton::new(
+ "toggle inline assitant",
+ Icon::MagicWand,
+ false,
+ Box::new(gpui::NoAction),
+ "Inline assistant",
+ );
+ h_stack()
+ .id("quick action bar")
+ .p_1()
+ .gap_2()
+ .child(search_button)
+ .child(
+ div()
+ .border()
+ .border_color(gpui::red())
+ .child(assistant_button),
+ )
+ }
+}
+
+impl EventEmitter<ToolbarItemEvent> for QuickActionBar {}
+
+// impl View for QuickActionBar {
+// fn ui_name() -> &'static str {
+// "QuickActionsBar"
+// }
+
+// fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
+// let Some(editor) = self.active_editor() else {
+// return div();
+// };
+
+// let mut bar = Flex::row();
+// if editor.read(cx).supports_inlay_hints(cx) {
+// bar = bar.with_child(render_quick_action_bar_button(
+// 0,
+// "icons/inlay_hint.svg",
+// editor.read(cx).inlay_hints_enabled(),
+// (
+// "Toggle Inlay Hints".to_string(),
+// Some(Box::new(editor::ToggleInlayHints)),
+// ),
+// cx,
+// |this, cx| {
+// if let Some(editor) = this.active_editor() {
+// editor.update(cx, |editor, cx| {
+// editor.toggle_inlay_hints(&editor::ToggleInlayHints, cx);
+// });
+// }
+// },
+// ));
+// }
+
+// if editor.read(cx).buffer().read(cx).is_singleton() {
+// let search_bar_shown = !self.buffer_search_bar.read(cx).is_dismissed();
+// let search_action = buffer_search::Deploy { focus: true };
+
+// bar = bar.with_child(render_quick_action_bar_button(
+// 1,
+// "icons/magnifying_glass.svg",
+// search_bar_shown,
+// (
+// "Buffer Search".to_string(),
+// Some(Box::new(search_action.clone())),
+// ),
+// cx,
+// move |this, cx| {
+// this.buffer_search_bar.update(cx, |buffer_search_bar, cx| {
+// if search_bar_shown {
+// buffer_search_bar.dismiss(&buffer_search::Dismiss, cx);
+// } else {
+// buffer_search_bar.deploy(&search_action, cx);
+// }
+// });
+// },
+// ));
+// }
+
+// bar.add_child(render_quick_action_bar_button(
+// 2,
+// "icons/magic-wand.svg",
+// false,
+// ("Inline Assist".into(), Some(Box::new(InlineAssist))),
+// cx,
+// move |this, cx| {
+// if let Some(workspace) = this.workspace.upgrade(cx) {
+// workspace.update(cx, |workspace, cx| {
+// AssistantPanel::inline_assist(workspace, &Default::default(), cx);
+// });
+// }
+// },
+// ));
+
+// bar.into_any()
+// }
+// }
+
+#[derive(IntoElement)]
+struct QuickActionBarButton {
+ id: ElementId,
+ icon: Icon,
+ toggled: bool,
+ action: Box<dyn Action>,
+ tooltip: SharedString,
+ tooltip_meta: Option<SharedString>,
+}
+
+impl QuickActionBarButton {
+ fn new(
+ id: impl Into<ElementId>,
+ icon: Icon,
+ toggled: bool,
+ action: Box<dyn Action>,
+ tooltip: impl Into<SharedString>,
+ ) -> Self {
+ Self {
+ id: id.into(),
+ icon,
+ toggled,
+ action,
+ tooltip: tooltip.into(),
+ tooltip_meta: None,
+ }
+ }
+
+ #[allow(dead_code)]
+ pub fn meta(mut self, meta: Option<impl Into<SharedString>>) -> Self {
+ self.tooltip_meta = meta.map(|meta| meta.into());
+ self
+ }
+}
+
+impl RenderOnce for QuickActionBarButton {
+ type Rendered = IconButton;
+
+ fn render(self, _: &mut WindowContext) -> Self::Rendered {
+ let tooltip = self.tooltip.clone();
+ let action = self.action.boxed_clone();
+ let tooltip_meta = self.tooltip_meta.clone();
+
+ IconButton::new(self.id.clone(), self.icon)
+ .size(ButtonSize::Compact)
+ .icon_size(IconSize::Small)
+ .style(ButtonStyle::Subtle)
+ .selected(self.toggled)
+ .tooltip(move |cx| {
+ if let Some(meta) = &tooltip_meta {
+ Tooltip::with_meta(tooltip.clone(), Some(&*action), meta.clone(), cx)
+ } else {
+ Tooltip::for_action(tooltip.clone(), &*action, cx)
+ }
+ })
+ .on_click({
+ let action = self.action.boxed_clone();
+ move |_, cx| cx.dispatch_action(action.boxed_clone())
+ })
+ }
+}
+
+// fn render_quick_action_bar_button<
+// F: 'static + Fn(&mut QuickActionBar, &mut ViewContext<QuickActionBar>),
+// >(
+// index: usize,
+// icon: &'static str,
+// toggled: bool,
+// tooltip: (String, Option<Box<dyn Action>>),
+// cx: &mut ViewContext<QuickActionBar>,
+// on_click: F,
+// ) -> AnyElement<QuickActionBar> {
+// enum QuickActionBarButton {}
+
+// let theme = theme::current(cx);
+// let (tooltip_text, action) = tooltip;
+
+// MouseEventHandler::new::<QuickActionBarButton, _>(index, cx, |mouse_state, _| {
+// let style = theme
+// .workspace
+// .toolbar
+// .toggleable_tool
+// .in_state(toggled)
+// .style_for(mouse_state);
+// Svg::new(icon)
+// .with_color(style.color)
+// .constrained()
+// .with_width(style.icon_width)
+// .aligned()
+// .constrained()
+// .with_width(style.button_width)
+// .with_height(style.button_width)
+// .contained()
+// .with_style(style.container)
+// })
+// .with_cursor_style(CursorStyle::PointingHand)
+// .on_click(MouseButton::Left, move |_, pane, cx| on_click(pane, cx))
+// .with_tooltip::<QuickActionBarButton>(index, tooltip_text, action, theme.tooltip.clone(), cx)
+// .into_any_named("quick action bar button")
+// }
+
+impl ToolbarItemView for QuickActionBar {
+ fn set_active_pane_item(
+ &mut self,
+ active_pane_item: Option<&dyn ItemHandle>,
+ cx: &mut ViewContext<Self>,
+ ) -> ToolbarItemLocation {
+ match active_pane_item {
+ Some(active_item) => {
+ self.active_item = Some(active_item.boxed_clone());
+ self._inlay_hints_enabled_subscription.take();
+
+ if let Some(editor) = active_item.downcast::<Editor>() {
+ let mut inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
+ let mut supports_inlay_hints = editor.read(cx).supports_inlay_hints(cx);
+ self._inlay_hints_enabled_subscription =
+ Some(cx.observe(&editor, move |_, editor, cx| {
+ let editor = editor.read(cx);
+ let new_inlay_hints_enabled = editor.inlay_hints_enabled();
+ let new_supports_inlay_hints = editor.supports_inlay_hints(cx);
+ let should_notify = inlay_hints_enabled != new_inlay_hints_enabled
+ || supports_inlay_hints != new_supports_inlay_hints;
+ inlay_hints_enabled = new_inlay_hints_enabled;
+ supports_inlay_hints = new_supports_inlay_hints;
+ if should_notify {
+ cx.notify()
+ }
+ }));
+ ToolbarItemLocation::PrimaryRight
+ } else {
+ ToolbarItemLocation::Hidden
+ }
+ }
+ None => {
+ self.active_item = None;
+ ToolbarItemLocation::Hidden
+ }
+ }
+ }
+}
@@ -5,7 +5,7 @@ use crate::ColorScale;
use crate::{SystemColors, ThemeColors};
pub(crate) fn neutral() -> ColorScaleSet {
- slate()
+ sand()
}
impl ThemeColors {
@@ -29,12 +29,12 @@ impl ThemeColors {
element_disabled: neutral().light_alpha().step_3(),
drop_target_background: blue().light_alpha().step_2(),
ghost_element_background: system.transparent,
- ghost_element_hover: neutral().light_alpha().step_4(),
- ghost_element_active: neutral().light_alpha().step_5(),
+ ghost_element_hover: neutral().light_alpha().step_3(),
+ ghost_element_active: neutral().light_alpha().step_4(),
ghost_element_selected: neutral().light_alpha().step_5(),
ghost_element_disabled: neutral().light_alpha().step_3(),
- text: yellow().light().step_9(),
- text_muted: neutral().light().step_11(),
+ text: neutral().light().step_12(),
+ text_muted: neutral().light().step_10(),
text_placeholder: neutral().light().step_10(),
text_disabled: neutral().light().step_9(),
text_accent: blue().light().step_11(),
@@ -53,13 +53,13 @@ impl ThemeColors {
editor_gutter_background: neutral().light().step_1(), // todo!("pick the right colors")
editor_subheader_background: neutral().light().step_2(),
editor_active_line_background: neutral().light_alpha().step_3(),
- editor_line_number: neutral().light_alpha().step_3(), // todo!("pick the right colors")
- editor_active_line_number: neutral().light_alpha().step_3(), // todo!("pick the right colors")
- editor_highlighted_line_background: neutral().light_alpha().step_4(), // todo!("pick the right colors")
- editor_invisible: neutral().light_alpha().step_4(), // todo!("pick the right colors")
- editor_wrap_guide: neutral().light_alpha().step_4(), // todo!("pick the right colors")
- editor_active_wrap_guide: neutral().light_alpha().step_4(), // todo!("pick the right colors")
- editor_document_highlight_read_background: neutral().light_alpha().step_4(), // todo!("pick the right colors")
+ editor_line_number: neutral().light().step_10(),
+ editor_active_line_number: neutral().light().step_11(),
+ editor_highlighted_line_background: neutral().light_alpha().step_3(),
+ editor_invisible: neutral().light().step_10(),
+ editor_wrap_guide: neutral().light_alpha().step_7(),
+ editor_active_wrap_guide: neutral().light_alpha().step_8(), // todo!("pick the right colors")
+ editor_document_highlight_read_background: neutral().light_alpha().step_3(), // todo!("pick the right colors")
editor_document_highlight_write_background: neutral().light_alpha().step_4(), // todo!("pick the right colors")
terminal_background: neutral().light().step_1(),
terminal_ansi_black: black().light().step_12(),
@@ -1,47 +1,51 @@
+use std::sync::Arc;
+
use crate::{
+ default_color_scales,
one_themes::{one_dark, one_family},
- Theme, ThemeFamily,
+ Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme, ThemeColors,
+ ThemeFamily, ThemeStyles,
};
-// fn zed_pro_daylight() -> Theme {
-// Theme {
-// id: "zed_pro_daylight".to_string(),
-// name: "Zed Pro Daylight".into(),
-// appearance: Appearance::Light,
-// styles: ThemeStyles {
-// system: SystemColors::default(),
-// colors: ThemeColors::light(),
-// status: StatusColors::light(),
-// player: PlayerColors::light(),
-// syntax: Arc::new(SyntaxTheme::light()),
-// },
-// }
-// }
+fn zed_pro_daylight() -> Theme {
+ Theme {
+ id: "zed_pro_daylight".to_string(),
+ name: "Zed Pro Daylight".into(),
+ appearance: Appearance::Light,
+ styles: ThemeStyles {
+ system: SystemColors::default(),
+ colors: ThemeColors::light(),
+ status: StatusColors::light(),
+ player: PlayerColors::light(),
+ syntax: Arc::new(SyntaxTheme::light()),
+ },
+ }
+}
-// pub(crate) fn zed_pro_moonlight() -> Theme {
-// Theme {
-// id: "zed_pro_moonlight".to_string(),
-// name: "Zed Pro Moonlight".into(),
-// appearance: Appearance::Dark,
-// styles: ThemeStyles {
-// system: SystemColors::default(),
-// colors: ThemeColors::dark(),
-// status: StatusColors::dark(),
-// player: PlayerColors::dark(),
-// syntax: Arc::new(SyntaxTheme::dark()),
-// },
-// }
-// }
+pub(crate) fn zed_pro_moonlight() -> Theme {
+ Theme {
+ id: "zed_pro_moonlight".to_string(),
+ name: "Zed Pro Moonlight".into(),
+ appearance: Appearance::Dark,
+ styles: ThemeStyles {
+ system: SystemColors::default(),
+ colors: ThemeColors::dark(),
+ status: StatusColors::dark(),
+ player: PlayerColors::dark(),
+ syntax: Arc::new(SyntaxTheme::dark()),
+ },
+ }
+}
-// pub fn zed_pro_family() -> ThemeFamily {
-// ThemeFamily {
-// id: "zed_pro".to_string(),
-// name: "Zed Pro".into(),
-// author: "Zed Team".into(),
-// themes: vec![zed_pro_daylight(), zed_pro_moonlight()],
-// scales: default_color_scales(),
-// }
-// }
+pub fn zed_pro_family() -> ThemeFamily {
+ ThemeFamily {
+ id: "zed_pro".to_string(),
+ name: "Zed Pro".into(),
+ author: "Zed Team".into(),
+ themes: vec![zed_pro_daylight(), zed_pro_moonlight()],
+ scales: default_color_scales(),
+ }
+}
impl Default for ThemeFamily {
fn default() -> Self {
@@ -6,8 +6,8 @@ use gpui::{HighlightStyle, SharedString};
use refineable::Refineable;
use crate::{
- one_themes::one_family, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors,
- Theme, ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
+ one_themes::one_family, zed_pro_family, Appearance, PlayerColors, StatusColors, SyntaxTheme,
+ SystemColors, Theme, ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
};
pub struct ThemeRegistry {
@@ -117,7 +117,7 @@ impl Default for ThemeRegistry {
themes: HashMap::default(),
};
- this.insert_theme_families([one_family()]);
+ this.insert_theme_families([zed_pro_family(), one_family()]);
this
}
@@ -22,8 +22,8 @@ impl SyntaxTheme {
highlights: vec![
("attribute".into(), cyan().light().step_11().into()),
("boolean".into(), tomato().light().step_11().into()),
- ("comment".into(), neutral().light().step_11().into()),
- ("comment.doc".into(), iris().light().step_12().into()),
+ ("comment".into(), neutral().light().step_10().into()),
+ ("comment.doc".into(), iris().light().step_11().into()),
("constant".into(), red().light().step_9().into()),
("constructor".into(), red().light().step_9().into()),
("embedded".into(), red().light().step_9().into()),
@@ -32,11 +32,11 @@ impl SyntaxTheme {
("enum".into(), red().light().step_9().into()),
("function".into(), red().light().step_9().into()),
("hint".into(), red().light().step_9().into()),
- ("keyword".into(), orange().light().step_11().into()),
+ ("keyword".into(), orange().light().step_9().into()),
("label".into(), red().light().step_9().into()),
("link_text".into(), red().light().step_9().into()),
("link_uri".into(), red().light().step_9().into()),
- ("number".into(), red().light().step_9().into()),
+ ("number".into(), purple().light().step_10().into()),
("operator".into(), red().light().step_9().into()),
("predictive".into(), red().light().step_9().into()),
("preproc".into(), red().light().step_9().into()),
@@ -49,16 +49,16 @@ impl SyntaxTheme {
),
(
"punctuation.delimiter".into(),
- neutral().light().step_11().into(),
+ neutral().light().step_10().into(),
),
(
"punctuation.list_marker".into(),
blue().light().step_11().into(),
),
("punctuation.special".into(), red().light().step_9().into()),
- ("string".into(), jade().light().step_11().into()),
+ ("string".into(), jade().light().step_9().into()),
("string.escape".into(), red().light().step_9().into()),
- ("string.regex".into(), tomato().light().step_11().into()),
+ ("string.regex".into(), tomato().light().step_9().into()),
("string.special".into(), red().light().step_9().into()),
(
"string.special.symbol".into(),
@@ -67,7 +67,7 @@ impl SyntaxTheme {
("tag".into(), red().light().step_9().into()),
("text.literal".into(), red().light().step_9().into()),
("title".into(), red().light().step_9().into()),
- ("type".into(), red().light().step_9().into()),
+ ("type".into(), cyan().light().step_9().into()),
("variable".into(), red().light().step_9().into()),
("variable.special".into(), red().light().step_9().into()),
("variant".into(), red().light().step_9().into()),
@@ -5,6 +5,7 @@ mod context_menu;
mod disclosure;
mod divider;
mod icon;
+mod indicator;
mod keybinding;
mod label;
mod list;
@@ -24,6 +25,7 @@ pub use context_menu::*;
pub use disclosure::*;
pub use divider::*;
pub use icon::*;
+pub use indicator::*;
pub use keybinding::*;
pub use label::*;
pub use list::*;
@@ -1,15 +1,26 @@
-use gpui::{rems, svg, IntoElement, Svg};
+use gpui::{rems, svg, IntoElement, Rems, Svg};
use strum::EnumIter;
use crate::prelude::*;
#[derive(Default, PartialEq, Copy, Clone)]
pub enum IconSize {
+ XSmall,
Small,
#[default]
Medium,
}
+impl IconSize {
+ pub fn rems(self) -> Rems {
+ match self {
+ IconSize::XSmall => rems(12. / 16.),
+ IconSize::Small => rems(14. / 16.),
+ IconSize::Medium => rems(16. / 16.),
+ }
+ }
+}
+
#[derive(Debug, PartialEq, Copy, Clone, EnumIter)]
pub enum Icon {
Ai,
@@ -170,13 +181,8 @@ impl RenderOnce for IconElement {
type Rendered = Svg;
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- let svg_size = match self.size {
- IconSize::Small => rems(14. / 16.),
- IconSize::Medium => rems(16. / 16.),
- };
-
svg()
- .size(svg_size)
+ .size(self.size.rems())
.flex_none()
.path(self.path)
.text_color(self.color.color(cx))
@@ -0,0 +1,60 @@
+use gpui::{Div, Position};
+
+use crate::prelude::*;
+
+#[derive(Default)]
+pub enum IndicatorStyle {
+ #[default]
+ Dot,
+ Bar,
+}
+
+#[derive(IntoElement)]
+pub struct Indicator {
+ position: Position,
+ style: IndicatorStyle,
+ color: Color,
+}
+
+impl Indicator {
+ pub fn dot() -> Self {
+ Self {
+ position: Position::Relative,
+ style: IndicatorStyle::Dot,
+ color: Color::Default,
+ }
+ }
+
+ pub fn bar() -> Self {
+ Self {
+ position: Position::Relative,
+ style: IndicatorStyle::Dot,
+ color: Color::Default,
+ }
+ }
+
+ pub fn color(mut self, color: Color) -> Self {
+ self.color = color;
+ self
+ }
+
+ pub fn absolute(mut self) -> Self {
+ self.position = Position::Absolute;
+ self
+ }
+}
+
+impl RenderOnce for Indicator {
+ type Rendered = Div;
+
+ fn render(self, cx: &mut WindowContext) -> Self::Rendered {
+ div()
+ .flex_none()
+ .map(|this| match self.style {
+ IndicatorStyle::Dot => this.w_1p5().h_1p5().rounded_full(),
+ IndicatorStyle::Bar => this.w_full().h_1p5().rounded_t_md(),
+ })
+ .when(self.position == Position::Absolute, |this| this.absolute())
+ .bg(self.color.color(cx))
+ }
+}
@@ -8,5 +8,6 @@ pub use crate::clickable::*;
pub use crate::disableable::*;
pub use crate::fixed::*;
pub use crate::selectable::*;
+pub use crate::{h_stack, v_stack};
pub use crate::{ButtonCommon, Color, StyledExt};
pub use theme::ActiveTheme;
@@ -1,5 +1,5 @@
use crate::{
- item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
+ item::{ClosePosition, Item, ItemHandle, ItemSettings, WeakItemHandle},
toolbar::Toolbar,
workspace_settings::{AutosaveSetting, WorkspaceSettings},
NewCenterTerminal, NewFile, NewSearch, SplitDirection, ToggleZoom, Workspace,
@@ -27,7 +27,8 @@ use std::{
};
use ui::{
- h_stack, prelude::*, right_click_menu, Color, Icon, IconButton, IconElement, Label, Tooltip,
+ h_stack, prelude::*, right_click_menu, ButtonSize, Color, Icon, IconButton, IconSize,
+ Indicator, Label, Tooltip,
};
use ui::{v_stack, ContextMenu};
use util::truncate_and_remove_front;
@@ -1418,22 +1419,7 @@ impl Pane {
cx: &mut ViewContext<'_, Pane>,
) -> impl IntoElement {
let label = item.tab_content(Some(detail), cx);
- let close_icon = || {
- let id = item.item_id();
-
- div()
- .id(ix)
- .invisible()
- .group_hover("", |style| style.visible())
- .child(
- IconButton::new("close_tab", Icon::Close).on_click(cx.listener(
- move |pane, _, cx| {
- pane.close_item_by_id(id, SaveIntent::Close, cx)
- .detach_and_log_err(cx);
- },
- )),
- )
- };
+ let close_side = &ItemSettings::get_global(cx).close_position;
let (text_color, tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index {
false => (
@@ -1450,102 +1436,129 @@ impl Pane {
),
};
- let close_right = ItemSettings::get_global(cx).close_position.right();
let is_active = ix == self.active_item_index;
+ let indicator = {
+ let indicator_color = match (item.has_conflict(cx), item.is_dirty(cx)) {
+ (true, _) => Some(Color::Warning),
+ (_, true) => Some(Color::Accent),
+ (false, false) => None,
+ };
+
+ h_stack()
+ .w_3()
+ .h_3()
+ .justify_center()
+ .absolute()
+ .map(|this| match close_side {
+ ClosePosition::Left => this.right_1(),
+ ClosePosition::Right => this.left_1(),
+ })
+ .when_some(indicator_color, |this, indicator_color| {
+ this.child(Indicator::dot().color(indicator_color))
+ })
+ };
+
+ let close_button = {
+ let id = item.item_id();
+
+ h_stack()
+ .invisible()
+ .w_3()
+ .h_3()
+ .justify_center()
+ .absolute()
+ .map(|this| match close_side {
+ ClosePosition::Left => this.left_1(),
+ ClosePosition::Right => this.right_1(),
+ })
+ .group_hover("", |style| style.visible())
+ .child(
+ // TODO: Fix button size
+ IconButton::new("close tab", Icon::Close)
+ .icon_color(Color::Muted)
+ .size(ButtonSize::None)
+ .icon_size(IconSize::XSmall)
+ .on_click(cx.listener(move |pane, _, cx| {
+ pane.close_item_by_id(id, SaveIntent::Close, cx)
+ .detach_and_log_err(cx);
+ })),
+ )
+ };
+
let tab = div()
- .group("")
- .id(ix)
- .cursor_pointer()
- .when_some(item.tab_tooltip_text(cx), |div, text| {
- div.tooltip(move |cx| cx.build_view(|cx| Tooltip::new(text.clone())).into())
- })
- .on_click(cx.listener(move |v: &mut Self, e, cx| v.activate_item(ix, true, true, cx)))
- // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
- // .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
- // .on_drop(|_view, state: View<DraggedTab>, cx| {
- // eprintln!("{:?}", state.read(cx));
- // })
- .flex()
- .items_center()
- .justify_center()
- // todo!("Nate - I need to do some work to balance all the items in the tab once things stablize")
- .map(|this| {
- if close_right {
- this.pl_3().pr_1()
- } else {
- this.pr_1().pr_3()
- }
- })
- .py_1()
- .bg(tab_bg)
.border_color(cx.theme().colors().border)
- .text_color(if is_active {
- cx.theme().colors().text
- } else {
- cx.theme().colors().text_muted
- })
+ .bg(tab_bg)
+ // 30px @ 16px/rem
+ .h(rems(1.875))
.map(|this| {
+ let is_first_item = ix == 0;
let is_last_item = ix == self.items.len() - 1;
match ix.cmp(&self.active_item_index) {
- cmp::Ordering::Less => this.border_l().mr_px(),
+ cmp::Ordering::Less => {
+ if is_first_item {
+ this.pl_px().pr_px().border_b()
+ } else {
+ this.border_l().pr_px().border_b()
+ }
+ }
cmp::Ordering::Greater => {
if is_last_item {
- this.mr_px().ml_px()
+ this.pr_px().pl_px().border_b()
} else {
- this.border_r().ml_px()
+ this.border_r().pl_px().border_b()
+ }
+ }
+ cmp::Ordering::Equal => {
+ if is_first_item {
+ this.pl_px().border_r().pb_px()
+ } else {
+ this.border_l().border_r().pb_px()
}
}
- cmp::Ordering::Equal => this.border_l().border_r(),
}
})
- // .hover(|h| h.bg(tab_hover_bg))
- // .active(|a| a.bg(tab_active_bg))
.child(
- div()
- .flex()
- .items_center()
+ h_stack()
+ .group("")
+ .id(ix)
+ .relative()
+ .h_full()
+ .cursor_pointer()
+ .when_some(item.tab_tooltip_text(cx), |div, text| {
+ div.tooltip(move |cx| cx.build_view(|cx| Tooltip::new(text.clone())).into())
+ })
+ .on_click(
+ cx.listener(move |v: &mut Self, e, cx| v.activate_item(ix, true, true, cx)),
+ )
+ // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
+ // .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
+ // .on_drop(|_view, state: View<DraggedTab>, cx| {
+ // eprintln!("{:?}", state.read(cx));
+ // })
+ .px_5()
+ // .hover(|h| h.bg(tab_hover_bg))
+ // .active(|a| a.bg(tab_active_bg))
.gap_1()
.text_color(text_color)
- .children(
- item.has_conflict(cx)
- .then(|| {
- div().border().border_color(gpui::red()).child(
- IconElement::new(Icon::ExclamationTriangle)
- .size(ui::IconSize::Small)
- .color(Color::Warning),
- )
- })
- .or(item.is_dirty(cx).then(|| {
- div().border().border_color(gpui::red()).child(
- IconElement::new(Icon::ExclamationTriangle)
- .size(ui::IconSize::Small)
- .color(Color::Info),
- )
- })),
- )
- .children((!close_right).then(|| close_icon()))
- .child(label)
- .children(close_right.then(|| close_icon())),
+ .child(indicator)
+ .child(close_button)
+ .child(label),
);
right_click_menu(ix).trigger(tab).menu(|cx| {
ContextMenu::build(cx, |menu, cx| {
- menu.action(
- "Close Active Item",
- CloseActiveItem { save_intent: None }.boxed_clone(),
- )
- .action("Close Inactive Items", CloseInactiveItems.boxed_clone())
- .action("Close Clean Items", CloseCleanItems.boxed_clone())
- .action("Close Items To The Left", CloseItemsToTheLeft.boxed_clone())
- .action(
- "Close Items To The Right",
- CloseItemsToTheRight.boxed_clone(),
- )
- .action(
- "Close All Items",
- CloseAllItems { save_intent: None }.boxed_clone(),
- )
+ menu.action("Close", CloseActiveItem { save_intent: None }.boxed_clone())
+ .action("Close Others", CloseInactiveItems.boxed_clone())
+ .separator()
+ .action("Close Left", CloseItemsToTheLeft.boxed_clone())
+ .action("Close Right", CloseItemsToTheRight.boxed_clone())
+ .separator()
+ .action("Close Clean", CloseCleanItems.boxed_clone())
+ .action(
+ "Close All",
+ CloseAllItems { save_intent: None }.boxed_clone(),
+ )
})
})
}
@@ -1565,116 +1578,118 @@ impl Pane {
// Left Side
.child(
h_stack()
- .px_2()
.flex()
.flex_none()
.gap_1()
+ .px_1()
+ .border_b()
+ .border_r()
+ .border_color(cx.theme().colors().border)
// Nav Buttons
.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()),
- ),
+ IconButton::new("navigate_backward", Icon::ArrowLeft)
+ .icon_size(IconSize::Small)
+ .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()),
- ),
+ IconButton::new("navigate_forward", Icon::ArrowRight)
+ .icon_size(IconSize::Small)
+ .on_click({
+ let view = cx.view().clone();
+ move |_, cx| view.update(cx, Self::navigate_backward)
+ })
+ .disabled(!self.can_navigate_forward()),
),
)
.child(
- div().flex_1().h_full().child(
- div().id("tabs").flex().overflow_x_scroll().children(
- self.items
- .iter()
- .enumerate()
- .zip(self.tab_details(cx))
- .map(|((ix, item), detail)| self.render_tab(ix, item, detail, cx)),
+ div()
+ .relative()
+ .flex_1()
+ .h_full()
+ .overflow_hidden_x()
+ .child(
+ div()
+ .absolute()
+ .top_0()
+ .left_0()
+ .z_index(1)
+ .size_full()
+ .border_b()
+ .border_color(cx.theme().colors().border),
+ )
+ .child(
+ h_stack().id("tabs").z_index(2).children(
+ self.items
+ .iter()
+ .enumerate()
+ .zip(self.tab_details(cx))
+ .map(|((ix, item), detail)| self.render_tab(ix, item, detail, cx)),
+ ),
),
- ),
)
// Right Side
.child(
- div()
- .px_1()
+ h_stack()
.flex()
.flex_none()
- .gap_2()
- // Nav Buttons
+ .gap_1()
+ .px_1()
+ .border_b()
+ .border_l()
+ .border_color(cx.theme().colors().border)
.child(
div()
.flex()
.items_center()
.gap_px()
.child(
- div()
- .bg(gpui::blue())
- .border()
- .border_color(gpui::red())
- .child(IconButton::new("plus", Icon::Plus).on_click(
- cx.listener(|this, _, cx| {
- let menu = ContextMenu::build(cx, |menu, cx| {
- menu.action("New File", NewFile.boxed_clone())
- .action(
- "New Terminal",
- NewCenterTerminal.boxed_clone(),
- )
- .action("New Search", NewSearch.boxed_clone())
- });
- cx.subscribe(
- &menu,
- |this, _, event: &DismissEvent, cx| {
- this.focus(cx);
- this.new_item_menu = None;
- },
- )
- .detach();
- this.new_item_menu = Some(menu);
- }),
- ))
- .when_some(self.new_item_menu.as_ref(), |el, new_item_menu| {
- el.child(Self::render_menu_overlay(new_item_menu))
- }),
+ IconButton::new("plus", Icon::Plus)
+ .icon_size(IconSize::Small)
+ .on_click(cx.listener(|this, _, cx| {
+ let menu = ContextMenu::build(cx, |menu, cx| {
+ menu.action("New File", NewFile.boxed_clone())
+ .action(
+ "New Terminal",
+ NewCenterTerminal.boxed_clone(),
+ )
+ .action("New Search", NewSearch.boxed_clone())
+ });
+ cx.subscribe(&menu, |this, _, event: &DismissEvent, cx| {
+ this.focus(cx);
+ this.new_item_menu = None;
+ })
+ .detach();
+ this.new_item_menu = Some(menu);
+ })),
)
+ .when_some(self.new_item_menu.as_ref(), |el, new_item_menu| {
+ el.child(Self::render_menu_overlay(new_item_menu))
+ })
.child(
- div()
- .border()
- .border_color(gpui::red())
- .child(IconButton::new("split", Icon::Split).on_click(
- cx.listener(|this, _, cx| {
- let menu = ContextMenu::build(cx, |menu, cx| {
- menu.action("Split Right", SplitRight.boxed_clone())
- .action("Split Left", SplitLeft.boxed_clone())
- .action("Split Up", SplitUp.boxed_clone())
- .action("Split Down", SplitDown.boxed_clone())
- });
- cx.subscribe(
- &menu,
- |this, _, event: &DismissEvent, cx| {
- this.focus(cx);
- this.split_item_menu = None;
- },
- )
- .detach();
- this.split_item_menu = Some(menu);
- }),
- ))
- .when_some(
- self.split_item_menu.as_ref(),
- |el, split_item_menu| {
- el.child(Self::render_menu_overlay(split_item_menu))
- },
- ),
- ),
+ IconButton::new("split", Icon::Split)
+ .icon_size(IconSize::Small)
+ .on_click(cx.listener(|this, _, cx| {
+ let menu = ContextMenu::build(cx, |menu, cx| {
+ menu.action("Split Right", SplitRight.boxed_clone())
+ .action("Split Left", SplitLeft.boxed_clone())
+ .action("Split Up", SplitUp.boxed_clone())
+ .action("Split Down", SplitDown.boxed_clone())
+ });
+ cx.subscribe(&menu, |this, _, event: &DismissEvent, cx| {
+ this.focus(cx);
+ this.split_item_menu = None;
+ })
+ .detach();
+ this.split_item_menu = Some(menu);
+ })),
+ )
+ .when_some(self.split_item_menu.as_ref(), |el, split_item_menu| {
+ el.child(Self::render_menu_overlay(split_item_menu))
+ }),
),
)
}
@@ -2092,6 +2107,8 @@ impl Render for Pane {
v_stack()
.key_context("Pane")
.track_focus(&self.focus_handle)
+ .size_full()
+ .overflow_hidden()
.on_focus_in({
let this = this.clone();
move |event, cx| {
@@ -2159,7 +2176,6 @@ impl Render for Pane {
pane.close_all_items(action, cx)
.map(|task| task.detach_and_log_err(cx));
}))
- .size_full()
.on_action(
cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
pane.close_active_item(action, cx)
@@ -1,10 +1,10 @@
use crate::ItemHandle;
use gpui::{
- div, AnyView, Div, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View,
+ AnyView, Div, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View,
ViewContext, WindowContext,
};
use ui::prelude::*;
-use ui::{h_stack, v_stack, Icon, IconButton};
+use ui::{h_stack, v_stack};
pub enum ToolbarItemEvent {
ChangeLocation(ToolbarItemLocation),
@@ -87,25 +87,7 @@ impl Render for Toolbar {
.child(
h_stack()
.justify_between()
- // Toolbar left side
- .children(self.items.iter().map(|(child, _)| child.to_any()))
- // Toolbar right side
- .child(
- h_stack()
- .p_1()
- .child(
- div()
- .border()
- .border_color(gpui::red())
- .child(IconButton::new("buffer-search", Icon::MagnifyingGlass)),
- )
- .child(
- div()
- .border()
- .border_color(gpui::red())
- .child(IconButton::new("inline-assist", Icon::MagicWand)),
- ),
- ),
+ .children(self.items.iter().map(|(child, _)| child.to_any())),
)
}
}
@@ -55,7 +55,7 @@ outline = { package = "outline2", path = "../outline2" }
project = { package = "project2", path = "../project2" }
project_panel = { package = "project_panel2", path = "../project_panel2" }
# project_symbols = { path = "../project_symbols" }
-# quick_action_bar = { path = "../quick_action_bar" }
+quick_action_bar = { package = "quick_action_bar2", path = "../quick_action_bar2" }
# recent_projects = { path = "../recent_projects" }
rope = { package = "rope2", path = "../rope2"}
rpc = { package = "rpc2", path = "../rpc2" }
@@ -19,6 +19,7 @@ pub use open_listener::*;
use anyhow::{anyhow, Context as _};
use project_panel::ProjectPanel;
+use quick_action_bar::QuickActionBar;
use settings::{initial_local_settings_content, Settings};
use std::{borrow::Cow, ops::Deref, sync::Arc};
use terminal_view::terminal_panel::TerminalPanel;
@@ -100,11 +101,10 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
toolbar.add_item(breadcrumbs, cx);
let buffer_search_bar = cx.build_view(search::BufferSearchBar::new);
toolbar.add_item(buffer_search_bar.clone(), cx);
- // todo!()
- // let quick_action_bar = cx.add_view(|_| {
- // QuickActionBar::new(buffer_search_bar, workspace)
- // });
- // toolbar.add_item(quick_action_bar, cx);
+
+ let quick_action_bar = cx
+ .build_view(|_| QuickActionBar::new(buffer_search_bar, workspace));
+ toolbar.add_item(quick_action_bar, cx);
let diagnostic_editor_controls =
cx.build_view(|_| diagnostics::ToolbarControls::new());
// toolbar.add_item(diagnostic_editor_controls, cx);