Restructure Pane to have a single Toolbar with multiple items

This commit is contained in:
Antonio Scandurra 2022-03-29 11:48:21 +02:00
parent d296bb21a8
commit 9df2dacd85
9 changed files with 318 additions and 264 deletions

View file

@ -1,5 +1,5 @@
use super::{ItemHandle, SplitDirection};
use crate::{Item, Settings, WeakItemHandle, Workspace};
use crate::{toolbar::Toolbar, Item, Settings, WeakItemHandle, Workspace};
use collections::{HashMap, VecDeque};
use gpui::{
action,
@ -7,16 +7,11 @@ use gpui::{
geometry::{rect::RectF, vector::vec2f},
keymap::Binding,
platform::{CursorStyle, NavigationDirection},
AnyViewHandle, AppContext, Entity, MutableAppContext, Quad, RenderContext, Task, View,
ViewContext, ViewHandle, WeakViewHandle,
AppContext, Entity, MutableAppContext, Quad, RenderContext, Task, View, ViewContext,
ViewHandle, WeakViewHandle,
};
use project::{ProjectEntryId, ProjectPath};
use std::{
any::{Any, TypeId},
cell::RefCell,
cmp, mem,
rc::Rc,
};
use std::{any::Any, cell::RefCell, cmp, mem, rc::Rc};
use util::ResultExt;
action!(Split, SplitDirection);
@ -101,28 +96,7 @@ pub struct Pane {
items: Vec<Box<dyn ItemHandle>>,
active_item_index: usize,
nav_history: Rc<RefCell<NavHistory>>,
toolbars: HashMap<TypeId, Box<dyn ToolbarHandle>>,
active_toolbar_type: Option<TypeId>,
active_toolbar_visible: bool,
}
pub trait Toolbar: View {
fn active_item_changed(
&mut self,
item: Option<Box<dyn ItemHandle>>,
cx: &mut ViewContext<Self>,
) -> bool;
fn on_dismiss(&mut self, cx: &mut ViewContext<Self>);
}
trait ToolbarHandle {
fn active_item_changed(
&self,
item: Option<Box<dyn ItemHandle>>,
cx: &mut MutableAppContext,
) -> bool;
fn on_dismiss(&self, cx: &mut MutableAppContext);
fn to_any(&self) -> AnyViewHandle;
toolbar: ViewHandle<Toolbar>,
}
pub struct ItemNavHistory {
@ -158,14 +132,12 @@ pub struct NavigationEntry {
}
impl Pane {
pub fn new() -> Self {
pub fn new(cx: &mut ViewContext<Self>) -> Self {
Self {
items: Vec::new(),
active_item_index: 0,
nav_history: Default::default(),
toolbars: Default::default(),
active_toolbar_type: Default::default(),
active_toolbar_visible: false,
toolbar: cx.add_view(|_| Toolbar::new()),
}
}
@ -402,7 +374,7 @@ impl Pane {
self.items[prev_active_item_ix].deactivated(cx);
cx.emit(Event::ActivateItem { local });
}
self.update_active_toolbar(cx);
self.update_toolbar(cx);
if local {
self.focus_active_item(cx);
self.activate(cx);
@ -487,7 +459,7 @@ impl Pane {
self.focus_active_item(cx);
self.activate(cx);
}
self.update_active_toolbar(cx);
self.update_toolbar(cx);
cx.notify();
}
@ -502,63 +474,18 @@ impl Pane {
cx.emit(Event::Split(direction));
}
pub fn show_toolbar<F, V>(&mut self, cx: &mut ViewContext<Self>, build_toolbar: F)
where
F: FnOnce(&mut ViewContext<V>) -> V,
V: Toolbar,
{
let type_id = TypeId::of::<V>();
if self.active_toolbar_type != Some(type_id) {
self.dismiss_toolbar(cx);
let active_item = self.active_item();
self.toolbars
.entry(type_id)
.or_insert_with(|| Box::new(cx.add_view(build_toolbar)));
self.active_toolbar_type = Some(type_id);
self.active_toolbar_visible =
self.toolbars[&type_id].active_item_changed(active_item, cx);
cx.notify();
}
pub fn toolbar(&self) -> &ViewHandle<Toolbar> {
&self.toolbar
}
pub fn dismiss_toolbar(&mut self, cx: &mut ViewContext<Self>) {
if let Some(active_toolbar_type) = self.active_toolbar_type.take() {
self.toolbars
.get_mut(&active_toolbar_type)
.unwrap()
.on_dismiss(cx);
self.active_toolbar_visible = false;
self.focus_active_item(cx);
cx.notify();
}
}
pub fn toolbar<T: Toolbar>(&self) -> Option<ViewHandle<T>> {
self.toolbars
.get(&TypeId::of::<T>())
.and_then(|toolbar| toolbar.to_any().downcast())
}
pub fn active_toolbar(&self) -> Option<AnyViewHandle> {
let type_id = self.active_toolbar_type?;
let toolbar = self.toolbars.get(&type_id)?;
if self.active_toolbar_visible {
Some(toolbar.to_any())
} else {
None
}
}
fn update_active_toolbar(&mut self, cx: &mut ViewContext<Self>) {
let active_item = self.items.get(self.active_item_index);
for (toolbar_type_id, toolbar) in &self.toolbars {
let visible = toolbar.active_item_changed(active_item.cloned(), cx);
if Some(*toolbar_type_id) == self.active_toolbar_type {
self.active_toolbar_visible = visible;
}
}
fn update_toolbar(&mut self, cx: &mut ViewContext<Self>) {
let active_item = self
.items
.get(self.active_item_index)
.map(|item| item.as_ref());
self.toolbar.update(cx, |toolbar, cx| {
toolbar.set_active_pane_item(active_item, cx);
});
}
fn render_tabs(&self, cx: &mut RenderContext<Self>) -> ElementBox {
@ -713,11 +640,7 @@ impl View for Pane {
EventHandler::new(if let Some(active_item) = self.active_item() {
Flex::column()
.with_child(self.render_tabs(cx))
.with_children(
self.active_toolbar()
.as_ref()
.map(|view| ChildView::new(view).boxed()),
)
.with_child(ChildView::new(&self.toolbar).boxed())
.with_child(ChildView::new(active_item).flexible(1., true).boxed())
.boxed()
} else {
@ -740,24 +663,6 @@ impl View for Pane {
}
}
impl<T: Toolbar> ToolbarHandle for ViewHandle<T> {
fn active_item_changed(
&self,
item: Option<Box<dyn ItemHandle>>,
cx: &mut MutableAppContext,
) -> bool {
self.update(cx, |this, cx| this.active_item_changed(item, cx))
}
fn on_dismiss(&self, cx: &mut MutableAppContext) {
self.update(cx, |this, cx| this.on_dismiss(cx));
}
fn to_any(&self) -> AnyViewHandle {
self.into()
}
}
impl ItemNavHistory {
pub fn new<T: Item>(history: Rc<RefCell<NavHistory>>, item: &ViewHandle<T>) -> Self {
Self {

View file

@ -0,0 +1,131 @@
use crate::{ItemHandle, Settings};
use gpui::{
elements::*, AnyViewHandle, ElementBox, Entity, MutableAppContext, RenderContext, View,
ViewContext, ViewHandle,
};
pub trait ToolbarItemView: View {
fn set_active_pane_item(
&mut self,
active_pane_item: Option<&dyn crate::ItemHandle>,
cx: &mut ViewContext<Self>,
);
}
trait ToolbarItemViewHandle {
fn to_any(&self) -> AnyViewHandle;
fn set_active_pane_item(
&self,
active_pane_item: Option<&dyn ItemHandle>,
cx: &mut MutableAppContext,
);
}
pub struct Toolbar {
active_pane_item: Option<Box<dyn ItemHandle>>,
left_items: Vec<Box<dyn ToolbarItemViewHandle>>,
right_items: Vec<Box<dyn ToolbarItemViewHandle>>,
}
impl Entity for Toolbar {
type Event = ();
}
impl View for Toolbar {
fn ui_name() -> &'static str {
"Toolbar"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
let theme = &cx.global::<Settings>().theme.workspace.toolbar;
Flex::row()
.with_children(self.left_items.iter().map(|i| {
ChildView::new(i.as_ref())
.aligned()
.contained()
.with_margin_right(theme.item_spacing)
.boxed()
}))
.with_child(Empty::new().flexible(1., true).boxed())
.with_children(self.right_items.iter().map(|i| {
ChildView::new(i.as_ref())
.aligned()
.contained()
.with_margin_left(theme.item_spacing)
.boxed()
}))
.contained()
.with_style(theme.container)
.constrained()
.with_height(theme.height)
.boxed()
}
}
impl Toolbar {
pub fn new() -> Self {
Self {
active_pane_item: None,
left_items: Default::default(),
right_items: Default::default(),
}
}
pub fn add_left_item<T>(&mut self, item: ViewHandle<T>, cx: &mut ViewContext<Self>)
where
T: 'static + ToolbarItemView,
{
item.set_active_pane_item(self.active_pane_item.as_deref(), cx);
self.left_items.push(Box::new(item));
cx.notify();
}
pub fn add_right_item<T>(&mut self, item: ViewHandle<T>, cx: &mut ViewContext<Self>)
where
T: 'static + ToolbarItemView,
{
item.set_active_pane_item(self.active_pane_item.as_deref(), cx);
self.right_items.push(Box::new(item));
cx.notify();
}
pub fn set_active_pane_item(
&mut self,
item: Option<&dyn ItemHandle>,
cx: &mut ViewContext<Self>,
) {
self.active_pane_item = item.map(|item| item.boxed_clone());
for tool in self.left_items.iter().chain(&self.right_items) {
tool.set_active_pane_item(item, cx);
}
}
pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<ViewHandle<T>> {
self.left_items
.iter()
.chain(&self.right_items)
.find_map(|tool| tool.to_any().downcast())
}
}
impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
fn to_any(&self) -> AnyViewHandle {
self.into()
}
fn set_active_pane_item(
&self,
active_pane_item: Option<&dyn ItemHandle>,
cx: &mut MutableAppContext,
) {
self.update(cx, |this, cx| {
this.set_active_pane_item(active_pane_item, cx)
});
}
}
impl Into<AnyViewHandle> for &dyn ToolbarItemViewHandle {
fn into(self) -> AnyViewHandle {
self.to_any()
}
}

View file

@ -5,6 +5,7 @@ pub mod pane_group;
pub mod settings;
pub mod sidebar;
mod status_bar;
mod toolbar;
use anyhow::{anyhow, Context, Result};
use client::{
@ -47,6 +48,7 @@ use std::{
},
};
use theme::{Theme, ThemeRegistry};
pub use toolbar::ToolbarItemView;
use util::ResultExt;
type ProjectItemBuilders = HashMap<
@ -720,7 +722,7 @@ impl Workspace {
})
.detach();
let pane = cx.add_view(|_| Pane::new());
let pane = cx.add_view(|cx| Pane::new(cx));
let pane_id = pane.id();
cx.observe(&pane, move |me, _, cx| {
let active_entry = me.active_project_path(cx);
@ -733,6 +735,7 @@ impl Workspace {
})
.detach();
cx.focus(&pane);
cx.emit(Event::PaneAdded(pane.clone()));
let status_bar = cx.add_view(|cx| StatusBar::new(&pane, cx));
let mut current_user = params.user_store.read(cx).watch_current_user().clone();
@ -1051,7 +1054,7 @@ impl Workspace {
}
fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> {
let pane = cx.add_view(|_| Pane::new());
let pane = cx.add_view(|cx| Pane::new(cx));
let pane_id = pane.id();
cx.observe(&pane, move |me, _, cx| {
let active_entry = me.active_project_path(cx);