Fileman icon toolbar (#975)

* Add cut/copy/paste menu with icons
* Add icons, file limit to fileman
This commit is contained in:
Kyle Reed 2023-05-11 13:46:38 -07:00 committed by GitHub
parent f9fdeb3419
commit a832bd433d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 306 additions and 63 deletions

View File

@ -22,7 +22,6 @@
/* TODO: /* TODO:
* - Paging menu items * - Paging menu items
* - Copy/Move
*/ */
#include <algorithm> #include <algorithm>
@ -146,6 +145,22 @@ bool partner_file_prompt(
return true; return true;
} }
fs::path get_unique_filename(const fs::path& path, const fs::path& file) {
auto stem = file.stem();
auto ext = file.extension();
auto serial = 1;
fs::path new_path = file;
while (fs::file_exists(path / new_path)) {
new_path = stem;
new_path += fs::path{ u"_" };
new_path += to_string_dec_int(serial++);
new_path += ext;
}
return new_path;
}
} }
namespace ui { namespace ui {
@ -198,6 +213,7 @@ FileManBaseView::FileManBaseView(
add_children({ add_children({
&labels, &labels,
&text_current, &text_current,
&text_info,
&button_exit &button_exit
}); });
@ -287,8 +303,13 @@ void FileManBaseView::refresh_list() {
} }
}); });
} }
// HACK: Should page menu items instead of limiting the number.
if (menu_view.item_count() >= max_items_shown)
break;
} }
text_info.set(menu_view.item_count() >= max_items_shown ? "Too many files!" : "");
menu_view.set_highlighted(prev_highlight); menu_view.set_highlighted(prev_highlight);
} }
@ -465,26 +486,30 @@ void FileManagerView::on_new_dir() {
}); });
} }
void FileManagerView::on_paste() { void FileManagerView::on_new_file() {
auto stem = copy_path.stem(); name_buffer = "";
auto ext = copy_path.extension(); text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) {
auto serial = 1; make_new_file(current_path / file_name);
fs::path new_path = copy_path.filename(); reload_current();
});
// 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;
} }
void FileManagerView::on_paste() {
// TODO: handle partner file. Need to fix nav stack first. // TODO: handle partner file. Need to fix nav stack first.
auto result = copy_file(copy_path, current_path / new_path); auto new_name = get_unique_filename(current_path, clipboard_path.filename());
fs::filesystem_error result;
if (clipboard_mode == ClipboardMode::Cut)
result = rename_file(clipboard_path, current_path / new_name);
else if (clipboard_mode == ClipboardMode::Copy)
result = copy_file(clipboard_path, current_path / new_name);
if (result.code() != FR_OK) if (result.code() != FR_OK)
nav_.display_modal("Paste Failed", result.what()); nav_.display_modal("Paste Failed", result.what());
copy_path = fs::path{ }; clipboard_path = fs::path{ };
clipboard_mode = ClipboardMode::None;
menu_view.focus(); menu_view.focus();
reload_current(); reload_current();
} }
@ -497,9 +522,12 @@ bool FileManagerView::selected_is_valid() const {
void FileManagerView::refresh_widgets(const bool v) { void FileManagerView::refresh_widgets(const bool v) {
button_rename.hidden(v); button_rename.hidden(v);
button_delete.hidden(v); button_delete.hidden(v);
button_new_dir.hidden(v); button_cut.hidden(v);
button_copy.hidden(v); button_copy.hidden(v);
button_paste.hidden(v); button_paste.hidden(v);
button_new_dir.hidden(v);
button_new_file.hidden(v);
set_dirty(); set_dirty();
} }
@ -521,13 +549,14 @@ FileManagerView::FileManagerView(
&text_date, &text_date,
&button_rename, &button_rename,
&button_delete, &button_delete,
&button_new_dir, &button_cut,
&button_copy, &button_copy,
&button_paste &button_paste,
&button_new_dir,
&button_new_file
}); });
menu_view.on_highlight = [this]() { menu_view.on_highlight = [this]() {
// TODO: enable/disable buttons.
if (selected_is_valid()) if (selected_is_valid())
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path()))); text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
else else
@ -544,32 +573,45 @@ FileManagerView::FileManagerView(
} }
}; };
button_rename.on_select = [this](Button&) { button_rename.on_select = [this]() {
if (selected_is_valid()) if (selected_is_valid())
on_rename(); on_rename();
}; };
button_delete.on_select = [this](Button&) { button_delete.on_select = [this]() {
if (selected_is_valid()) if (selected_is_valid())
on_delete(); on_delete();
}; };
button_new_dir.on_select = [this](Button&) { button_cut.on_select = [this]() {
on_new_dir(); if (selected_is_valid() && !get_selected_entry().is_directory) {
clipboard_path = get_selected_full_path();
clipboard_mode = ClipboardMode::Cut;
} else
nav_.display_modal("Cut", "Can't cut that.");
}; };
button_copy.on_select = [this](Button&) { button_copy.on_select = [this]() {
if (selected_is_valid() && !get_selected_entry().is_directory) if (selected_is_valid() && !get_selected_entry().is_directory) {
copy_path = get_selected_full_path(); clipboard_path = get_selected_full_path();
else clipboard_mode = ClipboardMode::Copy;
} else
nav_.display_modal("Copy", "Can't copy that."); nav_.display_modal("Copy", "Can't copy that.");
}; };
button_paste.on_select = [this](Button&) { button_paste.on_select = [this]() {
if (!copy_path.empty()) if (clipboard_mode != ClipboardMode::None)
on_paste(); on_paste();
else else
nav_.display_modal("Paste", "Copy a file first."); nav_.display_modal("Paste", "Cut or copy a file first.");
};
button_new_dir.on_select = [this]() {
on_new_dir();
};
button_new_file.on_select = [this]() {
on_new_file();
}; };
} }

