mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-28 17:58:42 +00:00
Implement cut/paste for ProjectPanel
This commit is contained in:
parent
6c145b2abc
commit
37a0c7f046
3 changed files with 120 additions and 6 deletions
|
@ -22,7 +22,7 @@ use std::{
|
|||
collections::{hash_map, HashMap},
|
||||
ffi::OsStr,
|
||||
ops::Range,
|
||||
path::PathBuf,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use unicase::UniCase;
|
||||
use workspace::Workspace;
|
||||
|
@ -37,6 +37,7 @@ pub struct ProjectPanel {
|
|||
selection: Option<Selection>,
|
||||
edit_state: Option<EditState>,
|
||||
filename_editor: ViewHandle<Editor>,
|
||||
clipboard_entry: Option<ClipboardEntry>,
|
||||
context_menu: ViewHandle<ContextMenu>,
|
||||
}
|
||||
|
||||
|
@ -55,6 +56,18 @@ struct EditState {
|
|||
processing_filename: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ClipboardEntry {
|
||||
Copied {
|
||||
worktree_id: WorktreeId,
|
||||
entry_id: ProjectEntryId,
|
||||
},
|
||||
Cut {
|
||||
worktree_id: WorktreeId,
|
||||
entry_id: ProjectEntryId,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct EntryDetails {
|
||||
filename: String,
|
||||
|
@ -65,6 +78,7 @@ struct EntryDetails {
|
|||
is_selected: bool,
|
||||
is_editing: bool,
|
||||
is_processing: bool,
|
||||
is_cut: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -116,7 +130,11 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
cx.add_action(ProjectPanel::copy);
|
||||
cx.add_action(ProjectPanel::copy_path);
|
||||
cx.add_action(ProjectPanel::cut);
|
||||
cx.add_action(ProjectPanel::paste);
|
||||
cx.add_action(
|
||||
|this: &mut ProjectPanel, action: &Paste, cx: &mut ViewContext<ProjectPanel>| {
|
||||
this.paste(action, cx);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
|
@ -172,6 +190,7 @@ impl ProjectPanel {
|
|||
selection: None,
|
||||
edit_state: None,
|
||||
filename_editor,
|
||||
clipboard_entry: None,
|
||||
context_menu: cx.add_view(|cx| ContextMenu::new(cx)),
|
||||
};
|
||||
this.update_visible_entries(None, cx);
|
||||
|
@ -239,6 +258,11 @@ impl ProjectPanel {
|
|||
menu_entries.push(ContextMenuItem::item("Copy", Copy));
|
||||
menu_entries.push(ContextMenuItem::item("Copy Path", CopyPath));
|
||||
menu_entries.push(ContextMenuItem::item("Cut", Cut));
|
||||
if let Some(clipboard_entry) = self.clipboard_entry {
|
||||
if clipboard_entry.worktree_id() == worktree.id() {
|
||||
menu_entries.push(ContextMenuItem::item("Paste", Paste));
|
||||
}
|
||||
}
|
||||
menu_entries.push(ContextMenuItem::Separator);
|
||||
menu_entries.push(ContextMenuItem::item("Rename", Rename));
|
||||
if !is_root {
|
||||
|
@ -608,15 +632,75 @@ impl ProjectPanel {
|
|||
}
|
||||
|
||||
fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
|
||||
todo!()
|
||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||
self.clipboard_entry = Some(ClipboardEntry::Cut {
|
||||
worktree_id: worktree.id(),
|
||||
entry_id: entry.id,
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
|
||||
todo!()
|
||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||
self.clipboard_entry = Some(ClipboardEntry::Copied {
|
||||
worktree_id: worktree.id(),
|
||||
entry_id: entry.id,
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
|
||||
todo!()
|
||||
fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) -> Option<()> {
|
||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||
let clipboard_entry = self.clipboard_entry?;
|
||||
if clipboard_entry.worktree_id() != worktree.id() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let clipboard_entry_file_name = self
|
||||
.project
|
||||
.read(cx)
|
||||
.path_for_entry(clipboard_entry.entry_id(), cx)?
|
||||
.path
|
||||
.file_name()?
|
||||
.to_os_string();
|
||||
|
||||
let mut new_path = entry.path.to_path_buf();
|
||||
if entry.is_file() {
|
||||
new_path.pop();
|
||||
}
|
||||
|
||||
new_path.push(&clipboard_entry_file_name);
|
||||
let extension = new_path.extension().map(|e| e.to_os_string());
|
||||
let file_name_without_extension = Path::new(&clipboard_entry_file_name).file_stem()?;
|
||||
let mut ix = 0;
|
||||
while worktree.entry_for_path(&new_path).is_some() {
|
||||
new_path.pop();
|
||||
|
||||
let mut new_file_name = file_name_without_extension.to_os_string();
|
||||
new_file_name.push(" copy");
|
||||
if ix > 0 {
|
||||
new_file_name.push(format!(" {}", ix));
|
||||
}
|
||||
new_path.push(new_file_name);
|
||||
if let Some(extension) = extension.as_ref() {
|
||||
new_path.set_extension(&extension);
|
||||
}
|
||||
ix += 1;
|
||||
}
|
||||
|
||||
self.clipboard_entry.take();
|
||||
if clipboard_entry.is_cut() {
|
||||
self.project
|
||||
.update(cx, |project, cx| {
|
||||
project.rename_entry(clipboard_entry.entry_id(), new_path, cx)
|
||||
})
|
||||
.map(|task| task.detach_and_log_err(cx));
|
||||
} else {
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
|
||||
|
@ -834,6 +918,9 @@ impl ProjectPanel {
|
|||
}),
|
||||
is_editing: false,
|
||||
is_processing: false,
|
||||
is_cut: self
|
||||
.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 {
|
||||
|
@ -878,6 +965,10 @@ impl ProjectPanel {
|
|||
style.text.color.fade_out(theme.ignored_entry_fade);
|
||||
style.icon_color.fade_out(theme.ignored_entry_fade);
|
||||
}
|
||||
if details.is_cut {
|
||||
style.text.color.fade_out(theme.cut_entry_fade);
|
||||
style.icon_color.fade_out(theme.cut_entry_fade);
|
||||
}
|
||||
let row_container_style = if show_editor {
|
||||
theme.filename_editor.container
|
||||
} else {
|
||||
|
@ -1018,6 +1109,27 @@ impl workspace::sidebar::SidebarItem for ProjectPanel {
|
|||
}
|
||||
}
|
||||
|
||||
impl ClipboardEntry {
|
||||
fn is_cut(&self) -> bool {
|
||||
matches!(self, Self::Cut { .. })
|
||||
}
|
||||
|
||||
fn entry_id(&self) -> ProjectEntryId {
|
||||
match self {
|
||||
ClipboardEntry::Copied { entry_id, .. } | ClipboardEntry::Cut { entry_id, .. } => {
|
||||
*entry_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn worktree_id(&self) -> WorktreeId {
|
||||
match self {
|
||||
ClipboardEntry::Copied { worktree_id, .. }
|
||||
| ClipboardEntry::Cut { worktree_id, .. } => *worktree_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -224,6 +224,7 @@ pub struct ProjectPanel {
|
|||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub entry: Interactive<ProjectPanelEntry>,
|
||||
pub cut_entry_fade: f32,
|
||||
pub ignored_entry_fade: f32,
|
||||
pub filename_editor: FieldEditor,
|
||||
pub indent_width: f32,
|
||||
|
|
|
@ -26,6 +26,7 @@ export default function projectPanel(theme: Theme) {
|
|||
text: text(theme, "mono", "active", { size: "sm" }),
|
||||
}
|
||||
},
|
||||
cutEntryFade: 0.4,
|
||||
ignoredEntryFade: 0.6,
|
||||
filenameEditor: {
|
||||
background: backgroundColor(theme, 500, "active"),
|
||||
|
|
Loading…
Reference in a new issue