Add sidebars

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2021-08-20 13:51:52 -07:00
parent a7ac37a318
commit b88b3e7657
10 changed files with 243 additions and 35 deletions

View file

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.01234 1.86426C4.13913 1.86426 1.00077 4.41444 1.00077 7.56176C1.00077 8.86644 1.54614 10.0613 2.45007 11.0186C2.04248 12.1006 1.19361 13.0149 1.17991 13.0251C0.998442 13.2168 0.950506 13.4976 1.05323 13.7373C1.15939 13.9769 1.39392 14.1358 1.65743 14.1358C3.34203 14.1358 4.64588 13.4305 5.46764 12.8689C6.23461 13.1168 7.11663 13.2593 8.01234 13.2593C11.8855 13.2593 15 10.7083 15 7.56176C15 4.41526 11.8855 1.86426 8.01234 1.86426ZM8.01508 11.9445C7.28235 11.9445 6.56002 11.8315 5.86811 11.6125L5.24494 11.4173L4.7108 11.7939C4.32047 12.0711 3.78276 12.3796 3.13577 12.5883C3.33778 12.2563 3.52939 11.883 3.68032 11.4858L3.97122 10.7188L3.4064 10.1198C2.91252 9.5915 2.31675 8.7177 2.31675 7.56176C2.31675 5.14443 4.87104 3.17907 8.01426 3.17907C11.1575 3.17907 13.7118 5.14443 13.7118 7.56176C13.7118 9.97909 11.1569 11.9445 8.01508 11.9445Z" fill="#7E7E83"/>
</svg>

After

Width:  |  Height:  |  Size: 979 B

View file

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.2222 9.55561H11.8889L10.8156 8.89377C10.6931 8.81672 10.5302 8.77783 10.4062 8.77783H8.77778C8.3483 8.77783 8 9.12613 8 9.55561V13.4445C8 13.874 8.3483 14.2223 8.77778 14.2223H14.2222C14.6517 14.2223 15 13.874 15 13.4445V10.3334C15 9.90318 14.6524 9.55561 14.2222 9.55561ZM13.8333 13.0556H9.16667V9.9445H10.2969L11.2764 10.5487C11.4611 10.6615 11.6726 10.7223 11.8889 10.7223H13.8333V13.0556ZM6.63889 5.66672C6.96215 5.66672 7.22222 5.40665 7.22222 5.08339C7.22222 4.76012 6.96215 4.50005 6.63889 4.50005H2.16667V2.36117C2.16667 2.03887 1.90538 1.77783 1.58333 1.77783C1.26128 1.77783 1 2.03887 1 2.36117V11.3056C1 12.0567 1.60934 12.6667 2.36111 12.6667H6.63889C6.96215 12.6667 7.22222 12.4067 7.22222 12.0834C7.22222 11.7611 6.96094 11.5001 6.63889 11.5001H2.36111C2.25417 11.5001 2.16667 11.4125 2.16667 11.3056V5.66672H6.63889ZM14.2222 2.55561H11.8889L10.8156 1.89377C10.6931 1.81789 10.5302 1.77783 10.4062 1.77783H8.77778C8.3483 1.77783 8 2.12613 8 2.55561V6.4445C8 6.87398 8.3483 7.22228 8.77778 7.22228H14.2222C14.6517 7.22228 15 6.87398 15 6.4445V3.33339C15 2.90391 14.6524 2.55561 14.2222 2.55561ZM13.8333 6.05561H9.16667V2.9445H10.2969L11.2764 3.54873C11.4611 3.66224 11.6726 3.72228 11.8889 3.72228H13.8333V6.05561Z" fill="#7E7E83"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.3125 9.3125H6.6875C4.02969 9.3125 1.875 11.4672 1.875 14.125C1.875 14.6082 2.26684 15 2.75 15H13.25C13.7332 15 14.125 14.6082 14.125 14.125C14.125 11.4672 11.9703 9.3125 9.3125 9.3125ZM3.21457 13.6875C3.43059 11.9621 4.90469 10.625 6.6875 10.625H9.3125C11.0942 10.625 12.5691 11.9635 12.7852 13.6875H3.21457ZM8 8C9.93293 8 11.5 6.43293 11.5 4.5C11.5 2.56707 9.93293 1 8 1C6.06707 1 4.5 2.56707 4.5 4.5C4.5 6.4332 6.0668 8 8 8ZM8 2.3125C9.20613 2.3125 10.1875 3.29387 10.1875 4.5C10.1875 5.70613 9.20613 6.6875 8 6.6875C6.79387 6.6875 5.8125 5.70586 5.8125 4.5C5.8125 3.29387 6.79414 2.3125 8 2.3125Z" fill="#9BA8BE"/>
</svg>

After

Width:  |  Height:  |  Size: 733 B

View file

@ -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" },
]

View file

@ -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" }

View file

@ -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;

View file

@ -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()
}
}

View file

@ -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)]

View file

@ -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<dyn ItemHandle> {
}
}
#[derive(Debug)]
pub struct State {
pub modal: Option<usize>,
pub center: PaneGroup,
}
pub struct Workspace {
pub settings: watch::Receiver<Settings>,
languages: Arc<LanguageRegistry>,
@ -331,6 +332,8 @@ pub struct Workspace {
fs: Arc<dyn Fs>,
modal: Option<AnyViewHandle>,
center: PaneGroup,
left_sidebar: Sidebar,
right_sidebar: Sidebar,
panes: Vec<ViewHandle<Pane>>,
active_pane: ViewHandle<Pane>,
worktrees: HashSet<ModelHandle<Worktree>>,
@ -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<Self>,
) {
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<Self>) {
match to_string_pretty(&cx.debug_elements()) {
Ok(json) => {
@ -892,12 +923,47 @@ impl View for Workspace {
"Workspace"
}
fn render(&self, _: &RenderContext<Self>) -> ElementBox {
fn render(&self, cx: &RenderContext<Self>) -> 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)

View file

@ -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<Item>,
active_item_ix: Option<usize>,
}
#[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::<SidebarButton, _>(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()
}
}