feat: add BookManageMenuActivity popup sub-activity
Contextual popup menu for book management with Archive/Unarchive, Delete, Delete Cache Only, and Reindex options. Supports long-press on Reindex to trigger full reindex including cover/thumbnail regen. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
115
src/activities/home/BookManageMenuActivity.cpp
Normal file
115
src/activities/home/BookManageMenuActivity.cpp
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#include "BookManageMenuActivity.h"
|
||||||
|
|
||||||
|
#include <GfxRenderer.h>
|
||||||
|
#include <I18n.h>
|
||||||
|
|
||||||
|
#include "MappedInputManager.h"
|
||||||
|
#include "components/UITheme.h"
|
||||||
|
#include "fontIds.h"
|
||||||
|
|
||||||
|
void BookManageMenuActivity::buildMenuItems() {
|
||||||
|
menuItems.clear();
|
||||||
|
if (archived) {
|
||||||
|
menuItems.push_back({Action::UNARCHIVE, StrId::STR_UNARCHIVE_BOOK});
|
||||||
|
} else {
|
||||||
|
menuItems.push_back({Action::ARCHIVE, StrId::STR_ARCHIVE_BOOK});
|
||||||
|
}
|
||||||
|
menuItems.push_back({Action::DELETE, StrId::STR_DELETE_BOOK});
|
||||||
|
menuItems.push_back({Action::DELETE_CACHE, StrId::STR_DELETE_CACHE_ONLY});
|
||||||
|
menuItems.push_back({Action::REINDEX, StrId::STR_REINDEX_BOOK});
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookManageMenuActivity::onEnter() {
|
||||||
|
Activity::onEnter();
|
||||||
|
selectedIndex = 0;
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookManageMenuActivity::onExit() { Activity::onExit(); }
|
||||||
|
|
||||||
|
void BookManageMenuActivity::loop() {
|
||||||
|
// Long-press detection: REINDEX_FULL when long-pressing on the Reindex item
|
||||||
|
if (mappedInput.isPressed(MappedInputManager::Button::Confirm) && mappedInput.getHeldTime() >= LONG_PRESS_MS) {
|
||||||
|
if (!ignoreNextConfirmRelease && selectedIndex < static_cast<int>(menuItems.size()) &&
|
||||||
|
menuItems[selectedIndex].action == Action::REINDEX) {
|
||||||
|
ignoreNextConfirmRelease = true;
|
||||||
|
auto cb = onAction;
|
||||||
|
cb(Action::REINDEX_FULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonNavigator.onNext([this] {
|
||||||
|
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, static_cast<int>(menuItems.size()));
|
||||||
|
requestUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonNavigator.onPrevious([this] {
|
||||||
|
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, static_cast<int>(menuItems.size()));
|
||||||
|
requestUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
|
if (ignoreNextConfirmRelease) {
|
||||||
|
ignoreNextConfirmRelease = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedIndex < static_cast<int>(menuItems.size())) {
|
||||||
|
auto cb = onAction;
|
||||||
|
cb(menuItems[selectedIndex].action);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||||
|
auto cb = onCancel;
|
||||||
|
cb();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookManageMenuActivity::render(Activity::RenderLock&&) {
|
||||||
|
renderer.clearScreen();
|
||||||
|
|
||||||
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|
||||||
|
constexpr int popupMargin = 20;
|
||||||
|
constexpr int lineHeight = 30;
|
||||||
|
constexpr int titleHeight = 40;
|
||||||
|
const int optionCount = static_cast<int>(menuItems.size());
|
||||||
|
const int popupH = titleHeight + popupMargin + lineHeight * optionCount + popupMargin;
|
||||||
|
const int popupW = pageWidth - 60;
|
||||||
|
const int popupX = (pageWidth - popupW) / 2;
|
||||||
|
const int popupY = (pageHeight - popupH) / 2;
|
||||||
|
|
||||||
|
// Popup border and background
|
||||||
|
renderer.fillRect(popupX - 2, popupY - 2, popupW + 4, popupH + 4, true);
|
||||||
|
renderer.fillRect(popupX, popupY, popupW, popupH, false);
|
||||||
|
|
||||||
|
// Title
|
||||||
|
renderer.drawText(UI_12_FONT_ID, popupX + popupMargin, popupY + 8, tr(STR_MANAGE_BOOK), true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
|
// Divider line
|
||||||
|
const int dividerY = popupY + titleHeight;
|
||||||
|
renderer.fillRect(popupX + 4, dividerY, popupW - 8, 1, true);
|
||||||
|
|
||||||
|
// Menu items
|
||||||
|
const int startY = dividerY + popupMargin / 2;
|
||||||
|
for (int i = 0; i < optionCount; ++i) {
|
||||||
|
const int itemY = startY + i * lineHeight;
|
||||||
|
const bool isSelected = (i == selectedIndex);
|
||||||
|
|
||||||
|
if (isSelected) {
|
||||||
|
renderer.fillRect(popupX + 2, itemY, popupW - 4, lineHeight, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.drawText(UI_10_FONT_ID, popupX + popupMargin, itemY, I18N.get(menuItems[i].labelId), !isSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button hints
|
||||||
|
const auto labels = mappedInput.mapLabels(tr(STR_CANCEL), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
||||||
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
|
renderer.displayBuffer();
|
||||||
|
}
|
||||||
59
src/activities/home/BookManageMenuActivity.h
Normal file
59
src/activities/home/BookManageMenuActivity.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <I18n.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "../Activity.h"
|
||||||
|
#include "util/ButtonNavigator.h"
|
||||||
|
|
||||||
|
class BookManageMenuActivity final : public Activity {
|
||||||
|
public:
|
||||||
|
enum class Action {
|
||||||
|
ARCHIVE,
|
||||||
|
UNARCHIVE,
|
||||||
|
DELETE,
|
||||||
|
DELETE_CACHE,
|
||||||
|
REINDEX,
|
||||||
|
REINDEX_FULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit BookManageMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||||
|
const std::string& bookPath, bool isArchived,
|
||||||
|
const std::function<void(Action)>& onAction,
|
||||||
|
const std::function<void()>& onCancel)
|
||||||
|
: Activity("BookManageMenu", renderer, mappedInput),
|
||||||
|
bookPath(bookPath),
|
||||||
|
archived(isArchived),
|
||||||
|
onAction(onAction),
|
||||||
|
onCancel(onCancel) {
|
||||||
|
buildMenuItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onEnter() override;
|
||||||
|
void onExit() override;
|
||||||
|
void loop() override;
|
||||||
|
void render(Activity::RenderLock&&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct MenuItem {
|
||||||
|
Action action;
|
||||||
|
StrId labelId;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string bookPath;
|
||||||
|
bool archived;
|
||||||
|
std::vector<MenuItem> menuItems;
|
||||||
|
int selectedIndex = 0;
|
||||||
|
ButtonNavigator buttonNavigator;
|
||||||
|
|
||||||
|
bool ignoreNextConfirmRelease = false;
|
||||||
|
static constexpr unsigned long LONG_PRESS_MS = 700;
|
||||||
|
|
||||||
|
const std::function<void(Action)> onAction;
|
||||||
|
const std::function<void()> onCancel;
|
||||||
|
|
||||||
|
void buildMenuItems();
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user