Files
crosspoint-reader/src/activities/home/MyLibraryActivity.h

68 lines
2.0 KiB
C
Raw Normal View History

My Library: Tab bar w/ Recent Books + File Browser (#250) # Summary This PR introduces a reusable Tab Bar component and combines the Recent Books and File Browser into a unified tabbed page called "My Library" accessible from the Home screen. ## Features ### New Tab Bar Component A flexible, reusable tab bar component added to `ScreenComponents` that can be used throughout the application. ### New Scroll Indicator Component A page position indicator for lists that span multiple pages. **Features:** - Up/down arrow indicators - Current page fraction display (e.g., "1/3") - Only renders when content spans multiple pages ### My Library Activity A new unified view combining Recent Books and File Browser into a single tabbed page. **Tabs:** - **Recent** - Shows recently opened books - **Files** - Browse SD card directory structure **Navigation:** - Up/Down or Left/Right: Navigate through list items - Left/Right (when first item selected): Switch between tabs - Confirm: Open selected book or enter directory - Back: Go up directory (Files tab) or return home - Long press Back: Jump to root directory (Files tab) **UI Elements:** - Tab bar with selection indicator - Scroll/page indicator on right side - Side button hints (up/down arrows) - Dynamic bottom button labels ("BACK" in subdirectories, "HOME" at root) ## Tab Bar Usage The tab bar component is designed to be reusable across different activities. Here's how to use it: ### Basic Example ```cpp #include "ScreenComponents.h" void MyActivity::render() const { renderer.clearScreen(); // Define tabs with labels and selection state std::vector<TabInfo> tabs = { {"Tab One", currentTab == 0}, // Selected when currentTab is 0 {"Tab Two", currentTab == 1}, // Selected when currentTab is 1 {"Tab Three", currentTab == 2} // Selected when currentTab is 2 }; // Draw tab bar at Y position 15, returns height of the tab bar int tabBarHeight = ScreenComponents::drawTabBar(renderer, 15, tabs); // Position your content below the tab bar int contentStartY = 15 + tabBarHeight + 10; // Add some padding // Draw content based on selected tab if (currentTab == 0) { renderTabOneContent(contentStartY); } else if (currentTab == 1) { renderTabTwoContent(contentStartY); } else { renderTabThreeContent(contentStartY); } renderer.displayBuffer(); } ``` Video Demo: https://share.cleanshot.com/P6NBncFS <img width="250" src="https://github.com/user-attachments/assets/07de4418-968e-4a88-9b42-ac5f53d8a832" /> <img width="250" src="https://github.com/user-attachments/assets/e40201ed-dcc8-4568-b008-cd2bf13ebb2a" /> <img width="250" src="https://github.com/user-attachments/assets/73db269f-e629-4696-b8ca-0b8443451a05" /> --------- Co-authored-by: Dave Allie <dave@daveallie.com>
2026-01-21 05:38:38 -06:00
#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <functional>
#include <string>
#include <vector>
#include "../Activity.h"
class MyLibraryActivity final : public Activity {
public:
enum class Tab { Recent, Files };
private:
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
Tab currentTab = Tab::Recent;
int selectorIndex = 0;
bool updateRequired = false;
// Recent tab state
std::vector<std::string> bookTitles; // Display titles for each book
std::vector<std::string> bookPaths; // Paths for each visible book (excludes missing)
// Files tab state (from FileSelectionActivity)
std::string basepath = "/";
std::vector<std::string> files;
// Callbacks
const std::function<void()> onGoHome;
const std::function<void(const std::string& path, Tab fromTab)> onSelectBook;
// Number of items that fit on a page
int getPageItems() const;
int getCurrentItemCount() const;
int getTotalPages() const;
int getCurrentPage() const;
// Data loading
void loadRecentBooks();
void loadFiles();
size_t findEntry(const std::string& name) const;
// Rendering
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
void render() const;
void renderRecentTab() const;
void renderFilesTab() const;
public:
explicit MyLibraryActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& onGoHome,
const std::function<void(const std::string& path, Tab fromTab)>& onSelectBook,
Tab initialTab = Tab::Recent, std::string initialPath = "/")
: Activity("MyLibrary", renderer, mappedInput),
currentTab(initialTab),
basepath(initialPath.empty() ? "/" : std::move(initialPath)),
onGoHome(onGoHome),
onSelectBook(onSelectBook) {}
void onEnter() override;
void onExit() override;
void loop() override;
};