Fileman copy/paste support (#970)

* Add copy/paste UI instead of file save
This commit is contained in:
Kyle Reed
2023-05-10 09:51:09 -07:00
committed by GitHub
parent 9a22a760ad
commit 8cae998146
9 changed files with 449 additions and 289 deletions

View File

@@ -192,8 +192,8 @@ const fileman_entry& FileManBaseView::get_selected_entry() const {
FileManBaseView::FileManBaseView(
NavigationView& nav,
std::string filter
) : nav_ (nav),
extension_filter { filter }
) : nav_{ nav },
extension_filter{ filter }
{
add_children({
&labels,
@@ -206,7 +206,7 @@ FileManBaseView::FileManBaseView(
};
if (!sdcIsCardInserted(&SDCD1)) {
empty_root = true;
empty_ = EmptyReason::NoSDC;
text_current.set("NO SD CARD!");
return;
}
@@ -214,7 +214,7 @@ FileManBaseView::FileManBaseView(
load_directory_contents(current_path);
if (!entry_list.size()) {
empty_root = true;
empty_ = EmptyReason::NoFiles;
text_current.set("EMPTY SD CARD!");
} else {
menu_view.on_left = [this]() {
@@ -224,7 +224,7 @@ FileManBaseView::FileManBaseView(
}
void FileManBaseView::focus() {
if (empty_root) {
if (empty_ != EmptyReason::NotEmpty) {
button_exit.focus();
} else {
menu_view.focus();
@@ -310,34 +310,8 @@ const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
return file_types[index];
}
/*void FileSaveView::on_save_name() {
text_prompt(nav_, &filename_buffer, 8, [this](std::string * buffer) {
nav_.pop();
});
}
FileSaveView::FileSaveView(
NavigationView& nav
) : FileManBaseView(nav)
{
name_buffer.clear();
add_children({
&text_save,
&button_save_name,
&live_timestamp
});
button_save_name.on_select = [this, &nav](Button&) {
on_save_name();
};
}*/
/* FileLoadView **************************************************************/
void FileLoadView::refresh_widgets(const bool) {
set_dirty();
}
FileLoadView::FileLoadView(
NavigationView& nav,
std::string filter
@@ -367,6 +341,68 @@ FileLoadView::FileLoadView(
};
}
void FileLoadView::refresh_widgets(const bool) {
set_dirty();
}
/* FileSaveView **************************************************************/
/*
FileSaveView::FileSaveView(
NavigationView& nav,
const fs::path& path,
const fs::path& file
) : nav_{ nav },
path_{ path },
file_{ file }
{
add_children({
&labels,
&text_path,
&button_edit_path,
&text_name,
&button_edit_name,
&button_save,
&button_cancel,
});
button_edit_path.on_select = [this](Button&) {
buffer_ = path_.string();
text_prompt(nav_, buffer_, max_filename_length,
[this](std::string&) {
path_ = buffer_;
refresh_widgets();
});
};
button_edit_name.on_select = [this](Button&) {
buffer_ = file_.string();
text_prompt(nav_, buffer_, max_filename_length,
[this](std::string&) {
file_ = buffer_;
refresh_widgets();
});
};
button_save.on_select = [this](Button&) {
if (on_save)
on_save(path_ / file_);
else
nav_.pop();
};
button_cancel.on_select = [this](Button&) {
nav_.pop();
};
refresh_widgets();
}
void FileSaveView::refresh_widgets() {
text_path.set(truncate(path_, 30));
text_name.set(truncate(file_, 30));
set_dirty();
}
*/
/* FileManagerView ***********************************************************/
void FileManagerView::on_rename() {
@@ -429,6 +465,30 @@ void FileManagerView::on_new_dir() {
});
}
void FileManagerView::on_paste() {
auto stem = copy_path.stem();
auto ext = copy_path.extension();
auto serial = 1;
fs::path new_path = copy_path.filename();
// Create a unique name.
while (fs::file_exists(current_path / new_path)) {
new_path = stem;
new_path += fs::path{ u"_" };
new_path += to_string_dec_int(serial++);
new_path += ext;
}
// TODO: handle partner file. Need to fix nav stack first.
auto result = copy_file(copy_path, current_path / new_path);
if (result.code() != FR_OK)
nav_.display_modal("Paste Failed", result.what());
copy_path = fs::path{ };
menu_view.focus();
reload_current();
}
bool FileManagerView::selected_is_valid() const {
return !entry_list.empty() &&
get_selected_entry().path != parent_dir_path;
@@ -438,62 +498,79 @@ void FileManagerView::refresh_widgets(const bool v) {
button_rename.hidden(v);
button_delete.hidden(v);
button_new_dir.hidden(v);
button_copy.hidden(v);
button_paste.hidden(v);
set_dirty();
}
FileManagerView::~FileManagerView() {
}
FileManagerView::FileManagerView(
NavigationView& nav
) : FileManBaseView(nav, "")
{
if (!empty_root) {
on_refresh_widgets = [this](bool v) {
refresh_widgets(v);
};
add_children({
&menu_view,
&labels,
&text_date,
&button_rename,
&button_delete,
&button_new_dir,
});
menu_view.on_highlight = [this]() {
// TODO: enable/disable buttons.
if (selected_is_valid())
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
else
text_date.set("");
};
refresh_list();
// Don't bother with the UI in the case of no SDC.
if (empty_ == EmptyReason::NoSDC)
return;
on_refresh_widgets = [this](bool v) {
refresh_widgets(v);
};
on_select_entry = [this](KeyEvent key) {
if (key == KeyEvent::Select && get_selected_entry().is_directory) {
push_dir(get_selected_entry().path);
} else {
button_rename.focus();
}
};
button_rename.on_select = [this](Button&) {
if (selected_is_valid())
on_rename();
};
add_children({
&menu_view,
&labels,
&text_date,
&button_rename,
&button_delete,
&button_new_dir,
&button_copy,
&button_paste
});
menu_view.on_highlight = [this]() {
// TODO: enable/disable buttons.
if (selected_is_valid())
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
else
text_date.set("");
};
refresh_list();
button_delete.on_select = [this](Button&) {
if (selected_is_valid())
on_delete();
};
on_select_entry = [this](KeyEvent key) {
if (key == KeyEvent::Select && get_selected_entry().is_directory) {
push_dir(get_selected_entry().path);
} else {
button_rename.focus();
}
};
button_rename.on_select = [this](Button&) {
if (selected_is_valid())
on_rename();
};
button_new_dir.on_select = [this](Button&) {
on_new_dir();
};
}
button_delete.on_select = [this](Button&) {
if (selected_is_valid())
on_delete();
};
button_new_dir.on_select = [this](Button&) {
on_new_dir();
};
button_copy.on_select = [this](Button&) {
if (selected_is_valid() && !get_selected_entry().is_directory)
copy_path = get_selected_full_path();
else
nav_.display_modal("Copy", "Can't copy that.");
};
button_paste.on_select = [this](Button&) {
if (!copy_path.empty())
on_paste();
else
nav_.display_modal("Paste", "Copy a file first.");
};
}
}

View File

@@ -36,6 +36,12 @@ struct fileman_entry {
bool is_directory { };
};
enum class EmptyReason : uint8_t {
NotEmpty,
NoFiles,
NoSDC
};
class FileManBaseView : public View {
public:
FileManBaseView(
@@ -43,11 +49,13 @@ public:
std::string filter
);
virtual ~FileManBaseView() { }
void focus() override;
std::string title() const override { return "Fileman"; };
protected:
static constexpr size_t max_filename_length = 50;
static constexpr size_t max_filename_length = 64;
struct file_assoc_t {
std::filesystem::path extension;
@@ -65,7 +73,6 @@ protected:
{ u"", &bitmap_icon_file, ui::Color::light_grey() } // NB: Must be last.
};
std::filesystem::path get_selected_full_path() const;
const fileman_entry& get_selected_entry() const;
@@ -78,7 +85,7 @@ protected:
NavigationView& nav_;
bool empty_root { false };
EmptyReason empty_ { EmptyReason::NotEmpty };
std::function<void(KeyEvent)> on_select_entry { nullptr };
std::function<void(bool)> on_refresh_widgets { nullptr };
@@ -104,57 +111,96 @@ protected:
};
Button button_exit {
{ 16 * 8, 34 * 8, 14 * 8, 32 },
{ 21 * 8, 34 * 8, 9 * 8, 32 },
"Exit"
};
};
/*class FileSaveView : public FileManBaseView {
public:
FileSaveView(NavigationView& nav);
~FileSaveView();
private:
std::string name_buffer { };
void on_save_name();
Text text_save {
{ 4 * 8, 15 * 8, 8 * 8, 16 },
"Save as:",
};
Button button_save_name {
{ 4 * 8, 18 * 8, 12 * 8, 32 },
"Name (set)"
};
LiveDateTime live_timestamp {
{ 17 * 8, 24 * 8, 11 * 8, 16 }
};
};*/
class FileLoadView : public FileManBaseView {
public:
std::function<void(std::filesystem::path)> on_changed { };
FileLoadView(NavigationView& nav, std::string filter);
virtual ~FileLoadView() { }
private:
void refresh_widgets(const bool v);
};
/*
// It would be nice to be able to launch FileLoadView
// but it will OOM if launched from within FileManager.
class FileSaveView : public View {
public:
FileSaveView(
NavigationView& nav,
const std::filesystem::path& path,
const std::filesystem::path& file);
std::function<void(std::filesystem::path)> on_save { };
private:
static constexpr size_t max_filename_length = 64;
void refresh_widgets();
NavigationView& nav_;
std::filesystem::path path_;
std::filesystem::path file_;
std::string buffer_ { };
Labels labels {
{ { 0 * 8, 1 * 16 }, "Path:", Color::light_grey() },
{ { 0 * 8, 6 * 16 }, "Filename:", Color::light_grey() },
};
Text text_path {
{ 0 * 8, 2 * 16, 30 * 8, 16 },
"",
};
Button button_edit_path {
{ 18 * 8, 3 * 16, 11 * 8, 32 },
"Edit Path"
};
Text text_name {
{ 0 * 8, 7 * 16, 30 * 8, 16 },
"",
};
Button button_edit_name {
{ 18 * 8, 8 * 16, 11 * 8, 32 },
"Edit Name"
};
Button button_save {
{ 10 * 8, 16 * 16, 9 * 8, 32 },
"Save"
};
Button button_cancel {
{ 20 * 8, 16 * 16, 9 * 8, 32 },
"Cancel"
};
};
*/
class FileManagerView : public FileManBaseView {
public:
FileManagerView(NavigationView& nav);
~FileManagerView();
virtual ~FileManagerView() { }
private:
// Passed by ref to other views needing lifetime extension.
std::string name_buffer { };
std::filesystem::path copy_path { };
void refresh_widgets(const bool v);
void on_rename();
void on_delete();
void on_new_dir();
void on_paste();
// True if the selected entry is a real file item.
bool selected_is_valid() const;
@@ -169,19 +215,29 @@ private:
};
Button button_rename {
{ 0 * 8, 29 * 8, 14 * 8, 32 },
{ 0 * 8, 29 * 8, 9 * 8, 32 },
"Rename"
};
Button button_delete {
{ 16 * 8, 29 * 8, 14 * 8, 32 },
{ 21 * 4, 29 * 8, 9 * 8, 32 },
"Delete"
};
Button button_new_dir {
{ 0 * 8, 34 * 8, 14 * 8, 32 },
{ 21 * 8, 29 * 8, 9 * 8, 32 },
"New Dir"
};
Button button_copy {
{ 0 * 8, 34 * 8, 9 * 8, 32 },
"Copy"
};
Button button_paste {
{ 21 * 4, 34 * 8, 9 * 8, 32 },
"Paste"
};
};
} /* namespace ui */