co-authored by
Nathan Sobo
parent
a7ac37a318
commit
b88b3e7657
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)]
|
||||
|
||||
+76
-10
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user