Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Tactility/Private/Tactility/app/files/View.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ namespace tt::app::files {

class View final {
std::shared_ptr<State> state;

size_t current_start_index = 0;
size_t last_loaded_index = 0;
const size_t MAX_BATCH = 50;

lv_obj_t* dir_entry_list = nullptr;
lv_obj_t* action_list = nullptr;
Expand All @@ -33,7 +37,7 @@ class View final {
explicit View(const std::shared_ptr<State>& state) : state(state) {}

void init(const AppContext& appContext, lv_obj_t* parent);
void update();
void update(size_t start_index = 0);

void onNavigateUpPressed();
void onDirEntryPressed(uint32_t index);
Expand All @@ -45,6 +49,10 @@ class View final {
void onDirEntryListScrollBegin();
void onResult(LaunchId launchId, Result result, std::unique_ptr<Bundle> bundle);
void deinit(const AppContext& appContext);

private:

bool resolveDirentFromListIndex(int32_t list_index, dirent& out_entry);
};

}
181 changes: 118 additions & 63 deletions Tactility/Source/app/files/View.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include <Tactility/app/files/View.h>
#include <Tactility/app/files/SupportedFiles.h>
#include <Tactility/app/files/View.h>

#include <Tactility/LogMessages.h>
#include <Tactility/Logger.h>
Expand All @@ -10,11 +10,11 @@
#include <Tactility/app/imageviewer/ImageViewer.h>
#include <Tactility/app/inputdialog/InputDialog.h>
#include <Tactility/app/notes/Notes.h>
#include <tactility/check.h>
#include <Tactility/file/File.h>
#include <Tactility/kernel/Platform.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/lvgl/Toolbar.h>
#include <tactility/check.h>

#include <cstdio>
#include <cstring>
Expand Down Expand Up @@ -104,7 +104,7 @@ void View::viewFile(const std::string& path, const std::string& filename) {
// install(filename);
auto message = std::format("Do you want to install {}?", filename);
installAppPath = processed_filepath;
auto choices = std::vector { "Yes", "No" };
auto choices = std::vector {"Yes", "No"};
installAppLaunchId = alertdialog::start("Install?", message, choices);
#endif
} else if (isSupportedImageFile(filename)) {
Expand All @@ -123,59 +123,72 @@ void View::viewFile(const std::string& path, const std::string& filename) {
onNavigate();
}

bool View::resolveDirentFromListIndex(int32_t list_index, dirent& out_entry) {
const bool is_root = (state->getCurrentPath() == "/");
const bool has_back = (!is_root && current_start_index > 0);

if (has_back && list_index == 0) {
return false; // Back button
}

const size_t adjusted_index =
current_start_index + static_cast<size_t>(list_index) - (has_back ? 1 : 0);

return state->getDirent(static_cast<uint32_t>(adjusted_index), out_entry);
}

void View::onDirEntryPressed(uint32_t index) {
dirent dir_entry;
if (state->getDirent(index, dir_entry)) {
LOGGER.info("Pressed {} {}", dir_entry.d_name, dir_entry.d_type);
state->setSelectedChildEntry(dir_entry.d_name);
using namespace tt::file;
switch (dir_entry.d_type) {
case TT_DT_DIR:
case TT_DT_CHR:
state->setEntriesForChildPath(dir_entry.d_name);
onNavigate();
update();
break;
case TT_DT_LNK:
LOGGER.warn("opening links is not supported");
break;
case TT_DT_REG:
viewFile(state->getCurrentPath(), dir_entry.d_name);
onNavigate();
break;
default:
// Assume it's a file
// TODO: Find a better way to identify a file
viewFile(state->getCurrentPath(), dir_entry.d_name);
onNavigate();
break;
}
if (!resolveDirentFromListIndex(static_cast<int32_t>(index), dir_entry)) {
return;
}

LOGGER.info("Pressed {} {}", dir_entry.d_name, dir_entry.d_type);
state->setSelectedChildEntry(dir_entry.d_name);

using namespace tt::file;
switch (dir_entry.d_type) {
case TT_DT_DIR:
case TT_DT_CHR:
state->setEntriesForChildPath(dir_entry.d_name);
onNavigate();
update();
break;

case TT_DT_LNK:
LOGGER.warn("opening links is not supported");
break;

default:
viewFile(state->getCurrentPath(), dir_entry.d_name);
onNavigate();
break;
}
}

void View::onDirEntryLongPressed(int32_t index) {
dirent dir_entry;
if (state->getDirent(index, dir_entry)) {
LOGGER.info("Pressed {} {}", dir_entry.d_name, dir_entry.d_type);
state->setSelectedChildEntry(dir_entry.d_name);
using namespace file;
switch (dir_entry.d_type) {
case TT_DT_DIR:
case TT_DT_CHR:
showActionsForDirectory();
break;
case TT_DT_LNK:
LOGGER.warn("Opening links is not supported");
break;
case TT_DT_REG:
showActionsForFile();
break;
default:
// Assume it's a file
// TODO: Find a better way to identify a file
showActionsForFile();
break;
}
if (!resolveDirentFromListIndex(index, dir_entry)) {
return;
}

LOGGER.info("Pressed {} {}", dir_entry.d_name, dir_entry.d_type);
state->setSelectedChildEntry(dir_entry.d_name);

using namespace file;
switch (dir_entry.d_type) {
case TT_DT_DIR:
case TT_DT_CHR:
showActionsForDirectory();
break;

case TT_DT_LNK:
LOGGER.warn("Opening links is not supported");
break;

default:
showActionsForFile();
break;
}
}

Expand Down Expand Up @@ -251,7 +264,7 @@ void View::onDeletePressed() {
LOGGER.info("Pending delete {}", file_path);
state->setPendingAction(State::ActionDelete);
std::string message = "Do you want to delete this?\n" + file_path;
const std::vector<std::string> choices = { "Yes", "No" };
const std::vector<std::string> choices = {"Yes", "No"};
alertdialog::start("Are you sure?", message, choices);
}

Expand Down Expand Up @@ -289,25 +302,67 @@ void View::showActionsForFile() {
lv_obj_remove_flag(action_list, LV_OBJ_FLAG_HIDDEN);
}

void View::update() {
void View::update(size_t start_index) {
const bool is_root = (state->getCurrentPath() == "/");

auto scoped_lockable = lvgl::getSyncLock()->asScopedLock();
if (scoped_lockable.lock(lvgl::defaultLockTime)) {
lv_obj_clean(dir_entry_list);
if (!scoped_lockable.lock(lvgl::defaultLockTime)) {
LOGGER.error(LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "lvgl");
return;
}

lv_obj_clean(dir_entry_list);

current_start_index = start_index;

state->withEntries([this, is_root](const std::vector<dirent>& entries) {
size_t total_entries = entries.size();
if (current_start_index >= total_entries) {
current_start_index = (total_entries > MAX_BATCH)
? (total_entries - MAX_BATCH)
: 0;
}
size_t count = 0;

if (!is_root && current_start_index > 0) {
auto* back_btn = lv_list_add_btn(dir_entry_list, LV_SYMBOL_LEFT, "Back");
lv_obj_add_event_cb(back_btn, [](lv_event_t* event) {
auto* view = static_cast<View*>(lv_event_get_user_data(event));
size_t new_index = (view->current_start_index >= view->MAX_BATCH) ?
view->current_start_index - view->MAX_BATCH : 0;
view->update(new_index); }, LV_EVENT_SHORT_CLICKED, this);
}

for (size_t i = current_start_index; i < total_entries; ++i) {
auto entry = entries[i];

createDirEntryWidget(dir_entry_list, entry);
count++;

state->withEntries([this](const std::vector<dirent>& entries) {
for (auto entry : entries) {
LOGGER.debug("Entry: {} {}", entry.d_name, entry.d_type);
createDirEntryWidget(dir_entry_list, entry);
if (count >= MAX_BATCH) {
break;
}
});
}

if (state->getCurrentPath() == "/") {
lv_obj_add_flag(navigate_up_button, LV_OBJ_FLAG_HIDDEN);
last_loaded_index = std::min(current_start_index + count, total_entries);

if (!is_root && last_loaded_index < total_entries) {
if (total_entries > current_start_index &&
+ (total_entries - current_start_index) > MAX_BATCH) {
auto* next_btn = lv_list_add_btn(dir_entry_list, LV_SYMBOL_RIGHT, "Next");
lv_obj_add_event_cb(next_btn, [](lv_event_t* event) {
auto* view = static_cast<View*>(lv_event_get_user_data(event));
view->update(view->last_loaded_index); }, LV_EVENT_SHORT_CLICKED, this);
}
} else {
lv_obj_remove_flag(navigate_up_button, LV_OBJ_FLAG_HIDDEN);
last_loaded_index = total_entries;
}
});

if (is_root) {
lv_obj_add_flag(navigate_up_button, LV_OBJ_FLAG_HIDDEN);
} else {
LOGGER.error(LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "lvgl");
lv_obj_remove_flag(navigate_up_button, LV_OBJ_FLAG_HIDDEN);
}
}

Expand Down Expand Up @@ -476,4 +531,4 @@ void View::deinit(const AppContext& appContext) {
lv_obj_remove_event_cb(dir_entry_list, dirEntryListScrollBeginCallback);
}

}
} // namespace tt::app::files