mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-27 12:54:42 +00:00
Restructure Pane
to have a single Toolbar
with multiple items
This commit is contained in:
parent
d296bb21a8
commit
9df2dacd85
9 changed files with 318 additions and 264 deletions
|
@ -8,7 +8,7 @@ use gpui::{
|
|||
use language::OffsetRangeExt;
|
||||
use project::search::SearchQuery;
|
||||
use std::ops::Range;
|
||||
use workspace::{ItemHandle, Pane, Settings, Toolbar, Workspace};
|
||||
use workspace::{ItemHandle, Pane, Settings, ToolbarItemView};
|
||||
|
||||
action!(Deploy, bool);
|
||||
action!(Dismiss);
|
||||
|
@ -38,7 +38,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
cx.add_action(SearchBar::select_match_on_pane);
|
||||
}
|
||||
|
||||
struct SearchBar {
|
||||
pub struct SearchBar {
|
||||
query_editor: ViewHandle<Editor>,
|
||||
active_editor: Option<ViewHandle<Editor>>,
|
||||
active_match_index: Option<usize>,
|
||||
|
@ -66,69 +66,69 @@ impl View for SearchBar {
|
|||
}
|
||||
|
||||
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let theme = cx.global::<Settings>().theme.clone();
|
||||
let editor_container = if self.query_contains_error {
|
||||
theme.search.invalid_editor
|
||||
if self.dismissed {
|
||||
Empty::new().boxed()
|
||||
} else {
|
||||
theme.search.editor.input.container
|
||||
};
|
||||
Flex::row()
|
||||
.with_child(
|
||||
ChildView::new(&self.query_editor)
|
||||
.contained()
|
||||
.with_style(editor_container)
|
||||
.aligned()
|
||||
.constrained()
|
||||
.with_max_width(theme.search.editor.max_width)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_child(self.render_search_option("Case", SearchOption::CaseSensitive, cx))
|
||||
.with_child(self.render_search_option("Word", SearchOption::WholeWord, cx))
|
||||
.with_child(self.render_search_option("Regex", SearchOption::Regex, cx))
|
||||
.contained()
|
||||
.with_style(theme.search.option_button_group)
|
||||
.aligned()
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_child(self.render_nav_button("<", Direction::Prev, cx))
|
||||
.with_child(self.render_nav_button(">", Direction::Next, cx))
|
||||
.aligned()
|
||||
.boxed(),
|
||||
)
|
||||
.with_children(self.active_editor.as_ref().and_then(|editor| {
|
||||
let matches = self.editors_with_matches.get(&editor.downgrade())?;
|
||||
let message = if let Some(match_ix) = self.active_match_index {
|
||||
format!("{}/{}", match_ix + 1, matches.len())
|
||||
} else {
|
||||
"No matches".to_string()
|
||||
};
|
||||
|
||||
Some(
|
||||
Label::new(message, theme.search.match_index.text.clone())
|
||||
let theme = cx.global::<Settings>().theme.clone();
|
||||
let editor_container = if self.query_contains_error {
|
||||
theme.search.invalid_editor
|
||||
} else {
|
||||
theme.search.editor.container
|
||||
};
|
||||
Flex::row()
|
||||
.with_child(
|
||||
ChildView::new(&self.query_editor)
|
||||
.contained()
|
||||
.with_style(theme.search.match_index.container)
|
||||
.with_style(editor_container)
|
||||
.aligned()
|
||||
.constrained()
|
||||
.with_max_width(theme.search.max_editor_width)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_child(self.render_search_option(
|
||||
"Case",
|
||||
SearchOption::CaseSensitive,
|
||||
cx,
|
||||
))
|
||||
.with_child(self.render_search_option("Word", SearchOption::WholeWord, cx))
|
||||
.with_child(self.render_search_option("Regex", SearchOption::Regex, cx))
|
||||
.contained()
|
||||
.with_style(theme.search.option_button_group)
|
||||
.aligned()
|
||||
.boxed(),
|
||||
)
|
||||
}))
|
||||
.contained()
|
||||
.with_style(theme.search.container)
|
||||
.constrained()
|
||||
.with_height(theme.workspace.toolbar.height)
|
||||
.named("search bar")
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_child(self.render_nav_button("<", Direction::Prev, cx))
|
||||
.with_child(self.render_nav_button(">", Direction::Next, cx))
|
||||
.aligned()
|
||||
.boxed(),
|
||||
)
|
||||
.with_children(self.active_editor.as_ref().and_then(|editor| {
|
||||
let matches = self.editors_with_matches.get(&editor.downgrade())?;
|
||||
let message = if let Some(match_ix) = self.active_match_index {
|
||||
format!("{}/{}", match_ix + 1, matches.len())
|
||||
} else {
|
||||
"No matches".to_string()
|
||||
};
|
||||
|
||||
Some(
|
||||
Label::new(message, theme.search.match_index.text.clone())
|
||||
.contained()
|
||||
.with_style(theme.search.match_index.container)
|
||||
.aligned()
|
||||
.boxed(),
|
||||
)
|
||||
}))
|
||||
.named("search bar")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Toolbar for SearchBar {
|
||||
fn active_item_changed(
|
||||
&mut self,
|
||||
item: Option<Box<dyn ItemHandle>>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> bool {
|
||||
impl ToolbarItemView for SearchBar {
|
||||
fn set_active_pane_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
|
||||
self.active_editor_subscription.take();
|
||||
self.active_editor.take();
|
||||
self.pending_search.take();
|
||||
|
@ -139,29 +139,19 @@ impl Toolbar for SearchBar {
|
|||
Some(cx.subscribe(&editor, Self::on_active_editor_event));
|
||||
self.active_editor = Some(editor);
|
||||
self.update_matches(false, cx);
|
||||
return true;
|
||||
self.dismissed = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn on_dismiss(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.dismissed = true;
|
||||
for (editor, _) in &self.editors_with_matches {
|
||||
if let Some(editor) = editor.upgrade(cx) {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.clear_background_highlights::<Self>(cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
self.dismiss(&Dismiss, cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl SearchBar {
|
||||
fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||
let query_editor = cx.add_view(|cx| {
|
||||
Editor::auto_height(2, Some(|theme| theme.search.editor.input.clone()), cx)
|
||||
});
|
||||
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||
let query_editor =
|
||||
cx.add_view(|cx| Editor::auto_height(2, Some(|theme| theme.search.editor.clone()), cx));
|
||||
cx.subscribe(&query_editor, Self::on_query_editor_event)
|
||||
.detach();
|
||||
|
||||
|
@ -176,10 +166,73 @@ impl SearchBar {
|
|||
regex: false,
|
||||
pending_search: None,
|
||||
query_contains_error: false,
|
||||
dismissed: false,
|
||||
dismissed: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
|
||||
self.dismissed = true;
|
||||
for (editor, _) in &self.editors_with_matches {
|
||||
if let Some(editor) = editor.upgrade(cx) {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.clear_background_highlights::<Self>(cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(active_editor) = self.active_editor.as_ref() {
|
||||
cx.focus(active_editor);
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn show(&mut self, focus: bool, cx: &mut ViewContext<Self>) -> bool {
|
||||
let editor = if let Some(editor) = self.active_editor.clone() {
|
||||
editor
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let display_map = editor
|
||||
.update(cx, |editor, cx| editor.snapshot(cx))
|
||||
.display_snapshot;
|
||||
let selection = editor
|
||||
.read(cx)
|
||||
.newest_selection_with_snapshot::<usize>(&display_map.buffer_snapshot);
|
||||
|
||||
let mut text: String;
|
||||
if selection.start == selection.end {
|
||||
let point = selection.start.to_display_point(&display_map);
|
||||
let range = editor::movement::surrounding_word(&display_map, point);
|
||||
let range = range.start.to_offset(&display_map, Bias::Left)
|
||||
..range.end.to_offset(&display_map, Bias::Right);
|
||||
text = display_map.buffer_snapshot.text_for_range(range).collect();
|
||||
if text.trim().is_empty() {
|
||||
text = String::new();
|
||||
}
|
||||
} else {
|
||||
text = display_map
|
||||
.buffer_snapshot
|
||||
.text_for_range(selection.start..selection.end)
|
||||
.collect();
|
||||
}
|
||||
|
||||
if !text.is_empty() {
|
||||
self.set_query(&text, cx);
|
||||
}
|
||||
|
||||
if focus {
|
||||
let query_editor = self.query_editor.clone();
|
||||
query_editor.update(cx, |query_editor, cx| {
|
||||
query_editor.select_all(&editor::SelectAll, cx);
|
||||
});
|
||||
cx.focus_self();
|
||||
}
|
||||
|
||||
self.dismissed = false;
|
||||
cx.notify();
|
||||
true
|
||||
}
|
||||
|
||||
fn set_query(&mut self, query: &str, cx: &mut ViewContext<Self>) {
|
||||
self.query_editor.update(cx, |query_editor, cx| {
|
||||
query_editor.buffer().update(cx, |query_buffer, cx| {
|
||||
|
@ -238,61 +291,13 @@ impl SearchBar {
|
|||
.boxed()
|
||||
}
|
||||
|
||||
fn deploy(workspace: &mut Workspace, Deploy(focus): &Deploy, cx: &mut ViewContext<Workspace>) {
|
||||
workspace.active_pane().update(cx, |pane, cx| {
|
||||
pane.show_toolbar(cx, |cx| SearchBar::new(cx));
|
||||
|
||||
if let Some(search_bar) = pane
|
||||
.active_toolbar()
|
||||
.and_then(|toolbar| toolbar.downcast::<Self>())
|
||||
{
|
||||
search_bar.update(cx, |search_bar, _| search_bar.dismissed = false);
|
||||
let editor = pane.active_item().unwrap().act_as::<Editor>(cx).unwrap();
|
||||
let display_map = editor
|
||||
.update(cx, |editor, cx| editor.snapshot(cx))
|
||||
.display_snapshot;
|
||||
let selection = editor
|
||||
.read(cx)
|
||||
.newest_selection_with_snapshot::<usize>(&display_map.buffer_snapshot);
|
||||
|
||||
let mut text: String;
|
||||
if selection.start == selection.end {
|
||||
let point = selection.start.to_display_point(&display_map);
|
||||
let range = editor::movement::surrounding_word(&display_map, point);
|
||||
let range = range.start.to_offset(&display_map, Bias::Left)
|
||||
..range.end.to_offset(&display_map, Bias::Right);
|
||||
text = display_map.buffer_snapshot.text_for_range(range).collect();
|
||||
if text.trim().is_empty() {
|
||||
text = String::new();
|
||||
}
|
||||
} else {
|
||||
text = display_map
|
||||
.buffer_snapshot
|
||||
.text_for_range(selection.start..selection.end)
|
||||
.collect();
|
||||
}
|
||||
|
||||
if !text.is_empty() {
|
||||
search_bar.update(cx, |search_bar, cx| search_bar.set_query(&text, cx));
|
||||
}
|
||||
|
||||
if *focus {
|
||||
let query_editor = search_bar.read(cx).query_editor.clone();
|
||||
query_editor.update(cx, |query_editor, cx| {
|
||||
query_editor.select_all(&editor::SelectAll, cx);
|
||||
});
|
||||
cx.focus(&search_bar);
|
||||
}
|
||||
} else {
|
||||
cx.propagate_action();
|
||||
fn deploy(pane: &mut Pane, Deploy(focus): &Deploy, cx: &mut ViewContext<Pane>) {
|
||||
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<SearchBar>() {
|
||||
if search_bar.update(cx, |search_bar, cx| search_bar.show(*focus, cx)) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn dismiss(pane: &mut Pane, _: &Dismiss, cx: &mut ViewContext<Pane>) {
|
||||
if pane.toolbar::<SearchBar>().is_some() {
|
||||
pane.dismiss_toolbar(cx);
|
||||
}
|
||||
cx.propagate_action();
|
||||
}
|
||||
|
||||
fn focus_editor(&mut self, _: &FocusEditor, cx: &mut ViewContext<Self>) {
|
||||
|
@ -346,7 +351,7 @@ impl SearchBar {
|
|||
}
|
||||
|
||||
fn select_match_on_pane(pane: &mut Pane, action: &SelectMatch, cx: &mut ViewContext<Pane>) {
|
||||
if let Some(search_bar) = pane.toolbar::<SearchBar>() {
|
||||
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<SearchBar>() {
|
||||
search_bar.update(cx, |search_bar, cx| search_bar.select_match(action, cx));
|
||||
}
|
||||
}
|
||||
|
@ -541,7 +546,7 @@ mod tests {
|
|||
|
||||
let search_bar = cx.add_view(Default::default(), |cx| {
|
||||
let mut search_bar = SearchBar::new(cx);
|
||||
search_bar.active_item_changed(Some(Box::new(editor.clone())), cx);
|
||||
search_bar.set_active_pane_item(Some(&editor), cx);
|
||||
search_bar
|
||||
});
|
||||
|
||||
|
|
|
@ -336,8 +336,7 @@ impl ProjectSearchView {
|
|||
.detach();
|
||||
|
||||
let query_editor = cx.add_view(|cx| {
|
||||
let mut editor =
|
||||
Editor::single_line(Some(|theme| theme.search.editor.input.clone()), cx);
|
||||
let mut editor = Editor::single_line(Some(|theme| theme.search.editor.clone()), cx);
|
||||
editor.set_text(query_text, cx);
|
||||
editor
|
||||
});
|
||||
|
@ -569,7 +568,7 @@ impl ProjectSearchView {
|
|||
let editor_container = if self.query_contains_error {
|
||||
theme.search.invalid_editor
|
||||
} else {
|
||||
theme.search.editor.input.container
|
||||
theme.search.editor.container
|
||||
};
|
||||
Flex::row()
|
||||
.with_child(
|
||||
|
@ -578,7 +577,7 @@ impl ProjectSearchView {
|
|||
.with_style(editor_container)
|
||||
.aligned()
|
||||
.constrained()
|
||||
.with_max_width(theme.search.editor.max_width)
|
||||
.with_max_width(theme.search.max_editor_width)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
|
@ -615,7 +614,7 @@ impl ProjectSearchView {
|
|||
})
|
||||
})
|
||||
.contained()
|
||||
.with_style(theme.search.container)
|
||||
.with_style(theme.workspace.toolbar.container)
|
||||
.constrained()
|
||||
.with_height(theme.workspace.toolbar.height)
|
||||
.named("project search")
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
pub use buffer_search::SearchBar;
|
||||
use editor::{Anchor, MultiBufferSnapshot};
|
||||
use gpui::{action, MutableAppContext};
|
||||
use std::{
|
||||
cmp::{self, Ordering},
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
use editor::{Anchor, MultiBufferSnapshot};
|
||||
use gpui::{action, MutableAppContext};
|
||||
|
||||
mod buffer_search;
|
||||
mod project_search;
|
||||
|
||||
|
|
|
@ -94,14 +94,18 @@ pub struct Tab {
|
|||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
pub struct Toolbar {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub height: f32,
|
||||
pub item_spacing: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
pub struct Search {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub editor: FindEditor,
|
||||
pub max_editor_width: f32,
|
||||
pub editor: FieldEditor,
|
||||
pub invalid_editor: ContainerStyle,
|
||||
pub option_button_group: ContainerStyle,
|
||||
pub option_button: ContainedText,
|
||||
|
@ -115,13 +119,6 @@ pub struct Search {
|
|||
pub tab_icon_spacing: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
pub struct FindEditor {
|
||||
#[serde(flatten)]
|
||||
pub input: FieldEditor,
|
||||
pub max_width: f32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
pub struct Sidebar {
|
||||
#[serde(flatten)]
|
||||
|
|
|
@ -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 {
|
||||
|
|
131
crates/workspace/src/toolbar.rs
Normal file
131
crates/workspace/src/toolbar.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -85,7 +85,10 @@ diagnostic_message = "$text.2"
|
|||
lsp_message = "$text.2"
|
||||
|
||||
[workspace.toolbar]
|
||||
background = "$surface.1"
|
||||
border = { color = "$border.0", width = 1, left = false, right = false, bottom = true, top = false }
|
||||
height = 44
|
||||
item_spacing = 8
|
||||
|
||||
[panel]
|
||||
padding = { top = 12, left = 12, bottom = 12, right = 12 }
|
||||
|
@ -353,8 +356,8 @@ tab_icon_spacing = 4
|
|||
tab_summary_spacing = 10
|
||||
|
||||
[search]
|
||||
max_editor_width = 400
|
||||
match_background = "$state.highlighted_line"
|
||||
background = "$surface.1"
|
||||
results_status = { extends = "$text.0", size = 18 }
|
||||
tab_icon_width = 14
|
||||
tab_icon_spacing = 4
|
||||
|
@ -388,7 +391,6 @@ extends = "$text.1"
|
|||
padding = 6
|
||||
|
||||
[search.editor]
|
||||
max_width = 400
|
||||
background = "$surface.0"
|
||||
corner_radius = 6
|
||||
padding = { left = 13, right = 13, top = 3, bottom = 3 }
|
||||
|
|
|
@ -21,6 +21,7 @@ pub use lsp;
|
|||
use project::Project;
|
||||
pub use project::{self, fs};
|
||||
use project_panel::ProjectPanel;
|
||||
use search::SearchBar;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
pub use workspace;
|
||||
use workspace::{AppState, Settings, Workspace, WorkspaceParams};
|
||||
|
@ -104,6 +105,17 @@ pub fn build_workspace(
|
|||
app_state: &Arc<AppState>,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Workspace {
|
||||
cx.subscribe(&cx.handle(), |_, _, event, cx| {
|
||||
let workspace::Event::PaneAdded(pane) = event;
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.toolbar().update(cx, |toolbar, cx| {
|
||||
let search_bar = cx.add_view(|cx| SearchBar::new(cx));
|
||||
toolbar.add_right_item(search_bar, cx);
|
||||
})
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
|
||||
let workspace_params = WorkspaceParams {
|
||||
project,
|
||||
client: app_state.client.clone(),
|
||||
|
|
Loading…
Reference in a new issue