Files
crosspoint-reader-mod/src/activities/util/DirectoryPickerActivity.cpp

167 lines
5.0 KiB
C++
Raw Normal View History

#include "DirectoryPickerActivity.h"
#include <HalStorage.h>
#include <I18n.h>
#include <cstring>
#include "MappedInputManager.h"
#include "components/UITheme.h"
#include "fontIds.h"
#include "util/StringUtils.h"
void DirectoryPickerActivity::onEnter() {
Activity::onEnter();
basepath = initialPath;
if (basepath.empty()) basepath = "/";
// Validate the initial path exists; fall back to root if not
auto dir = Storage.open(basepath.c_str());
if (!dir || !dir.isDirectory()) {
if (dir) dir.close();
basepath = "/";
} else {
dir.close();
}
selectorIndex = 0;
loadDirectories();
requestUpdate();
}
void DirectoryPickerActivity::onExit() {
directories.clear();
Activity::onExit();
}
void DirectoryPickerActivity::loadDirectories() {
directories.clear();
auto root = Storage.open(basepath.c_str());
if (!root || !root.isDirectory()) {
if (root) root.close();
return;
}
root.rewindDirectory();
char name[256];
for (auto file = root.openNextFile(); file; file = root.openNextFile()) {
file.getName(name, sizeof(name));
if (name[0] == '.' || strcmp(name, "System Volume Information") == 0) {
file.close();
continue;
}
if (file.isDirectory()) {
directories.emplace_back(std::string(name) + "/");
}
file.close();
}
root.close();
StringUtils::sortFileList(directories);
}
void DirectoryPickerActivity::loop() {
// Absorb the Confirm release from the parent activity that launched us
if (waitForConfirmRelease) {
if (!mappedInput.isPressed(MappedInputManager::Button::Confirm)) {
waitForConfirmRelease = false;
}
return;
}
// Index 0 = "Save Here", indices 1..N = directory entries
const int totalItems = 1 + static_cast<int>(directories.size());
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
if (selectorIndex == 0) {
onSelect(basepath);
} else {
const auto& dirName = directories[selectorIndex - 1];
// Strip trailing '/'
std::string folderName = dirName.substr(0, dirName.length() - 1);
basepath = (basepath.back() == '/' ? basepath : basepath + "/") + folderName;
selectorIndex = 0;
loadDirectories();
requestUpdate();
}
return;
}
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
if (basepath == "/") {
onCancel();
} else {
auto slash = basepath.find_last_of('/');
basepath = (slash == 0) ? "/" : basepath.substr(0, slash);
selectorIndex = 0;
loadDirectories();
requestUpdate();
}
return;
}
buttonNavigator.onNextRelease([this, totalItems] {
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, totalItems);
requestUpdate();
});
buttonNavigator.onPreviousRelease([this, totalItems] {
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, totalItems);
requestUpdate();
});
const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, false);
buttonNavigator.onNextContinuous([this, totalItems, pageItems] {
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, totalItems, pageItems);
requestUpdate();
});
buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] {
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, totalItems, pageItems);
requestUpdate();
});
}
void DirectoryPickerActivity::render(Activity::RenderLock&&) {
renderer.clearScreen();
const auto& metrics = UITheme::getInstance().getMetrics();
const auto pageWidth = renderer.getScreenWidth();
const auto pageHeight = renderer.getScreenHeight();
std::string folderName = (basepath == "/") ? tr(STR_SD_CARD) : basepath.substr(basepath.rfind('/') + 1);
GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_SELECT_FOLDER));
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing;
const int totalItems = 1 + static_cast<int>(directories.size());
GUI.drawList(
renderer, Rect{0, contentTop, pageWidth, contentHeight}, totalItems, selectorIndex,
[this](int index) -> std::string {
if (index == 0) {
std::string label = std::string(tr(STR_SAVE_HERE)) + " (" + basepath + ")";
return label;
}
// Strip trailing '/' for display
const auto& dir = directories[index - 1];
return dir.substr(0, dir.length() - 1);
},
nullptr,
[this](int index) -> UIIcon {
return (index == 0) ? UIIcon::File : UIIcon::Folder;
});
const char* backLabel = (basepath == "/") ? tr(STR_CANCEL) : tr(STR_BACK);
const char* confirmLabel = (selectorIndex == 0) ? tr(STR_SAVE_HERE) : tr(STR_OPEN);
const auto labels = mappedInput.mapLabels(backLabel, confirmLabel, tr(STR_DIR_UP), tr(STR_DIR_DOWN));
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();
}