From b88b3e765747f5a734931a16b775533be2b93c83 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 20 Aug 2021 13:51:52 -0700 Subject: [PATCH] Add sidebars Co-Authored-By: Nathan Sobo --- zed/assets/icons/comment-16.svg | 3 + zed/assets/icons/folder-tree-16.svg | 3 + zed/assets/icons/user-16.svg | 3 + zed/assets/themes/_base.toml | 41 +++++++------ zed/assets/themes/dark.toml | 21 ++++--- zed/src/lib.rs | 1 + zed/src/project_browser.rs | 19 ++++++ zed/src/theme.rs | 8 +++ zed/src/workspace.rs | 86 ++++++++++++++++++++++---- zed/src/workspace/sidebar.rs | 93 +++++++++++++++++++++++++++++ 10 files changed, 243 insertions(+), 35 deletions(-) create mode 100644 zed/assets/icons/comment-16.svg create mode 100644 zed/assets/icons/folder-tree-16.svg create mode 100644 zed/assets/icons/user-16.svg create mode 100644 zed/src/project_browser.rs create mode 100644 zed/src/workspace/sidebar.rs diff --git a/zed/assets/icons/comment-16.svg b/zed/assets/icons/comment-16.svg new file mode 100644 index 0000000000..6316d3a4a9 --- /dev/null +++ b/zed/assets/icons/comment-16.svg @@ -0,0 +1,3 @@ + + + diff --git a/zed/assets/icons/folder-tree-16.svg b/zed/assets/icons/folder-tree-16.svg new file mode 100644 index 0000000000..f22773b159 --- /dev/null +++ b/zed/assets/icons/folder-tree-16.svg @@ -0,0 +1,3 @@ + + + diff --git a/zed/assets/icons/user-16.svg b/zed/assets/icons/user-16.svg new file mode 100644 index 0000000000..4ec153f538 --- /dev/null +++ b/zed/assets/icons/user-16.svg @@ -0,0 +1,3 @@ + + + diff --git a/zed/assets/themes/_base.toml b/zed/assets/themes/_base.toml index 2ad222c174..cf340282d5 100644 --- a/zed/assets/themes/_base.toml +++ b/zed/assets/themes/_base.toml @@ -2,22 +2,29 @@ background = "$surface.0" [tab] -background = "$surface.1" -text = "$text_color.dull" -border = { color = "#000000", width = 1.0 } +text = "$text.2" padding = { left = 10, right = 10 } -icon_close = "#383839" -icon_dirty = "#556de8" -icon_conflict = "#e45349" +icon_close = "$text.0" +icon_dirty = "$status.info" +icon_conflict = "$status.warn" [active_tab] extends = "$tab" -background = "$surface.2" -text = "$text_color.bright" +background = "$surface.1" +text = "$text.0" + +[sidebar] +padding = { left = 10, right = 10 } + +[sidebar_icon] +color = "$text.2" + +[active_sidebar_icon] +color = "$text.0" [selector] -background = "$surface.3" -text = "$text_color.bright" +background = "$surface.2" +text = "$text.0" padding = { top = 6.0, bottom = 6.0, left = 6.0, right = 6.0 } margin.top = 12.0 corner_radius = 6.0 @@ -35,13 +42,13 @@ extends = "$selector.item" background = "#094771" [editor] -background = "$surface.2" -gutter_background = "$surface.2" -active_line_background = "$surface.3" -line_number = "$text_color.dull" -line_number_active = "$text_color.bright" -text = "$text_color.normal" +background = "$surface.1" +gutter_background = "$surface.1" +active_line_background = "$surface.2" +line_number = "$text.2" +line_number_active = "$text.0" +text = "$text.1" replicas = [ - { selection = "#264f78", cursor = "$text_color.bright" }, + { selection = "#264f78", cursor = "$text.0" }, { selection = "#504f31", cursor = "#fcf154" }, ] diff --git a/zed/assets/themes/dark.toml b/zed/assets/themes/dark.toml index 2bbd37e934..b019057f65 100644 --- a/zed/assets/themes/dark.toml +++ b/zed/assets/themes/dark.toml @@ -1,15 +1,20 @@ extends = "_base" [surface] -0 = "#050101" -1 = "#131415" -2 = "#1c1d1e" -3 = "#3a3b3c" +0 = "#222324" +1 = "#141516" +2 = "#131415" -[text_color] -dull = "#5a5a5b" -bright = "#ffffff" -normal = "#d4d4d4" +[text] +0 = "#ffffff" +1 = "#b3b3b3" +2 = "#7b7d80" + +[status] +good = "#4fac63" +info = "#3c5dd4" +warn = "#faca50" +bad = "#b7372e" [syntax] keyword = { color = "#0086c0", weight = "bold" } diff --git a/zed/src/lib.rs b/zed/src/lib.rs index 492eaaf104..4cec473dfc 100644 --- a/zed/src/lib.rs +++ b/zed/src/lib.rs @@ -7,6 +7,7 @@ mod fuzzy; pub mod language; pub mod menus; mod operation_queue; +pub mod project_browser; pub mod rpc; pub mod settings; mod sum_tree; diff --git a/zed/src/project_browser.rs b/zed/src/project_browser.rs new file mode 100644 index 0000000000..552eab851c --- /dev/null +++ b/zed/src/project_browser.rs @@ -0,0 +1,19 @@ +use gpui::{elements::Empty, Element, Entity, View}; + +pub struct ProjectBrowser; + +pub enum Event {} + +impl Entity for ProjectBrowser { + type Event = Event; +} + +impl View for ProjectBrowser { + fn ui_name() -> &'static str { + "ProjectBrowser" + } + + fn render(&self, _: &gpui::RenderContext<'_, Self>) -> gpui::ElementBox { + Empty::new().boxed() + } +} diff --git a/zed/src/theme.rs b/zed/src/theme.rs index 52b87860c7..a8111d8b0a 100644 --- a/zed/src/theme.rs +++ b/zed/src/theme.rs @@ -33,6 +33,9 @@ pub struct Theme { pub workspace: Workspace, pub tab: Tab, pub active_tab: Tab, + pub sidebar: ContainerStyle, + pub sidebar_icon: SidebarIcon, + pub active_sidebar_icon: SidebarIcon, pub selector: Selector, pub editor: Editor, #[serde(deserialize_with = "deserialize_syntax_theme")] @@ -72,6 +75,11 @@ pub struct Tab { pub icon_conflict: Color, } +#[derive(Debug, Default, Deserialize)] +pub struct SidebarIcon { + pub color: Color, +} + #[derive(Debug, Default, Deserialize)] pub struct Selector { #[serde(flatten)] diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 3169e9e58d..7c133944f1 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -1,10 +1,12 @@ pub mod pane; pub mod pane_group; +pub mod sidebar; use crate::{ editor::{Buffer, Editor}, fs::Fs, language::LanguageRegistry, + project_browser::ProjectBrowser, rpc, settings::Settings, worktree::{File, Worktree}, @@ -25,6 +27,7 @@ use log::error; pub use pane::*; pub use pane_group::*; use postage::watch; +use sidebar::{Side, Sidebar}; use smol::prelude::*; use std::{ collections::{hash_map::Entry, HashMap, HashSet}, @@ -46,6 +49,10 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action("workspace:new_file", Workspace::open_new_file); cx.add_action("workspace:share_worktree", Workspace::share_worktree); cx.add_action("workspace:join_worktree", Workspace::join_worktree); + cx.add_action( + "workspace:toggle_sidebar_item", + Workspace::toggle_sidebar_item, + ); cx.add_bindings(vec![ Binding::new("cmd-s", "workspace:save", None), Binding::new("cmd-alt-i", "workspace:debug_elements", None), @@ -318,12 +325,6 @@ impl Clone for Box { } } -#[derive(Debug)] -pub struct State { - pub modal: Option, - pub center: PaneGroup, -} - pub struct Workspace { pub settings: watch::Receiver, languages: Arc, @@ -331,6 +332,8 @@ pub struct Workspace { fs: Arc, modal: Option, center: PaneGroup, + left_sidebar: Sidebar, + right_sidebar: Sidebar, panes: Vec>, active_pane: ViewHandle, worktrees: HashSet>, @@ -350,6 +353,19 @@ impl Workspace { }); cx.focus(&pane); + let mut left_sidebar = Sidebar::new(Side::Left); + left_sidebar.add_item( + "icons/folder-tree-16.svg", + cx.add_view(|_| ProjectBrowser).into(), + ); + + let mut right_sidebar = Sidebar::new(Side::Right); + right_sidebar.add_item( + "icons/comment-16.svg", + cx.add_view(|_| ProjectBrowser).into(), + ); + right_sidebar.add_item("icons/user-16.svg", cx.add_view(|_| ProjectBrowser).into()); + Workspace { modal: None, center: PaneGroup::new(pane.id()), @@ -359,6 +375,8 @@ impl Workspace { languages: app_state.languages.clone(), rpc: app_state.rpc.clone(), fs: app_state.fs.clone(), + left_sidebar, + right_sidebar, worktrees: Default::default(), items: Default::default(), loading_items: Default::default(), @@ -724,6 +742,19 @@ impl Workspace { } } + pub fn toggle_sidebar_item( + &mut self, + (side, item_ix): &(Side, usize), + cx: &mut ViewContext, + ) { + let sidebar = match side { + Side::Left => &mut self.left_sidebar, + Side::Right => &mut self.right_sidebar, + }; + sidebar.toggle_item(*item_ix); + cx.notify(); + } + pub fn debug_elements(&mut self, _: &(), cx: &mut ViewContext) { match to_string_pretty(&cx.debug_elements()) { Ok(json) => { @@ -892,12 +923,47 @@ impl View for Workspace { "Workspace" } - fn render(&self, _: &RenderContext) -> ElementBox { + fn render(&self, cx: &RenderContext) -> ElementBox { let settings = self.settings.borrow(); Container::new( - Stack::new() - .with_child(self.center.render()) - .with_children(self.modal.as_ref().map(|m| ChildView::new(m.id()).boxed())) + Flex::column() + .with_child( + ConstrainedBox::new(Empty::new().boxed()) + .with_height(cx.titlebar_height) + .named("titlebar"), + ) + .with_child( + Expanded::new( + 1.0, + Stack::new() + .with_child({ + let mut content = Flex::row(); + content.add_child(self.left_sidebar.render(&settings, cx)); + if let Some(panel) = self.left_sidebar.active_item() { + content.add_child( + ConstrainedBox::new(ChildView::new(panel.id()).boxed()) + .with_width(200.0) + .named("left panel"), + ); + } + content.add_child(Expanded::new(1.0, self.center.render()).boxed()); + if let Some(panel) = self.right_sidebar.active_item() { + content.add_child( + ConstrainedBox::new(ChildView::new(panel.id()).boxed()) + .with_width(200.0) + .named("right panel"), + ); + } + content.add_child(self.right_sidebar.render(&settings, cx)); + content.boxed() + }) + .with_children( + self.modal.as_ref().map(|m| ChildView::new(m.id()).boxed()), + ) + .boxed(), + ) + .boxed(), + ) .boxed(), ) .with_background_color(settings.theme.workspace.background) diff --git a/zed/src/workspace/sidebar.rs b/zed/src/workspace/sidebar.rs new file mode 100644 index 0000000000..7d7e1f7c6e --- /dev/null +++ b/zed/src/workspace/sidebar.rs @@ -0,0 +1,93 @@ +use crate::Settings; +use gpui::{ + elements::{ + Align, ConstrainedBox, Container, Flex, MouseEventHandler, ParentElement as _, Svg, + }, + AnyViewHandle, AppContext, Element as _, ElementBox, +}; + +pub struct Sidebar { + side: Side, + items: Vec, + active_item_ix: Option, +} + +#[derive(Clone, Copy)] +pub enum Side { + Left, + Right, +} + +struct Item { + icon_path: &'static str, + view: AnyViewHandle, +} + +impl Sidebar { + pub fn new(side: Side) -> Self { + Self { + side, + items: Default::default(), + active_item_ix: None, + } + } + + pub fn add_item(&mut self, icon_path: &'static str, view: AnyViewHandle) { + self.items.push(Item { icon_path, view }); + } + + pub fn toggle_item(&mut self, item_ix: usize) { + if self.active_item_ix == Some(item_ix) { + self.active_item_ix = None; + } else { + self.active_item_ix = Some(item_ix) + } + } + + pub fn active_item(&self) -> Option<&AnyViewHandle> { + self.active_item_ix + .and_then(|ix| self.items.get(ix)) + .map(|item| &item.view) + } + + pub fn render(&self, settings: &Settings, cx: &AppContext) -> ElementBox { + let side = self.side; + let line_height = cx.font_cache().line_height( + cx.font_cache().default_font(settings.ui_font_family), + settings.ui_font_size, + ); + + Container::new( + Flex::column() + .with_children(self.items.iter().enumerate().map(|(item_ix, item)| { + let theme = if Some(item_ix) == self.active_item_ix { + &settings.theme.active_sidebar_icon + } else { + &settings.theme.sidebar_icon + }; + enum SidebarButton {} + MouseEventHandler::new::(item.view.id(), cx, |_| { + ConstrainedBox::new( + Align::new( + ConstrainedBox::new( + Svg::new(item.icon_path).with_color(theme.color).boxed(), + ) + .with_height(line_height) + .boxed(), + ) + .boxed(), + ) + .with_height(line_height + 16.0) + .boxed() + }) + .on_click(move |cx| { + cx.dispatch_action("workspace:toggle_sidebar_item", (side, item_ix)) + }) + .boxed() + })) + .boxed(), + ) + .with_style(&settings.theme.sidebar) + .boxed() + } +}