## Summary * **What is the goal of this PR?** Noticed that the footnotes selection screen does not add proper margins to accommodate screen orientation * **What changes are included?** Copied some code over from `EpubReaderChapterSelectionActivity` to calculate the proper margins in `CW` and `Inverted` orientation | Before | After | |--------|--------| | <img width="578" height="435" alt="image" src="https://github.com/user-attachments/assets/0518a0c6-13d2-48a1-9283-90c83861e4c2" /> | <img width="578" height="435" alt="image" src="https://github.com/user-attachments/assets/ac34365c-72d0-4f07-85a6-17e966b28909" /> | | <img width="328" height="435" alt="image" src="https://github.com/user-attachments/assets/0614f19b-1000-4efe-8ef9-b533d2763a53" /> | <img width="328" height="435" alt="image" src="https://github.com/user-attachments/assets/ce9add2f-88e8-4032-a59c-efb55f366604" /> | --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**NO**_ --------- Co-authored-by: Jan Ivanov <jan.ivanov@sirma.com>
111 lines
3.9 KiB
C++
111 lines
3.9 KiB
C++
#include "EpubReaderFootnotesActivity.h"
|
|
|
|
#include <GfxRenderer.h>
|
|
#include <I18n.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include "MappedInputManager.h"
|
|
#include "components/UITheme.h"
|
|
#include "fontIds.h"
|
|
|
|
void EpubReaderFootnotesActivity::onEnter() {
|
|
Activity::onEnter();
|
|
selectedIndex = 0;
|
|
requestUpdate();
|
|
}
|
|
|
|
void EpubReaderFootnotesActivity::onExit() { Activity::onExit(); }
|
|
|
|
void EpubReaderFootnotesActivity::loop() {
|
|
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
|
ActivityResult result;
|
|
result.isCancelled = true;
|
|
setResult(std::move(result));
|
|
finish();
|
|
return;
|
|
}
|
|
|
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
|
if (selectedIndex >= 0 && selectedIndex < static_cast<int>(footnotes.size())) {
|
|
setResult(FootnoteResult{footnotes[selectedIndex].href});
|
|
finish();
|
|
}
|
|
return;
|
|
}
|
|
|
|
buttonNavigator.onNext([this] {
|
|
if (!footnotes.empty()) {
|
|
selectedIndex = (selectedIndex + 1) % footnotes.size();
|
|
requestUpdate();
|
|
}
|
|
});
|
|
|
|
buttonNavigator.onPrevious([this] {
|
|
if (!footnotes.empty()) {
|
|
selectedIndex = (selectedIndex - 1 + footnotes.size()) % footnotes.size();
|
|
requestUpdate();
|
|
}
|
|
});
|
|
}
|
|
|
|
void EpubReaderFootnotesActivity::render(RenderLock&&) {
|
|
renderer.clearScreen();
|
|
|
|
const auto pageWidth = renderer.getScreenWidth();
|
|
const auto orientation = renderer.getOrientation();
|
|
// Landscape orientation: reserve a horizontal gutter for button hints.
|
|
const bool isLandscapeCw = orientation == GfxRenderer::Orientation::LandscapeClockwise;
|
|
const bool isLandscapeCcw = orientation == GfxRenderer::Orientation::LandscapeCounterClockwise;
|
|
// Inverted portrait: reserve vertical space for hints at the top.
|
|
const bool isPortraitInverted = orientation == GfxRenderer::Orientation::PortraitInverted;
|
|
const int hintGutterWidth = (isLandscapeCw || isLandscapeCcw) ? 30 : 0;
|
|
// Landscape CW places hints on the left edge; CCW keeps them on the right.
|
|
const int contentX = isLandscapeCw ? hintGutterWidth : 0;
|
|
const int contentWidth = pageWidth - hintGutterWidth;
|
|
const int hintGutterHeight = isPortraitInverted ? 50 : 0;
|
|
const int contentY = hintGutterHeight;
|
|
|
|
// Manual centering to honor content gutters.
|
|
const int titleX =
|
|
contentX + (contentWidth - renderer.getTextWidth(UI_12_FONT_ID, tr(STR_FOOTNOTES), EpdFontFamily::BOLD)) / 2;
|
|
renderer.drawText(UI_12_FONT_ID, titleX, 15 + contentY, tr(STR_FOOTNOTES), true, EpdFontFamily::BOLD);
|
|
|
|
if (footnotes.empty()) {
|
|
renderer.drawCenteredText(UI_10_FONT_ID, 90 + contentY, tr(STR_NO_FOOTNOTES));
|
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", "");
|
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
renderer.displayBuffer();
|
|
return;
|
|
}
|
|
|
|
constexpr int lineHeight = 36;
|
|
const int screenWidth = renderer.getScreenWidth();
|
|
const int marginLeft = contentX + 20;
|
|
|
|
const int visibleCount = std::max(1, (renderer.getScreenHeight() - contentY) / lineHeight);
|
|
if (selectedIndex < scrollOffset) scrollOffset = selectedIndex;
|
|
if (selectedIndex >= scrollOffset + visibleCount) scrollOffset = selectedIndex - visibleCount + 1;
|
|
|
|
for (int i = scrollOffset; i < static_cast<int>(footnotes.size()) && i < scrollOffset + visibleCount; i++) {
|
|
const int y = 60 + contentY + (i - scrollOffset) * lineHeight;
|
|
const bool isSelected = (i == selectedIndex);
|
|
|
|
if (isSelected) {
|
|
renderer.fillRect(0, y, screenWidth, lineHeight, true);
|
|
}
|
|
|
|
// Show footnote number and abbreviated href
|
|
std::string label = footnotes[i].number;
|
|
if (label.empty()) {
|
|
label = tr(STR_LINK);
|
|
}
|
|
renderer.drawText(UI_10_FONT_ID, marginLeft, y + 4, label.c_str(), !isSelected);
|
|
}
|
|
|
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), "", "");
|
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
|
|
renderer.displayBuffer();
|
|
}
|