95 lines
2.9 KiB
C++
95 lines
2.9 KiB
C++
#pragma once
|
|
|
|
#include <MdParser.h>
|
|
#include <Txt.h>
|
|
|
|
#include <vector>
|
|
|
|
#include "CrossPointSettings.h"
|
|
#include "ReaderUtils.h"
|
|
#include "activities/Activity.h"
|
|
|
|
struct MdHeading {
|
|
size_t offset = 0;
|
|
int level = 1;
|
|
std::string title;
|
|
int pageIndex = -1;
|
|
};
|
|
|
|
class MdReaderActivity final : public Activity {
|
|
std::unique_ptr<Txt> txt;
|
|
|
|
int currentPage = 0;
|
|
int totalPages = 1;
|
|
int pagesUntilFullRefresh = 0;
|
|
|
|
// A single rendered line on screen (after word-wrapping)
|
|
struct RenderedLine {
|
|
std::vector<MdParser::Span> spans;
|
|
int indent = 0; // left indent in pixels
|
|
bool isHR = false; // draw as horizontal rule
|
|
bool isCodeBlock = false;
|
|
};
|
|
|
|
// Streaming reader state
|
|
std::vector<size_t> pageOffsets;
|
|
std::vector<uint8_t> pageCodeBlockState; // 1 if page starts inside a code block
|
|
std::vector<RenderedLine> currentPageLines;
|
|
std::vector<uint8_t> pageBuffer;
|
|
|
|
std::vector<MdHeading> headings;
|
|
int currentHeadingIndex = -1;
|
|
|
|
int linesPerPage = 0;
|
|
int viewportWidth = 0;
|
|
bool initialized = false;
|
|
|
|
// Cached settings for cache validation
|
|
int cachedFontId = 0;
|
|
uint8_t cachedScreenMargin = 0;
|
|
uint8_t cachedParagraphAlignment = CrossPointSettings::LEFT_ALIGN;
|
|
int cachedOrientedMarginTop = 0;
|
|
int cachedOrientedMarginRight = 0;
|
|
int cachedOrientedMarginBottom = 0;
|
|
int cachedOrientedMarginLeft = 0;
|
|
|
|
// Indent constants (in pixels)
|
|
static constexpr int LIST_INDENT = 20;
|
|
static constexpr int BLOCKQUOTE_INDENT = 16;
|
|
static constexpr int CODE_INDENT = 8;
|
|
|
|
void renderPage();
|
|
void renderStatusBar() const;
|
|
|
|
void initializeReader();
|
|
bool loadPageAtOffset(size_t offset, bool startInCodeBlock, std::vector<RenderedLine>& outLines, size_t& nextOffset,
|
|
bool& endInCodeBlock);
|
|
void buildPageIndex();
|
|
bool loadPageIndexCache();
|
|
void savePageIndexCache() const;
|
|
void saveProgress() const;
|
|
void loadProgress();
|
|
void scanHeadings();
|
|
void assignHeadingPageNumbers();
|
|
int getHeadingIndexForOffset(size_t offset) const;
|
|
void jumpToHeading(bool next);
|
|
ReaderUtils::InputDrainGuard inputDrainGuard;
|
|
|
|
// Word-wrap a parsed markdown line into one or more RenderedLines.
|
|
// Returns true if all content was emitted, false if truncated by maxLines.
|
|
bool wordWrapParsedLine(const MdParser::ParsedLine& parsed, int indent, std::vector<RenderedLine>& outLines,
|
|
int maxLines, bool isCodeBlock = false);
|
|
|
|
// Measure total pixel width of a span list
|
|
int measureSpans(const std::vector<MdParser::Span>& spans) const;
|
|
|
|
public:
|
|
explicit MdReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr<Txt> txt)
|
|
: Activity("MdReader", renderer, mappedInput), txt(std::move(txt)) {}
|
|
void onEnter() override;
|
|
void onExit() override;
|
|
void loop() override;
|
|
void render(RenderLock&&) override;
|
|
bool isReaderActivity() const override { return true; }
|
|
void onButtonAction(CrossPointSettings::BUTTON_ACTION action) override;
|
|
}; |