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:
* - Paging menu items
* - Copy/Move
*/
#include <algorithm>
@ -146,6 +145,22 @@ bool partner_file_prompt(
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 {
@ -198,6 +213,7 @@ FileManBaseView::FileManBaseView(
add_children({
&labels,
&text_current,
&text_info,
&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);
}
@ -465,26 +486,30 @@ void FileManagerView::on_new_dir() {
});
}
void FileManagerView::on_new_file() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) {
make_new_file(current_path / file_name);
reload_current();
});
}
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);
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)
nav_.display_modal("Paste Failed", result.what());
copy_path = fs::path{ };
clipboard_path = fs::path{ };
clipboard_mode = ClipboardMode::None;
menu_view.focus();
reload_current();
}
@ -497,9 +522,12 @@ bool FileManagerView::selected_is_valid() const {
void FileManagerView::refresh_widgets(const bool v) {
button_rename.hidden(v);
button_delete.hidden(v);
button_new_dir.hidden(v);
button_cut.hidden(v);
button_copy.hidden(v);
button_paste.hidden(v);
button_new_dir.hidden(v);
button_new_file.hidden(v);
set_dirty();
}
@ -521,13 +549,14 @@ FileManagerView::FileManagerView(
&text_date,
&button_rename,
&button_delete,
&button_new_dir,
&button_cut,
&button_copy,
&button_paste
&button_paste,
&button_new_dir,
&button_new_file
});
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
@ -544,32 +573,45 @@ FileManagerView::FileManagerView(
}
};
button_rename.on_select = [this](Button&) {
button_rename.on_select = [this]() {
if (selected_is_valid())
on_rename();
};
button_delete.on_select = [this](Button&) {
button_delete.on_select = [this]() {
if (selected_is_valid())
on_delete();
};
button_new_dir.on_select = [this](Button&) {
on_new_dir();
button_cut.on_select = [this]() {
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&) {
if (selected_is_valid() && !get_selected_entry().is_directory)
copy_path = get_selected_full_path();
else
button_copy.on_select = [this]() {
if (selected_is_valid() && !get_selected_entry().is_directory) {
clipboard_path = get_selected_full_path();
clipboard_mode = ClipboardMode::Copy;
} else
nav_.display_modal("Copy", "Can't copy that.");
};
button_paste.on_select = [this](Button&) {
if (!copy_path.empty())
button_paste.on_select = [this]() {
if (clipboard_mode != ClipboardMode::None)
on_paste();
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
};
enum class ClipboardMode : uint8_t {
None,
Cut,
Copy
};
class FileManBaseView : public View {
public:
FileManBaseView(
@ -56,6 +62,7 @@ public:
protected:
static constexpr size_t max_filename_length = 64;
static constexpr size_t max_items_shown = 128;
struct file_assoc_t {
std::filesystem::path extension;
@ -109,6 +116,12 @@ protected:
{ 0, 2 * 8, 240, 26 * 8 },
true
};
// HACK: for item count limit.
Text text_info {
{ 1 * 8, 35 * 8, 15 * 8, 16 },
""
};
Button button_exit {
{ 21 * 8, 34 * 8, 9 * 8, 32 },
@ -194,13 +207,15 @@ public:
private:
// Passed by ref to other views needing lifetime extension.
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 on_rename();
void on_delete();
void on_new_dir();
void on_paste();
void on_new_dir();
void on_new_file();
// True if the selected entry is a real file item.
bool selected_is_valid() const;
@ -214,29 +229,53 @@ private:
""
};
Button button_rename {
{ 0 * 8, 29 * 8, 9 * 8, 32 },
"Rename"
NewButton button_rename {
{ 0 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_rename,
Color::dark_blue()
};
Button button_delete {
{ 21 * 4, 29 * 8, 9 * 8, 32 },
"Delete"
NewButton button_delete {
{ 4 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_trash,
Color::red()
};
Button button_new_dir {
{ 21 * 8, 29 * 8, 9 * 8, 32 },
"New Dir"
NewButton button_cut {
{ 9 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_cut,
Color::dark_grey()
};
Button button_copy {
{ 0 * 8, 34 * 8, 9 * 8, 32 },
"Copy"
NewButton button_copy {
{ 13 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_copy,
Color::dark_grey()
};
Button button_paste {
{ 21 * 4, 34 * 8, 9 * 8, 32 },
"Paste"
NewButton button_paste {
{ 17 * 8, 29 * 8, 4 * 8, 32 },
{ },
&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
{
auto result = delete_file( pmem_flag_file );
if( result != 0 )
if( result.code() != FR_OK )
{
text_pmem_status.set("!err. deleting pmem flagfile!");
}

View File

@ -2543,6 +2543,137 @@ static constexpr Bitmap bitmap_target {
{ 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 */

View File

@ -197,18 +197,21 @@ std::vector<std::filesystem::path> scan_root_directories(const std::filesystem::
return directory_list;
}
uint32_t delete_file(const std::filesystem::path& file_path) {
return f_unlink(reinterpret_cast<const TCHAR*>(file_path.c_str()));
std::filesystem::filesystem_error delete_file(const std::filesystem::path& file_path) {
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) {
return f_rename(reinterpret_cast<const TCHAR*>(file_path.c_str()), reinterpret_cast<const TCHAR*>(new_name.c_str()));
std::filesystem::filesystem_error rename_file(
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(
const std::filesystem::path& file_path,
const std::filesystem::path& dest_path)
{
const std::filesystem::path& dest_path
) {
File src;
File dst;
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 };
}
uint32_t make_new_directory(const std::filesystem::path& dir_path) {
return f_mkdir(reinterpret_cast<const TCHAR*>(dir_path.c_str()));
std::filesystem::filesystem_error make_new_file(
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 {

View File

@ -252,11 +252,12 @@ struct FATTimestamp {
uint16_t FAT_time;
};
uint32_t 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 delete_file(const std::filesystem::path& file_path);
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);
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_directories(const std::filesystem::path& directory);

View File

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

View File

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

View File

@ -1143,9 +1143,18 @@ NewButton::NewButton(
Rect parent_rect,
std::string text,
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 },
text_ { text },
bitmap_ (bitmap)
bitmap_ { bitmap },
color_ { color }
{
set_focusable(true);
}

View File

@ -466,6 +466,7 @@ public:
NewButton(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, Color color);
NewButton(
) : NewButton { { }, { }, { } }
{
@ -486,8 +487,8 @@ public:
private:
std::string text_;
Color color_ = Color::dark_cyan();
const Bitmap* bitmap_;
Color color_;
};
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