Start dragging project panel entries

Co-Authored-By: Kay Simmons <kay@zed.dev>
This commit is contained in:
Julia 2022-11-07 17:00:01 -05:00
parent 1d6af4cf20
commit 847376a4f5
7 changed files with 142 additions and 71 deletions

View file

@ -1,12 +1,13 @@
use context_menu::{ContextMenu, ContextMenuItem};
use drag_and_drop::Draggable;
use editor::{Cancel, Editor};
use futures::stream::StreamExt;
use gpui::{
actions,
anyhow::{anyhow, Result},
elements::{
AnchorCorner, ChildView, ConstrainedBox, Empty, Flex, Label, MouseEventHandler,
ParentElement, ScrollTarget, Stack, Svg, UniformList, UniformListState,
AnchorCorner, ChildView, ConstrainedBox, ContainerStyle, Empty, Flex, Label,
MouseEventHandler, ParentElement, ScrollTarget, Stack, Svg, UniformList, UniformListState,
},
geometry::vector::Vector2F,
impl_internal_actions, keymap,
@ -25,6 +26,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use theme::ProjectPanelEntry;
use unicase::UniCase;
use workspace::Workspace;
@ -70,9 +72,10 @@ pub enum ClipboardEntry {
},
}
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, Clone)]
struct EntryDetails {
filename: String,
path: Arc<Path>,
depth: usize,
kind: EntryKind,
is_ignored: bool,
@ -220,6 +223,7 @@ impl ProjectPanel {
this.update_visible_entries(None, cx);
this
});
cx.subscribe(&project_panel, {
let project_panel = project_panel.downgrade();
move |workspace, _, event, cx| match event {
@ -950,14 +954,15 @@ impl ProjectPanel {
let end_ix = range.end.min(ix + visible_worktree_entries.len());
if let Some(worktree) = self.project.read(cx).worktree_for_id(*worktree_id, cx) {
let snapshot = worktree.read(cx).snapshot();
let root_name = OsStr::new(snapshot.root_name());
let expanded_entry_ids = self
.expanded_dir_ids
.get(&snapshot.id())
.map(Vec::as_slice)
.unwrap_or(&[]);
let root_name = OsStr::new(snapshot.root_name());
for entry in &visible_worktree_entries[range.start.saturating_sub(ix)..end_ix - ix]
{
let entry_range = range.start.saturating_sub(ix)..end_ix - ix;
for entry in &visible_worktree_entries[entry_range] {
let mut details = EntryDetails {
filename: entry
.path
@ -965,6 +970,7 @@ impl ProjectPanel {
.unwrap_or(root_name)
.to_string_lossy()
.to_string(),
path: entry.path.clone(),
depth: entry.path.components().count(),
kind: entry.kind,
is_ignored: entry.is_ignored,
@ -978,12 +984,14 @@ impl ProjectPanel {
.clipboard_entry
.map_or(false, |e| e.is_cut() && e.entry_id() == entry.id),
};
if let Some(edit_state) = &self.edit_state {
let is_edited_entry = if edit_state.is_new_entry {
entry.id == NEW_ENTRY_ID
} else {
entry.id == edit_state.entry_id
};
if is_edited_entry {
if let Some(processing_filename) = &edit_state.processing_filename {
details.is_processing = true;
@ -1005,6 +1013,63 @@ impl ProjectPanel {
}
}
fn render_entry_visual_element<V: View>(
details: EntryDetails,
editor: &ViewHandle<Editor>,
padding: f32,
row_container_style: ContainerStyle,
style: &ProjectPanelEntry,
cx: &mut RenderContext<V>,
) -> ElementBox {
let kind = details.kind;
let show_editor = details.is_editing && !details.is_processing;
Flex::row()
.with_child(
ConstrainedBox::new(if kind == EntryKind::Dir {
if details.is_expanded {
Svg::new("icons/chevron_down_8.svg")
.with_color(style.icon_color)
.boxed()
} else {
Svg::new("icons/chevron_right_8.svg")
.with_color(style.icon_color)
.boxed()
}
} else {
Empty::new().boxed()
})
.with_max_width(style.icon_size)
.with_max_height(style.icon_size)
.aligned()
.constrained()
.with_width(style.icon_size)
.boxed(),
)
.with_child(if show_editor {
ChildView::new(editor.clone(), cx)
.contained()
.with_margin_left(style.icon_spacing)
.aligned()
.left()
.flex(1.0, true)
.boxed()
} else {
Label::new(details.filename.clone(), style.text.clone())
.contained()
.with_margin_left(style.icon_spacing)
.aligned()
.left()
.boxed()
})
.constrained()
.with_height(style.height)
.contained()
.with_style(row_container_style)
.with_padding_left(padding)
.boxed()
}
fn render_entry(
entry_id: ProjectEntryId,
details: EntryDetails,
@ -1013,69 +1078,34 @@ impl ProjectPanel {
cx: &mut RenderContext<Self>,
) -> ElementBox {
let kind = details.kind;
let padding = theme.container.padding.left + details.depth as f32 * theme.indent_width;
let entry_style = if details.is_cut {
&theme.cut_entry
} else if details.is_ignored {
&theme.ignored_entry
} else {
&theme.entry
};
let show_editor = details.is_editing && !details.is_processing;
MouseEventHandler::<Self>::new(entry_id.to_usize(), cx, |state, cx| {
let padding = theme.container.padding.left + details.depth as f32 * theme.indent_width;
let entry_style = if details.is_cut {
&theme.cut_entry
} else if details.is_ignored {
&theme.ignored_entry
} else {
&theme.entry
};
let style = entry_style.style_for(state, details.is_selected).clone();
let row_container_style = if show_editor {
theme.filename_editor.container
} else {
style.container
};
Flex::row()
.with_child(
ConstrainedBox::new(if kind == EntryKind::Dir {
if details.is_expanded {
Svg::new("icons/chevron_down_8.svg")
.with_color(style.icon_color)
.boxed()
} else {
Svg::new("icons/chevron_right_8.svg")
.with_color(style.icon_color)
.boxed()
}
} else {
Empty::new().boxed()
})
.with_max_width(style.icon_size)
.with_max_height(style.icon_size)
.aligned()
.constrained()
.with_width(style.icon_size)
.boxed(),
)
.with_child(if show_editor {
ChildView::new(editor.clone(), cx)
.contained()
.with_margin_left(theme.entry.default.icon_spacing)
.aligned()
.left()
.flex(1.0, true)
.boxed()
} else {
Label::new(details.filename, style.text.clone())
.contained()
.with_margin_left(style.icon_spacing)
.aligned()
.left()
.boxed()
})
.constrained()
.with_height(theme.entry.default.height)
.contained()
.with_style(row_container_style)
.with_padding_left(padding)
.boxed()
Self::render_entry_visual_element(
details.clone(),
editor,
padding,
row_container_style,
&style,
cx,
)
})
.on_click(MouseButton::Left, move |e, cx| {
if kind == EntryKind::Dir {
@ -1093,6 +1123,22 @@ impl ProjectPanel {
position: e.position,
})
})
.as_draggable(details.clone(), {
let editor = editor.clone();
let row_container_style = theme.dragged_entry.container;
move |payload, cx: &mut RenderContext<Workspace>| {
let theme = cx.global::<Settings>().theme.clone();
Self::render_entry_visual_element(
payload.clone(),
&editor,
padding,
row_container_style,
&theme.project_panel.dragged_entry,
cx,
)
}
})
.with_cursor_style(CursorStyle::PointingHand)
.boxed()
}