project panel: select autofolded entries (#17520)
Closes #17252 Release Notes: - Intermediate auto-folded project entries can now be selected and acted upon (removed, renamed, cut, pasted).
This commit is contained in:
parent
174e125686
commit
66ef318823
4 changed files with 238 additions and 32 deletions
|
@ -60,11 +60,15 @@ pub struct ProjectPanel {
|
||||||
scroll_handle: UniformListScrollHandle,
|
scroll_handle: UniformListScrollHandle,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
visible_entries: Vec<(WorktreeId, Vec<Entry>, OnceCell<HashSet<Arc<Path>>>)>,
|
visible_entries: Vec<(WorktreeId, Vec<Entry>, OnceCell<HashSet<Arc<Path>>>)>,
|
||||||
|
/// Maps from leaf project entry ID to the currently selected ancestor.
|
||||||
|
/// Relevant only for auto-fold dirs, where a single project panel entry may actually consist of several
|
||||||
|
/// project entries (and all non-leaf nodes are guaranteed to be directories).
|
||||||
|
ancestors: HashMap<ProjectEntryId, FoldedAncestors>,
|
||||||
last_worktree_root_id: Option<ProjectEntryId>,
|
last_worktree_root_id: Option<ProjectEntryId>,
|
||||||
last_external_paths_drag_over_entry: Option<ProjectEntryId>,
|
last_external_paths_drag_over_entry: Option<ProjectEntryId>,
|
||||||
expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>,
|
expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>,
|
||||||
unfolded_dir_ids: HashSet<ProjectEntryId>,
|
unfolded_dir_ids: HashSet<ProjectEntryId>,
|
||||||
// Currently selected entry in a file tree
|
// Currently selected leaf entry (see auto-folding for a definition of that) in a file tree
|
||||||
selection: Option<SelectedEntry>,
|
selection: Option<SelectedEntry>,
|
||||||
marked_entries: BTreeSet<SelectedEntry>,
|
marked_entries: BTreeSet<SelectedEntry>,
|
||||||
context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>,
|
context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>,
|
||||||
|
@ -96,7 +100,7 @@ enum ClipboardEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct EntryDetails {
|
struct EntryDetails {
|
||||||
filename: String,
|
filename: String,
|
||||||
icon: Option<SharedString>,
|
icon: Option<SharedString>,
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
|
@ -111,18 +115,19 @@ pub struct EntryDetails {
|
||||||
is_cut: bool,
|
is_cut: bool,
|
||||||
git_status: Option<GitFileStatus>,
|
git_status: Option<GitFileStatus>,
|
||||||
is_private: bool,
|
is_private: bool,
|
||||||
|
is_auto_folded: bool,
|
||||||
worktree_id: WorktreeId,
|
worktree_id: WorktreeId,
|
||||||
canonical_path: Option<Box<Path>>,
|
canonical_path: Option<Box<Path>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Default, Debug, Deserialize)]
|
#[derive(PartialEq, Clone, Default, Debug, Deserialize)]
|
||||||
pub struct Delete {
|
struct Delete {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub skip_prompt: bool,
|
pub skip_prompt: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Default, Debug, Deserialize)]
|
#[derive(PartialEq, Clone, Default, Debug, Deserialize)]
|
||||||
pub struct Trash {
|
struct Trash {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub skip_prompt: bool,
|
pub skip_prompt: bool,
|
||||||
}
|
}
|
||||||
|
@ -155,6 +160,18 @@ actions!(
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct FoldedAncestors {
|
||||||
|
current_ancestor_depth: usize,
|
||||||
|
ancestors: Vec<ProjectEntryId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FoldedAncestors {
|
||||||
|
fn max_ancestor_depth(&self) -> usize {
|
||||||
|
self.ancestors.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init_settings(cx: &mut AppContext) {
|
pub fn init_settings(cx: &mut AppContext) {
|
||||||
ProjectPanelSettings::register(cx);
|
ProjectPanelSettings::register(cx);
|
||||||
}
|
}
|
||||||
|
@ -277,6 +294,7 @@ impl ProjectPanel {
|
||||||
scroll_handle: UniformListScrollHandle::new(),
|
scroll_handle: UniformListScrollHandle::new(),
|
||||||
focus_handle,
|
focus_handle,
|
||||||
visible_entries: Default::default(),
|
visible_entries: Default::default(),
|
||||||
|
ancestors: Default::default(),
|
||||||
last_worktree_root_id: Default::default(),
|
last_worktree_root_id: Default::default(),
|
||||||
last_external_paths_drag_over_entry: None,
|
last_external_paths_drag_over_entry: None,
|
||||||
expanded_dir_ids: Default::default(),
|
expanded_dir_ids: Default::default(),
|
||||||
|
@ -457,7 +475,7 @@ impl ProjectPanel {
|
||||||
entry_id,
|
entry_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
if let Some((worktree, entry)) = self.selected_sub_entry(cx) {
|
||||||
let auto_fold_dirs = ProjectPanelSettings::get_global(cx).auto_fold_dirs;
|
let auto_fold_dirs = ProjectPanelSettings::get_global(cx).auto_fold_dirs;
|
||||||
let is_root = Some(entry) == worktree.root_entry();
|
let is_root = Some(entry) == worktree.root_entry();
|
||||||
let is_dir = entry.is_dir();
|
let is_dir = entry.is_dir();
|
||||||
|
@ -583,6 +601,13 @@ impl ProjectPanel {
|
||||||
|
|
||||||
fn expand_selected_entry(&mut self, _: &ExpandSelectedEntry, cx: &mut ViewContext<Self>) {
|
fn expand_selected_entry(&mut self, _: &ExpandSelectedEntry, cx: &mut ViewContext<Self>) {
|
||||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||||
|
if let Some(folded_ancestors) = self.ancestors.get_mut(&entry.id) {
|
||||||
|
if folded_ancestors.current_ancestor_depth > 0 {
|
||||||
|
folded_ancestors.current_ancestor_depth -= 1;
|
||||||
|
cx.notify();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if entry.is_dir() {
|
if entry.is_dir() {
|
||||||
let worktree_id = worktree.id();
|
let worktree_id = worktree.id();
|
||||||
let entry_id = entry.id;
|
let entry_id = entry.id;
|
||||||
|
@ -611,6 +636,13 @@ impl ProjectPanel {
|
||||||
|
|
||||||
fn collapse_selected_entry(&mut self, _: &CollapseSelectedEntry, cx: &mut ViewContext<Self>) {
|
fn collapse_selected_entry(&mut self, _: &CollapseSelectedEntry, cx: &mut ViewContext<Self>) {
|
||||||
if let Some((worktree, mut entry)) = self.selected_entry(cx) {
|
if let Some((worktree, mut entry)) = self.selected_entry(cx) {
|
||||||
|
if let Some(folded_ancestors) = self.ancestors.get_mut(&entry.id) {
|
||||||
|
if folded_ancestors.current_ancestor_depth < folded_ancestors.max_ancestor_depth() {
|
||||||
|
folded_ancestors.current_ancestor_depth += 1;
|
||||||
|
cx.notify();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
let worktree_id = worktree.id();
|
let worktree_id = worktree.id();
|
||||||
let expanded_dir_ids =
|
let expanded_dir_ids =
|
||||||
if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) {
|
if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) {
|
||||||
|
@ -943,6 +975,17 @@ impl ProjectPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unflatten_entry_id(&self, leaf_entry_id: ProjectEntryId) -> ProjectEntryId {
|
||||||
|
if let Some(ancestors) = self.ancestors.get(&leaf_entry_id) {
|
||||||
|
ancestors
|
||||||
|
.ancestors
|
||||||
|
.get(ancestors.current_ancestor_depth)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(leaf_entry_id)
|
||||||
|
} else {
|
||||||
|
leaf_entry_id
|
||||||
|
}
|
||||||
|
}
|
||||||
fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) {
|
fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(SelectedEntry {
|
if let Some(SelectedEntry {
|
||||||
worktree_id,
|
worktree_id,
|
||||||
|
@ -950,6 +993,7 @@ impl ProjectPanel {
|
||||||
}) = self.selection
|
}) = self.selection
|
||||||
{
|
{
|
||||||
if let Some(worktree) = self.project.read(cx).worktree_for_id(worktree_id, cx) {
|
if let Some(worktree) = self.project.read(cx).worktree_for_id(worktree_id, cx) {
|
||||||
|
let entry_id = self.unflatten_entry_id(entry_id);
|
||||||
if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) {
|
if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) {
|
||||||
self.edit_state = Some(EditState {
|
self.edit_state = Some(EditState {
|
||||||
worktree_id,
|
worktree_id,
|
||||||
|
@ -1161,7 +1205,7 @@ impl ProjectPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_parent(&mut self, _: &SelectParent, cx: &mut ViewContext<Self>) {
|
fn select_parent(&mut self, _: &SelectParent, cx: &mut ViewContext<Self>) {
|
||||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
if let Some((worktree, entry)) = self.selected_sub_entry(cx) {
|
||||||
if let Some(parent) = entry.path.parent() {
|
if let Some(parent) = entry.path.parent() {
|
||||||
if let Some(parent_entry) = worktree.entry_for_path(parent) {
|
if let Some(parent_entry) = worktree.entry_for_path(parent) {
|
||||||
self.selection = Some(SelectedEntry {
|
self.selection = Some(SelectedEntry {
|
||||||
|
@ -1447,13 +1491,13 @@ impl ProjectPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
|
fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
|
||||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
if let Some((worktree, entry)) = self.selected_sub_entry(cx) {
|
||||||
cx.reveal_path(&worktree.abs_path().join(&entry.path));
|
cx.reveal_path(&worktree.abs_path().join(&entry.path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
|
fn open_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
|
||||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
if let Some((worktree, entry)) = self.selected_sub_entry(cx) {
|
||||||
let abs_path = worktree.abs_path().join(&entry.path);
|
let abs_path = worktree.abs_path().join(&entry.path);
|
||||||
let working_directory = if entry.is_dir() {
|
let working_directory = if entry.is_dir() {
|
||||||
Some(abs_path)
|
Some(abs_path)
|
||||||
|
@ -1476,7 +1520,7 @@ impl ProjectPanel {
|
||||||
_: &NewSearchInDirectory,
|
_: &NewSearchInDirectory,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
if let Some((worktree, entry)) = self.selected_sub_entry(cx) {
|
||||||
if entry.is_dir() {
|
if entry.is_dir() {
|
||||||
let include_root = self.project.read(cx).visible_worktrees(cx).count() > 1;
|
let include_root = self.project.read(cx).visible_worktrees(cx).count() > 1;
|
||||||
let dir_path = if include_root {
|
let dir_path = if include_root {
|
||||||
|
@ -1596,15 +1640,36 @@ impl ProjectPanel {
|
||||||
// Returns list of entries that should be affected by an operation.
|
// Returns list of entries that should be affected by an operation.
|
||||||
// When currently selected entry is not marked, it's treated as the only marked entry.
|
// When currently selected entry is not marked, it's treated as the only marked entry.
|
||||||
fn marked_entries(&self) -> BTreeSet<SelectedEntry> {
|
fn marked_entries(&self) -> BTreeSet<SelectedEntry> {
|
||||||
let Some(selection) = self.selection else {
|
let Some(mut selection) = self.selection else {
|
||||||
return Default::default();
|
return Default::default();
|
||||||
};
|
};
|
||||||
if self.marked_entries.contains(&selection) {
|
if self.marked_entries.contains(&selection) {
|
||||||
self.marked_entries.clone()
|
self.marked_entries
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.map(|mut entry| {
|
||||||
|
entry.entry_id = self.resolve_entry(entry.entry_id);
|
||||||
|
entry
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
|
selection.entry_id = self.resolve_entry(selection.entry_id);
|
||||||
BTreeSet::from_iter([selection])
|
BTreeSet::from_iter([selection])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_entry(&self, id: ProjectEntryId) -> ProjectEntryId {
|
||||||
|
self.ancestors
|
||||||
|
.get(&id)
|
||||||
|
.and_then(|ancestors| {
|
||||||
|
if ancestors.current_ancestor_depth == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
ancestors.ancestors.get(ancestors.current_ancestor_depth)
|
||||||
|
})
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(id)
|
||||||
|
}
|
||||||
pub fn selected_entry<'a>(
|
pub fn selected_entry<'a>(
|
||||||
&self,
|
&self,
|
||||||
cx: &'a AppContext,
|
cx: &'a AppContext,
|
||||||
|
@ -1613,6 +1678,21 @@ impl ProjectPanel {
|
||||||
Some((worktree.read(cx), entry))
|
Some((worktree.read(cx), entry))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compared to selected_entry, this function resolves to the currently
|
||||||
|
/// selected subentry if dir auto-folding is enabled.
|
||||||
|
fn selected_sub_entry<'a>(
|
||||||
|
&self,
|
||||||
|
cx: &'a AppContext,
|
||||||
|
) -> Option<(&'a Worktree, &'a project::Entry)> {
|
||||||
|
let (worktree, mut entry) = self.selected_entry_handle(cx)?;
|
||||||
|
|
||||||
|
let worktree = worktree.read(cx);
|
||||||
|
let resolved_id = self.resolve_entry(entry.id);
|
||||||
|
if resolved_id != entry.id {
|
||||||
|
entry = worktree.entry_for_id(resolved_id)?;
|
||||||
|
}
|
||||||
|
Some((worktree, entry))
|
||||||
|
}
|
||||||
fn selected_entry_handle<'a>(
|
fn selected_entry_handle<'a>(
|
||||||
&self,
|
&self,
|
||||||
cx: &'a AppContext,
|
cx: &'a AppContext,
|
||||||
|
@ -1655,6 +1735,7 @@ impl ProjectPanel {
|
||||||
.and_then(|worktree| worktree.read(cx).root_entry())
|
.and_then(|worktree| worktree.read(cx).root_entry())
|
||||||
.map(|entry| entry.id);
|
.map(|entry| entry.id);
|
||||||
|
|
||||||
|
let old_ancestors = std::mem::take(&mut self.ancestors);
|
||||||
self.visible_entries.clear();
|
self.visible_entries.clear();
|
||||||
for worktree in project.visible_worktrees(cx) {
|
for worktree in project.visible_worktrees(cx) {
|
||||||
let snapshot = worktree.read(cx).snapshot();
|
let snapshot = worktree.read(cx).snapshot();
|
||||||
|
@ -1688,11 +1769,11 @@ impl ProjectPanel {
|
||||||
|
|
||||||
let mut visible_worktree_entries = Vec::new();
|
let mut visible_worktree_entries = Vec::new();
|
||||||
let mut entry_iter = snapshot.entries(true, 0);
|
let mut entry_iter = snapshot.entries(true, 0);
|
||||||
|
let mut auto_folded_ancestors = vec![];
|
||||||
while let Some(entry) = entry_iter.entry() {
|
while let Some(entry) = entry_iter.entry() {
|
||||||
if auto_collapse_dirs
|
if auto_collapse_dirs && entry.kind.is_dir() {
|
||||||
&& entry.kind.is_dir()
|
auto_folded_ancestors.push(entry.id);
|
||||||
&& !self.unfolded_dir_ids.contains(&entry.id)
|
if !self.unfolded_dir_ids.contains(&entry.id) {
|
||||||
{
|
|
||||||
if let Some(root_path) = snapshot.root_entry() {
|
if let Some(root_path) = snapshot.root_entry() {
|
||||||
let mut child_entries = snapshot.child_entries(&entry.path);
|
let mut child_entries = snapshot.child_entries(&entry.path);
|
||||||
if let Some(child) = child_entries.next() {
|
if let Some(child) = child_entries.next() {
|
||||||
|
@ -1701,12 +1782,29 @@ impl ProjectPanel {
|
||||||
&& child.kind.is_dir()
|
&& child.kind.is_dir()
|
||||||
{
|
{
|
||||||
entry_iter.advance();
|
entry_iter.advance();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let depth = old_ancestors
|
||||||
|
.get(&entry.id)
|
||||||
|
.map(|ancestor| ancestor.current_ancestor_depth)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let mut ancestors = std::mem::take(&mut auto_folded_ancestors);
|
||||||
|
if ancestors.len() > 1 {
|
||||||
|
ancestors.reverse();
|
||||||
|
self.ancestors.insert(
|
||||||
|
entry.id,
|
||||||
|
FoldedAncestors {
|
||||||
|
current_ancestor_depth: depth,
|
||||||
|
ancestors,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto_folded_ancestors.clear();
|
||||||
visible_worktree_entries.push(entry.clone());
|
visible_worktree_entries.push(entry.clone());
|
||||||
if Some(entry.id) == new_entry_parent_id {
|
if Some(entry.id) == new_entry_parent_id {
|
||||||
visible_worktree_entries.push(Entry {
|
visible_worktree_entries.push(Entry {
|
||||||
|
@ -1999,6 +2097,7 @@ impl ProjectPanel {
|
||||||
.map_or(false, |e| e.is_cut() && e.items().contains(&selection)),
|
.map_or(false, |e| e.is_cut() && e.items().contains(&selection)),
|
||||||
git_status: status,
|
git_status: status,
|
||||||
is_private: entry.is_private,
|
is_private: entry.is_private,
|
||||||
|
is_auto_folded: difference > 1,
|
||||||
worktree_id: *worktree_id,
|
worktree_id: *worktree_id,
|
||||||
canonical_path: entry.canonical_path.clone(),
|
canonical_path: entry.canonical_path.clone(),
|
||||||
};
|
};
|
||||||
|
@ -2008,6 +2107,15 @@ impl ProjectPanel {
|
||||||
entry.id == NEW_ENTRY_ID
|
entry.id == NEW_ENTRY_ID
|
||||||
} else {
|
} else {
|
||||||
entry.id == edit_state.entry_id
|
entry.id == edit_state.entry_id
|
||||||
|
|| self
|
||||||
|
.ancestors
|
||||||
|
.get(&entry.id)
|
||||||
|
.is_some_and(|auto_folded_dirs| {
|
||||||
|
auto_folded_dirs
|
||||||
|
.ancestors
|
||||||
|
.iter()
|
||||||
|
.any(|entry_id| *entry_id == edit_state.entry_id)
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_edited_entry {
|
if is_edited_entry {
|
||||||
|
@ -2102,6 +2210,7 @@ impl ProjectPanel {
|
||||||
active_selection: selection,
|
active_selection: selection,
|
||||||
marked_selections: selections,
|
marked_selections: selections,
|
||||||
};
|
};
|
||||||
|
let is_auto_folded = details.is_auto_folded;
|
||||||
div()
|
div()
|
||||||
.id(entry_id.to_proto() as usize)
|
.id(entry_id.to_proto() as usize)
|
||||||
.on_drag_move::<ExternalPaths>(cx.listener(
|
.on_drag_move::<ExternalPaths>(cx.listener(
|
||||||
|
@ -2202,12 +2311,79 @@ impl ProjectPanel {
|
||||||
if let (Some(editor), true) = (Some(&self.filename_editor), show_editor) {
|
if let (Some(editor), true) = (Some(&self.filename_editor), show_editor) {
|
||||||
h_flex().h_6().w_full().child(editor.clone())
|
h_flex().h_6().w_full().child(editor.clone())
|
||||||
} else {
|
} else {
|
||||||
h_flex().h_6().child(
|
h_flex().h_6().map(|this| {
|
||||||
|
if is_auto_folded && is_active {
|
||||||
|
let folded_ancestors = self.ancestors.get(&entry_id).unwrap();
|
||||||
|
let Some(part_to_highlight) = Path::new(&file_name)
|
||||||
|
.ancestors()
|
||||||
|
.nth(folded_ancestors.current_ancestor_depth)
|
||||||
|
else {
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
let suffix = Path::new(&file_name)
|
||||||
|
.strip_prefix(part_to_highlight)
|
||||||
|
.ok()
|
||||||
|
.filter(|suffix| !suffix.as_os_str().is_empty());
|
||||||
|
let prefix = part_to_highlight
|
||||||
|
.parent()
|
||||||
|
.filter(|prefix| !prefix.as_os_str().is_empty());
|
||||||
|
let Some(part_to_highlight) = part_to_highlight
|
||||||
|
.file_name()
|
||||||
|
.and_then(|name| name.to_str().map(String::from))
|
||||||
|
else {
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.children(prefix.and_then(|prefix| {
|
||||||
|
Some(
|
||||||
|
h_flex()
|
||||||
|
.child(
|
||||||
|
Label::new(prefix.to_str().map(String::from)?)
|
||||||
|
.single_line()
|
||||||
|
.color(filename_text_color),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Label::new(std::path::MAIN_SEPARATOR_STR)
|
||||||
|
.single_line()
|
||||||
|
.color(filename_text_color),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
Label::new(part_to_highlight)
|
||||||
|
.single_line()
|
||||||
|
.color(filename_text_color)
|
||||||
|
.underline(true),
|
||||||
|
)
|
||||||
|
.children(
|
||||||
|
suffix.and_then(|suffix| {
|
||||||
|
Some(
|
||||||
|
h_flex()
|
||||||
|
.child(
|
||||||
|
Label::new(std::path::MAIN_SEPARATOR_STR)
|
||||||
|
.single_line()
|
||||||
|
.color(filename_text_color),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Label::new(
|
||||||
|
suffix.to_str().map(String::from)?,
|
||||||
|
)
|
||||||
|
.single_line()
|
||||||
|
.color(filename_text_color),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this.child(
|
||||||
Label::new(file_name)
|
Label::new(file_name)
|
||||||
.single_line()
|
.single_line()
|
||||||
.color(filename_text_color),
|
.color(filename_text_color),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
.ml_1(),
|
.ml_1(),
|
||||||
)
|
)
|
||||||
.on_click(cx.listener(move |this, event: &gpui::ClickEvent, cx| {
|
.on_click(cx.listener(move |this, event: &gpui::ClickEvent, cx| {
|
||||||
|
@ -2551,7 +2727,7 @@ impl Render for ProjectPanel {
|
||||||
.child(
|
.child(
|
||||||
uniform_list(cx.view().clone(), "entries", items_count, {
|
uniform_list(cx.view().clone(), "entries", items_count, {
|
||||||
|this, range, cx| {
|
|this, range, cx| {
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::with_capacity(range.end - range.start);
|
||||||
this.for_each_visible_entry(range, cx, |id, details, cx| {
|
this.for_each_visible_entry(range, cx, |id, details, cx| {
|
||||||
items.push(this.render_entry(id, details, cx));
|
items.push(this.render_entry(id, details, cx));
|
||||||
});
|
});
|
||||||
|
|
|
@ -58,6 +58,11 @@ impl LabelCommon for HighlightedLabel {
|
||||||
self.base = self.base.alpha(alpha);
|
self.base = self.base.alpha(alpha);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn underline(mut self, underline: bool) -> Self {
|
||||||
|
self.base = self.base.underline(underline);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn highlight_ranges(
|
pub fn highlight_ranges(
|
||||||
|
|
|
@ -170,6 +170,11 @@ impl LabelCommon for Label {
|
||||||
self.base = self.base.alpha(alpha);
|
self.base = self.base.alpha(alpha);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn underline(mut self, underline: bool) -> Self {
|
||||||
|
self.base = self.base.underline(underline);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderOnce for Label {
|
impl RenderOnce for Label {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use gpui::{relative, AnyElement, FontWeight, StyleRefinement, Styled};
|
use gpui::{relative, AnyElement, FontWeight, StyleRefinement, Styled, UnderlineStyle};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
|
@ -42,6 +42,9 @@ pub trait LabelCommon {
|
||||||
/// Sets the italic property of the label.
|
/// Sets the italic property of the label.
|
||||||
fn italic(self, italic: bool) -> Self;
|
fn italic(self, italic: bool) -> Self;
|
||||||
|
|
||||||
|
/// Sets the underline property of the label
|
||||||
|
fn underline(self, underline: bool) -> Self;
|
||||||
|
|
||||||
/// Sets the alpha property of the label, overwriting the alpha value of the color.
|
/// Sets the alpha property of the label, overwriting the alpha value of the color.
|
||||||
fn alpha(self, alpha: f32) -> Self;
|
fn alpha(self, alpha: f32) -> Self;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +60,7 @@ pub struct LabelLike {
|
||||||
italic: bool,
|
italic: bool,
|
||||||
children: SmallVec<[AnyElement; 2]>,
|
children: SmallVec<[AnyElement; 2]>,
|
||||||
alpha: Option<f32>,
|
alpha: Option<f32>,
|
||||||
|
underline: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LabelLike {
|
impl Default for LabelLike {
|
||||||
|
@ -77,6 +81,7 @@ impl LabelLike {
|
||||||
italic: false,
|
italic: false,
|
||||||
children: SmallVec::new(),
|
children: SmallVec::new(),
|
||||||
alpha: None,
|
alpha: None,
|
||||||
|
underline: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +128,11 @@ impl LabelCommon for LabelLike {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn underline(mut self, underline: bool) -> Self {
|
||||||
|
self.underline = underline;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn alpha(mut self, alpha: f32) -> Self {
|
fn alpha(mut self, alpha: f32) -> Self {
|
||||||
self.alpha = Some(alpha);
|
self.alpha = Some(alpha);
|
||||||
self
|
self
|
||||||
|
@ -165,6 +175,16 @@ impl RenderOnce for LabelLike {
|
||||||
this.line_height(relative(1.))
|
this.line_height(relative(1.))
|
||||||
})
|
})
|
||||||
.when(self.italic, |this| this.italic())
|
.when(self.italic, |this| this.italic())
|
||||||
|
.when(self.underline, |mut this| {
|
||||||
|
this.text_style()
|
||||||
|
.get_or_insert_with(Default::default)
|
||||||
|
.underline = Some(UnderlineStyle {
|
||||||
|
thickness: px(1.),
|
||||||
|
color: None,
|
||||||
|
wavy: false,
|
||||||
|
});
|
||||||
|
this
|
||||||
|
})
|
||||||
.text_color(color)
|
.text_color(color)
|
||||||
.font_weight(self.weight.unwrap_or(settings.ui_font.weight))
|
.font_weight(self.weight.unwrap_or(settings.ui_font.weight))
|
||||||
.children(self.children)
|
.children(self.children)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue