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
+134 -129
View File
@@ -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
});
+4 -5
View File
@@ -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")
+3 -3
View File
@@ -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;