diff --git a/src/activities/home/FileBrowserActivity.cpp b/src/activities/home/FileBrowserActivity.cpp index 723a3e199..6b16bf8ba 100644 --- a/src/activities/home/FileBrowserActivity.cpp +++ b/src/activities/home/FileBrowserActivity.cpp @@ -164,7 +164,8 @@ void FileBrowserActivity::loop() { return; } - const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, false); + const int pathReserved = renderer.getLineHeight(SMALL_FONT_ID) + UITheme::getInstance().getMetrics().verticalSpacing; + const int pageItems = UITheme::getNumberOfItemsPerPage(renderer, true, false, true, false, pathReserved); if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { if (files.empty()) return; @@ -294,8 +295,11 @@ void FileBrowserActivity::render(RenderLock&&) { std::string folderName = (basepath == "/") ? tr(STR_SD_CARD) : basepath.substr(basepath.rfind('/') + 1); GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, folderName.c_str()); + const int pathLineHeight = renderer.getLineHeight(SMALL_FONT_ID); + const int pathReserved = pathLineHeight + metrics.verticalSpacing; const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; - const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing; + const int contentHeight = + pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing - pathReserved; if (files.empty()) { renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding, contentTop + 20, tr(STR_NO_FILES_FOUND)); } else { @@ -306,6 +310,33 @@ void FileBrowserActivity::render(RenderLock&&) { [this](int index) { return getFileExtension(files[index]); }, false); } + // Full path display + { + const int pathY = pageHeight - metrics.buttonHintsHeight - metrics.verticalSpacing - pathLineHeight; + const int separatorY = pathY - metrics.verticalSpacing / 2; + renderer.drawLine(0, separatorY, pageWidth - 1, separatorY, 3, true); + const int pathMaxWidth = pageWidth - metrics.contentSidePadding * 2; + // Left-truncate so the deepest directory is always visible + const char* pathStr = basepath.c_str(); + const char* pathDisplay = pathStr; + char leftTruncBuf[256]; + if (renderer.getTextWidth(SMALL_FONT_ID, pathStr) > pathMaxWidth) { + const char ellipsis[] = "\xe2\x80\xa6"; // UTF-8 ellipsis (…) + const int ellipsisWidth = renderer.getTextWidth(SMALL_FONT_ID, ellipsis); + const int available = pathMaxWidth - ellipsisWidth; + // Walk forward from the start until the suffix fits, skipping UTF-8 continuation bytes + const char* p = pathStr; + while (*p) { + if (renderer.getTextWidth(SMALL_FONT_ID, p) <= available) break; + ++p; + while (*p && (static_cast(*p) & 0xC0) == 0x80) ++p; + } + snprintf(leftTruncBuf, sizeof(leftTruncBuf), "%s%s", ellipsis, p); + pathDisplay = leftTruncBuf; + } + renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, pathY, pathDisplay); + } + // Help text const auto labels = mappedInput.mapLabels(basepath == "/" ? tr(STR_HOME) : tr(STR_BACK), files.empty() ? "" : tr(STR_OPEN), @@ -319,4 +350,4 @@ size_t FileBrowserActivity::findEntry(const std::string& name) const { for (size_t i = 0; i < files.size(); i++) if (files[i] == name) return i; return 0; -} \ No newline at end of file +} diff --git a/src/components/UITheme.cpp b/src/components/UITheme.cpp index 57e494840..425981a21 100644 --- a/src/components/UITheme.cpp +++ b/src/components/UITheme.cpp @@ -49,7 +49,7 @@ void UITheme::setTheme(CrossPointSettings::UI_THEME type) { } int UITheme::getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader, bool hasTabBar, bool hasButtonHints, - bool hasSubtitle) { + bool hasSubtitle, int extraReservedHeight) { const ThemeMetrics& metrics = UITheme::getInstance().getMetrics(); int reservedHeight = metrics.topPadding; if (hasHeader) { @@ -61,7 +61,7 @@ int UITheme::getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader if (hasButtonHints) { reservedHeight += metrics.verticalSpacing + metrics.buttonHintsHeight; } - const int availableHeight = renderer.getScreenHeight() - reservedHeight; + const int availableHeight = renderer.getScreenHeight() - reservedHeight - extraReservedHeight; int rowHeight = hasSubtitle ? metrics.listWithSubtitleRowHeight : metrics.listRowHeight; return availableHeight / rowHeight; } diff --git a/src/components/UITheme.h b/src/components/UITheme.h index daa1ec452..c4c37235c 100644 --- a/src/components/UITheme.h +++ b/src/components/UITheme.h @@ -19,7 +19,7 @@ class UITheme { void reload(); void setTheme(CrossPointSettings::UI_THEME type); static int getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader, bool hasTabBar, bool hasButtonHints, - bool hasSubtitle); + bool hasSubtitle, int extraReservedHeight = 0); static std::string getCoverThumbPath(std::string coverBmpPath, int coverHeight); static UIIcon getFileIcon(const std::string& filename); static int getStatusBarHeight();