View File

@ -42,6 +42,12 @@ enum class EmptyReason : uint8_t {
NoSDC NoSDC
}; };
enum class ClipboardMode : uint8_t {
None,
Cut,
Copy
};
class FileManBaseView : public View { class FileManBaseView : public View {
public: public:
FileManBaseView( FileManBaseView(
@ -56,6 +62,7 @@ public:
protected: protected:
static constexpr size_t max_filename_length = 64; static constexpr size_t max_filename_length = 64;
static constexpr size_t max_items_shown = 128;
struct file_assoc_t { struct file_assoc_t {
std::filesystem::path extension; std::filesystem::path extension;
@ -110,6 +117,12 @@ protected:
true true
}; };
// HACK: for item count limit.
Text text_info {
{ 1 * 8, 35 * 8, 15 * 8, 16 },
""
};
Button button_exit { Button button_exit {
{ 21 * 8, 34 * 8, 9 * 8, 32 }, { 21 * 8, 34 * 8, 9 * 8, 32 },
"Exit" "Exit"
@ -194,13 +207,15 @@ public:
private: private:
// Passed by ref to other views needing lifetime extension. // Passed by ref to other views needing lifetime extension.
std::string name_buffer { }; std::string name_buffer { };
std::filesystem::path copy_path { }; std::filesystem::path clipboard_path { };
ClipboardMode clipboard_mode { ClipboardMode::None };
void refresh_widgets(const bool v); void refresh_widgets(const bool v);
void on_rename(); void on_rename();
void on_delete(); void on_delete();
void on_new_dir();
void on_paste(); void on_paste();
void on_new_dir();
void on_new_file();
// True if the selected entry is a real file item. // True if the selected entry is a real file item.
bool selected_is_valid() const; bool selected_is_valid() const;
@ -214,29 +229,53 @@ private:
"" ""
}; };
Button button_rename { NewButton button_rename {
{ 0 * 8, 29 * 8, 9 * 8, 32 }, { 0 * 8, 29 * 8, 4 * 8, 32 },
"Rename" { },
&bitmap_icon_rename,
Color::dark_blue()
}; };
Button button_delete { NewButton button_delete {
{ 21 * 4, 29 * 8, 9 * 8, 32 }, { 4 * 8, 29 * 8, 4 * 8, 32 },
"Delete" { },
&bitmap_icon_trash,
Color::red()
}; };
Button button_new_dir { NewButton button_cut {
{ 21 * 8, 29 * 8, 9 * 8, 32 }, { 9 * 8, 29 * 8, 4 * 8, 32 },
"New Dir" { },
&bitmap_icon_cut,
Color::dark_grey()
}; };
Button button_copy { NewButton button_copy {
{ 0 * 8, 34 * 8, 9 * 8, 32 }, { 13 * 8, 29 * 8, 4 * 8, 32 },
"Copy" { },
&bitmap_icon_copy,
Color::dark_grey()
}; };
Button button_paste { NewButton button_paste {
{ 21 * 4, 34 * 8, 9 * 8, 32 }, { 17 * 8, 29 * 8, 4 * 8, 32 },
"Paste" { },
&bitmap_icon_paste,
Color::dark_grey()
};
NewButton button_new_dir {
{ 22 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_new_dir,
Color::green()
};
NewButton button_new_file {
{ 26 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_new_file,
Color::green()
}; };
}; };

View File

@ -446,7 +446,7 @@ namespace ui {
else else
{ {
auto result = delete_file( pmem_flag_file ); auto result = delete_file( pmem_flag_file );
if( result != 0 ) if( result.code() != FR_OK )
{ {
text_pmem_status.set("!err. deleting pmem flagfile!"); text_pmem_status.set("!err. deleting pmem flagfile!");
} }

View File

@ -2543,6 +2543,137 @@ static constexpr Bitmap bitmap_target {
{ 16, 16 }, bitmap_target_data { 16, 16 }, bitmap_target_data
}; };
static constexpr uint8_t bitmap_icon_trash_data[] = {
0x00, 0x00,
0xC0, 0x01,
0x20, 0x02,
0xFC, 0x1F,
0x00, 0x00,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0x08, 0x08,
0xF0, 0x07,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_trash {
{ 16, 16 }, bitmap_icon_trash_data
};
static constexpr uint8_t bitmap_icon_copy_data[] = {
0x00, 0x00,
0xFC, 0x00,
0x84, 0x01,
0xC4, 0x0F,
0x74, 0x18,
0x44, 0x38,
0x44, 0x78,
0x74, 0x40,
0x44, 0x44,
0x44, 0x44,
0x74, 0x5F,
0x44, 0x44,
0x44, 0x44,
0x7C, 0x40,
0xC0, 0x7F,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_copy {
{ 16, 16 }, bitmap_icon_copy_data
};
static constexpr uint8_t bitmap_icon_cut_data[] = {
0x00, 0x00,
0x10, 0x10,
0x30, 0x18,
0x20, 0x08,
0x60, 0x0C,
0x40, 0x04,
0xC0, 0x06,
0x80, 0x00,
0x80, 0x01,
0x80, 0x01,
0xC0, 0x03,
0x78, 0x1E,
0x44, 0x22,
0x44, 0x22,
0x44, 0x22,
0x38, 0x1C,
};
static constexpr Bitmap bitmap_icon_cut {
{ 16, 16 }, bitmap_icon_cut_data
};
static constexpr uint8_t bitmap_icon_paste_data[] = {
0x00, 0x00,
0xE0, 0x00,
0x18, 0x03,
0xE4, 0x04,
0x04, 0x04,
0x04, 0x04,
0x84, 0x3F,
0x84, 0x20,
0x84, 0x2E,
0x84, 0x20,
0x84, 0x2E,
0x84, 0x20,
0x84, 0x2E,
0xF8, 0x20,
0x80, 0x3F,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_paste {
{ 16, 16 }, bitmap_icon_paste_data
};
static constexpr uint8_t bitmap_icon_new_dir_data[] = {
0x00, 0x00,
0x1E, 0x00,
0x21, 0x00,
0xE1, 0x7F,
0x01, 0xC0,
0x81, 0x81,
0x81, 0x81,
0x81, 0x81,
0xF1, 0x8F,
0xF1, 0x8F,
0x81, 0x81,
0x81, 0x81,
0x81, 0x81,
0x03, 0xC0,
0xFE, 0x7F,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_new_dir {
{ 16, 16 }, bitmap_icon_new_dir_data
};
static constexpr uint8_t bitmap_icon_new_file_data[] = {
0x00, 0x00,
0xFC, 0x07,
0x04, 0x0C,
0x04, 0x1C,
0x04, 0x3C,
0x84, 0x21,
0x84, 0x21,
0x84, 0x21,
0xF4, 0x2F,
0xF4, 0x2F,
0x84, 0x21,
0x84, 0x21,
0x84, 0x21,
0x04, 0x20,
0xFC, 0x3F,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_new_file {
{ 16, 16 }, bitmap_icon_new_file_data
};
} /* namespace ui */ } /* namespace ui */

View File

@ -197,18 +197,21 @@ std::vector<std::filesystem::path> scan_root_directories(const std::filesystem::
return directory_list; return directory_list;
} }
uint32_t delete_file(const std::filesystem::path& file_path) { std::filesystem::filesystem_error delete_file(const std::filesystem::path& file_path) {
return f_unlink(reinterpret_cast<const TCHAR*>(file_path.c_str())); return { f_unlink(reinterpret_cast<const TCHAR*>(file_path.c_str())) };
} }
uint32_t rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name) { std::filesystem::filesystem_error rename_file(
return f_rename(reinterpret_cast<const TCHAR*>(file_path.c_str()), reinterpret_cast<const TCHAR*>(new_name.c_str())); const std::filesystem::path& file_path,
const std::filesystem::path& new_name
) {
return { f_rename(reinterpret_cast<const TCHAR*>(file_path.c_str()), reinterpret_cast<const TCHAR*>(new_name.c_str())) };
} }
std::filesystem::filesystem_error copy_file( std::filesystem::filesystem_error copy_file(
const std::filesystem::path& file_path, const std::filesystem::path& file_path,
const std::filesystem::path& dest_path) const std::filesystem::path& dest_path
{ ) {
File src; File src;
File dst; File dst;
constexpr size_t buffer_size = 512; constexpr size_t buffer_size = 512;
@ -242,8 +245,20 @@ FATTimestamp file_created_date(const std::filesystem::path& file_path) {
return { filinfo.fdate, filinfo.ftime }; return { filinfo.fdate, filinfo.ftime };
} }
uint32_t make_new_directory(const std::filesystem::path& dir_path) { std::filesystem::filesystem_error make_new_file(
return f_mkdir(reinterpret_cast<const TCHAR*>(dir_path.c_str())); const std::filesystem::path& file_path
) {
File f;
auto result = f.create(file_path);
return result.is_valid()
? result.value()
: std::filesystem::filesystem_error{ };
}
std::filesystem::filesystem_error make_new_directory(
const std::filesystem::path& dir_path
) {
return { f_mkdir(reinterpret_cast<const TCHAR*>(dir_path.c_str())) };
} }
namespace std { namespace std {

View File

@ -252,11 +252,12 @@ struct FATTimestamp {
uint16_t FAT_time; uint16_t FAT_time;
}; };
uint32_t delete_file(const std::filesystem::path& file_path); std::filesystem::filesystem_error delete_file(const std::filesystem::path& file_path);
uint32_t rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name); std::filesystem::filesystem_error rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name);
std::filesystem::filesystem_error copy_file(const std::filesystem::path& file_path, const std::filesystem::path& dest_path); std::filesystem::filesystem_error copy_file(const std::filesystem::path& file_path, const std::filesystem::path& dest_path);
FATTimestamp file_created_date(const std::filesystem::path& file_path); FATTimestamp file_created_date(const std::filesystem::path& file_path);
uint32_t make_new_directory(const std::filesystem::path& dir_path); std::filesystem::filesystem_error make_new_file(const std::filesystem::path& file_path);
std::filesystem::filesystem_error make_new_directory(const std::filesystem::path& dir_path);
std::vector<std::filesystem::path> scan_root_files(const std::filesystem::path& directory, const std::filesystem::path& extension); std::vector<std::filesystem::path> scan_root_files(const std::filesystem::path& directory, const std::filesystem::path& extension);
std::vector<std::filesystem::path> scan_root_directories(const std::filesystem::path& directory); std::vector<std::filesystem::path> scan_root_directories(const std::filesystem::path& directory);

View File

@ -167,6 +167,10 @@ void MenuView::clear() {
offset = 0; offset = 0;
} }
size_t MenuView::item_count() const {
return menu_items.size();
}
void MenuView::add_item(MenuItem new_item) { void MenuView::add_item(MenuItem new_item) {
menu_items.push_back(new_item); menu_items.push_back(new_item);

View File

@ -83,6 +83,7 @@ public:
void add_item(MenuItem new_item); void add_item(MenuItem new_item);
void add_items(std::initializer_list<MenuItem> new_items); void add_items(std::initializer_list<MenuItem> new_items);
void clear(); void clear();
size_t item_count() const;
MenuItemView* item_view(size_t index) const; MenuItemView* item_view(size_t index) const;

View File

@ -1143,9 +1143,18 @@ NewButton::NewButton(
Rect parent_rect, Rect parent_rect,
std::string text, std::string text,
const Bitmap* bitmap const Bitmap* bitmap
) : NewButton { parent_rect, text, bitmap, Color::dark_cyan() }
{ }
NewButton::NewButton(
Rect parent_rect,
std::string text,
const Bitmap* bitmap,
Color color
) : Widget { parent_rect }, ) : Widget { parent_rect },
text_ { text }, text_ { text },
bitmap_ (bitmap) bitmap_ { bitmap },
color_ { color }
{ {
set_focusable(true); set_focusable(true);
} }

View File

@ -466,6 +466,7 @@ public:
NewButton(const NewButton&) = delete; NewButton(const NewButton&) = delete;
NewButton& operator=(const NewButton&) = delete; NewButton& operator=(const NewButton&) = delete;
NewButton(Rect parent_rect, std::string text, const Bitmap* bitmap); NewButton(Rect parent_rect, std::string text, const Bitmap* bitmap);
NewButton(Rect parent_rect, std::string text, const Bitmap* bitmap, Color color);
NewButton( NewButton(
) : NewButton { { }, { }, { } } ) : NewButton { { }, { }, { } }
{ {
@ -486,8 +487,8 @@ public:
private: private:
std::string text_; std::string text_;
Color color_ = Color::dark_cyan();
const Bitmap* bitmap_; const Bitmap* bitmap_;
Color color_;
}; };
class Image : public Widget { class Image : public Widget {

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B