Fileman memory opt (#2076)

* paginator
* fixes
* string instead of path, so saves ram.
* fix FileLoadView
* pagination list number
* pagination "size" test
* added detection of special folder name to display page numbers instead of (0 or a number of byte)
* order of things: only one directory '..', only on first page
Co-authored-by: gullradriel <gullradriel@no-mail.com>
This commit is contained in:
Totoo 2024-04-02 17:02:02 +02:00 committed by GitHub
parent 56303eb385
commit a30875b582
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 147 additions and 48 deletions

View File

@ -62,7 +62,7 @@ std::string truncate(const fs::path& path, size_t max_length) {
}
// Inserts the entry into the entry list sorted directories first then by file name.
void insert_sorted(std::vector<fileman_entry>& entries, fileman_entry&& entry) {
void insert_sorted(std::list<fileman_entry>& entries, fileman_entry&& entry) {
auto it = std::lower_bound(
std::begin(entries), std::end(entries), entry,
[](const fileman_entry& lhs, const fileman_entry& rhs) {
@ -149,31 +149,64 @@ namespace ui {
void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
current_path = dir_path;
entry_list.clear();
menu_view.clear();
auto filtering = !extension_filter.empty();
bool cxx_file = path_iequal(cxx_ext, extension_filter);
bool wasfull = false; // reached the limit or not
text_current.set(dir_path.empty() ? "(sd root)" : truncate(dir_path, 24));
for (const auto& entry : fs::directory_iterator(dir_path, u"*")) {
// Hide files starting with '.' (hidden / tmp).
if (entry_list.size() >= max_items_loaded) {
wasfull = true;
break; // hard limit. size() complexyt constant in list from c++11, and for vector it constant.
}
if (!show_hidden_files && is_hidden_file(entry.path()))
continue;
if (fs::is_regular_file(entry.status())) {
if (!filtering || path_iequal(entry.path().extension(), extension_filter) || (cxx_file && is_cxx_capture_file(entry.path())))
insert_sorted(entry_list, {entry.path(), (uint32_t)entry.size(), false});
insert_sorted(entry_list, {entry.path().string(), (uint32_t)entry.size(), false});
} else if (fs::is_directory(entry.status())) {
insert_sorted(entry_list, {entry.path(), 0, true});
insert_sorted(entry_list, {entry.path().string(), 0, true});
}
}
// paginating
auto list_size = entry_list.size();
nb_pages = 1 + (list_size / items_per_page);
size_t start = pagination * items_per_page;
size_t stop = start + items_per_page;
if (list_size > start) {
if (list_size < stop)
stop = list_size;
entry_list.erase(std::next(entry_list.begin(), stop), entry_list.end());
entry_list.erase(entry_list.begin(), std::next(entry_list.begin(), start));
}
// Add "parent" directory if not at the root.
if (!dir_path.empty())
entry_list.insert(entry_list.begin(), {parent_dir_path, 0, true});
if (!dir_path.empty() && pagination == 0)
entry_list.insert(entry_list.begin(), {parent_dir_path.string(), 0, true});
// add next page
if (list_size > start + items_per_page) {
entry_list.push_back({str_next, (uint32_t)pagination + 1, true});
} else {
// add warning if reached limit
if (wasfull) {
entry_list.push_back({str_full, max_items_loaded, true});
}
}
// add prev page
if (pagination > 0) {
entry_list.insert(entry_list.begin(), {str_back, (uint32_t)pagination - 1, true});
}
}
fs::path FileManBaseView::get_selected_full_path() const {
if (get_selected_entry().path == parent_dir_path)
if (get_selected_entry().path == parent_dir_path.string())
return current_path.parent_path();
return current_path / get_selected_entry().path;
@ -181,7 +214,10 @@ fs::path FileManBaseView::get_selected_full_path() const {
const fileman_entry& FileManBaseView::get_selected_entry() const {
// TODO: return reference to an "empty" entry on OOB?
return entry_list[menu_view.highlighted_index()];
auto it = entry_list.begin();
if (menu_view.highlighted_index() >= 1) std::advance(it, menu_view.highlighted_index());
return *it;
// return entry_list[menu_view.highlighted_index()];
}
FileManBaseView::FileManBaseView(
@ -230,7 +266,7 @@ void FileManBaseView::push_dir(const fs::path& path) {
current_path /= path;
saved_index_stack.push_back(menu_view.highlighted_index());
menu_view.set_highlighted(0);
reload_current();
reload_current(true);
}
}
@ -239,29 +275,56 @@ void FileManBaseView::pop_dir() {
return;
current_path = current_path.parent_path();
reload_current();
reload_current(true);
menu_view.set_highlighted(saved_index_stack.back());
saved_index_stack.pop_back();
}
std::string get_extension(std::string t) {
const auto index = t.find_last_of(u'.');
if (index == t.npos) {
return {};
} else {
return t.substr(index);
}
}
std::string get_stem(std::string t) {
const auto index = t.find_last_of(u'.');
if (index == t.npos) {
return t;
} else {
return t.substr(0, index);
}
}
std::string get_filename(std::string _s) {
const auto index = _s.find_last_of("/");
if (index == _s.npos) {
return _s;
} else {
return _s.substr(index + 1);
}
}
void FileManBaseView::refresh_list() {
if (on_refresh_widgets)
on_refresh_widgets(false);
auto prev_highlight = menu_view.highlighted_index();
prev_highlight = menu_view.highlighted_index();
menu_view.clear();
for (const auto& entry : entry_list) {
auto entry_name = truncate(entry.path, 20);
auto entry_name = std::string{entry.path.length() <= 20 ? entry.path : entry.path.substr(0, 20)};
if (entry.is_directory) {
auto size_str =
(entry.path == parent_dir_path)
? ""
: to_string_dec_uint(file_count(current_path / entry.path));
std::string size_str{};
if (entry.path == str_next || entry.path == str_back) {
size_str = to_string_dec_uint(1 + entry.size) + "/" + to_string_dec_uint(nb_pages); // show computed number of pages
} else {
size_str = (entry.path == parent_dir_path.string()) ? "" : to_string_dec_uint(file_count(current_path / entry.path));
}
menu_view.add_item(
{entry_name + std::string(21 - entry_name.length(), ' ') + size_str,
{entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str,
ui::Color::yellow(),
&bitmap_icon_dir,
[this](KeyEvent key) {
@ -270,11 +333,11 @@ void FileManBaseView::refresh_list() {
}});
} else {
const auto& assoc = get_assoc(entry.path.extension());
const auto& assoc = get_assoc(get_extension(entry.path));
auto size_str = to_string_file_size(entry.size);
menu_view.add_item(
{entry_name + std::string(21 - entry_name.length(), ' ') + size_str,
{entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str,
assoc.color,
assoc.icon,
[this](KeyEvent key) {
@ -282,16 +345,13 @@ void FileManBaseView::refresh_list() {
on_select_entry(key);
}});
}
// HACK: Should page menu items instead of limiting the number.
if (menu_view.item_count() >= max_items_shown)
break;
}
menu_view.set_highlighted(prev_highlight);
}
void FileManBaseView::reload_current() {
void FileManBaseView::reload_current(bool reset_pagination) {
if (reset_pagination) pagination = 0;
load_directory_contents(current_path);
refresh_list();
}
@ -327,6 +387,21 @@ FileLoadView::FileLoadView(
on_select_entry = [this](KeyEvent) {
if (get_selected_entry().is_directory) {
if (get_selected_entry().path == str_full) {
return;
}
if (get_selected_entry().path == str_back) {
pagination--;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
if (get_selected_entry().path == str_next) {
pagination++;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
push_dir(get_selected_entry().path);
} else {
if (on_changed)
@ -416,10 +491,10 @@ void FileManagerView::on_rename(std::string_view hint) {
auto& entry = get_selected_entry();
// Append the hint to the filename stem as a rename suggestion.
name_buffer = entry.path.stem().string();
name_buffer = get_stem(entry.path);
if (!hint.empty())
name_buffer += "_" + std::string{hint};
name_buffer += entry.path.extension().string();
name_buffer += get_extension(entry.path);
// Set the rename cursor to before the extension to make renaming simpler.
uint32_t cursor_pos = (uint32_t)name_buffer.length();
@ -440,11 +515,11 @@ void FileManagerView::on_rename(std::string_view hint) {
auto new_name = renamed_path.replace_extension(partner.extension());
rename_file(partner, current_path / new_name);
}
reload_current();
reload_current(false);
});
if (!has_partner)
reload_current();
reload_current(false);
});
}
@ -454,7 +529,7 @@ void FileManagerView::on_delete() {
return;
}
auto name = get_selected_entry().path.filename().string();
auto name = get_filename(get_selected_entry().path);
nav_.push<ModalMessageView>(
"Delete", "Delete " + name + "\nAre you sure?", YESNO,
[this](bool choice) {
@ -466,11 +541,11 @@ void FileManagerView::on_delete() {
[this](const fs::path& partner, bool should_delete) {
if (should_delete)
delete_file(partner);
reload_current();
reload_current(true);
});
if (!has_partner)
reload_current();
reload_current(true);
}
});
}
@ -495,7 +570,7 @@ void FileManagerView::on_clean() {
std::filesystem::path current_full_path = path_name / file_name;
delete_file(current_full_path);
}
reload_current();
reload_current(true);
}
});
}
@ -504,7 +579,7 @@ void FileManagerView::on_new_dir() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) {
make_new_directory(current_path / dir_name);
reload_current();
reload_current(true);
});
}
@ -528,14 +603,14 @@ void FileManagerView::on_paste() {
clipboard_path = fs::path{};
clipboard_mode = ClipboardMode::None;
menu_view.focus();
reload_current();
reload_current(true);
}
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();
reload_current(true);
});
}
@ -558,7 +633,7 @@ bool FileManagerView::handle_file_open() {
return true;
} else if (path_iequal(bmp_ext, ext)) {
nav_.push<SplashViewer>(path);
reload_current();
reload_current(false);
return true;
} else if (path_iequal(rem_ext, ext)) {
nav_.push<RemoteView>(path);
@ -570,7 +645,7 @@ bool FileManagerView::handle_file_open() {
bool FileManagerView::selected_is_valid() const {
return !entry_list.empty() &&
get_selected_entry().path != parent_dir_path;
get_selected_entry().path != parent_dir_path.string();
}
FileManagerView::FileManagerView(
@ -602,7 +677,7 @@ FileManagerView::FileManagerView(
});
menu_view.on_highlight = [this]() {
if (menu_view.highlighted_index() >= max_items_shown - 1) {
if (menu_view.highlighted_index() >= max_items_loaded - 1) { // todo check this if correct
text_date.set_style(&Styles::red);
text_date.set("Too many files!");
} else {
@ -617,13 +692,30 @@ FileManagerView::FileManagerView(
refresh_list();
on_select_entry = [this](KeyEvent key) {
if (key == KeyEvent::Select && get_selected_entry().is_directory) {
push_dir(get_selected_entry().path);
} else if (key == KeyEvent::Select && handle_file_open()) {
return;
} else {
button_rename.focus();
if (key == KeyEvent::Select) {
if (get_selected_entry().is_directory) {
if (get_selected_entry().path == str_full) {
return;
}
if (get_selected_entry().path == str_back) {
pagination--;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
if (get_selected_entry().path == str_next) {
pagination++;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
push_dir(get_selected_entry().path);
return;
} else if (handle_file_open()) {
return;
}
}
button_rename.focus();
};
button_rename.on_select = [this]() {

View File

@ -31,7 +31,7 @@
namespace ui {
struct fileman_entry {
std::filesystem::path path{};
std::string path{};
uint32_t size{};
bool is_directory{};
};
@ -61,8 +61,12 @@ class FileManBaseView : public View {
void push_dir(const std::filesystem::path& path);
protected:
static constexpr size_t max_filename_length = 64;
static constexpr size_t max_items_shown = 100;
uint32_t prev_highlight = 0;
uint8_t pagination = 0;
uint8_t nb_pages = 1;
static constexpr size_t max_filename_length = 20;
static constexpr size_t max_items_loaded = 75; // too memory hungry, so won't load more
static constexpr size_t items_per_page = 20;
struct file_assoc_t {
std::filesystem::path extension;
@ -87,7 +91,7 @@ class FileManBaseView : public View {
void pop_dir();
void refresh_list();
void reload_current();
void reload_current(bool reset_pagination = false);
void load_directory_contents(const std::filesystem::path& dir_path);
const file_assoc_t& get_assoc(const std::filesystem::path& ext) const;
@ -97,11 +101,14 @@ class FileManBaseView : public View {
std::function<void(KeyEvent)> on_select_entry{nullptr};
std::function<void(bool)> on_refresh_widgets{nullptr};
const std::string str_back{"<--"};
const std::string str_next{"-->"};
const std::string str_full{"Can't load more.."};
const std::filesystem::path parent_dir_path{u".."};
std::filesystem::path current_path{u""};
std::filesystem::path extension_filter{u""};
std::vector<fileman_entry> entry_list{};
std::list<fileman_entry> entry_list{};
std::vector<uint32_t> saved_index_stack{};
bool show_hidden_files{false};