Public Access
A page larger than memory allows is now windowed instead of cut: one pass over its file (cache or Saved Page) counts lines, indexes every 64th (offset, and the preformatted state there in bit 31: about 1 KB for a 1 MB page) and loads the first window. Scrolling near either end reads the next or previous window on the Gemini task; the line on top of the screen stays put, the scrollbar follows the whole page, and Tab at a window's edge pages on instead of wrapping. Window budgets count the memory the old window gives back. Display pages alternate between two cache files so the one on screen is never overwritten by the next fetch; jobs use a third. On the device, Cosmos with IRC connected: 226 of 419 lines at first, then lines 192-419, back to 64 and 0 while scrolling. `key space` added to the console's key command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
82 lines
3.2 KiB
C++
82 lines
3.2 KiB
C++
#pragma once
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "app.h"
|
|
#include "dialog_model.h"
|
|
#include "gemtext.h"
|
|
#include "line_editor.h"
|
|
#include "services/gemini_service.h"
|
|
#include "ui/canvas.h"
|
|
|
|
namespace roro {
|
|
|
|
// Geminispace on the Cardputer (docs/milestones/G1.md). Pages come from the GeminiService as
|
|
// lines; the App wraps them once (keeping only each line's first row), then wraps just the lines
|
|
// on screen when it draws, so a long page costs 4 bytes a line on top of its text.
|
|
class GeminiApp : public App {
|
|
public:
|
|
explicit GeminiApp(GeminiService& gemini) : gemini_(gemini) {}
|
|
void onEnter() override;
|
|
bool onKey(const KeyEvent& e) override;
|
|
bool textEntryActive() const override { return addressOpen_ || inputOpen_; }
|
|
void update(uint32_t nowMs) override;
|
|
void draw(Canvas& c) override;
|
|
|
|
private:
|
|
struct Visit {
|
|
std::string url;
|
|
int topRow;
|
|
};
|
|
|
|
void navigate(const std::string& url, bool remember = true, bool preferSaved = false);
|
|
void follow(const std::string& link);
|
|
std::string titleOf() const;
|
|
bool isSaved() const { return !page_.savedFrom.empty(); }
|
|
std::string savedPathOf() const { return page_.url.substr(7); } // file://…
|
|
void show(GeminiPage&& page);
|
|
void analyse(); // types and links of the lines in memory
|
|
void applyWindow(GeminiPage&& window);
|
|
void requestWindow(size_t firstLine);
|
|
size_t topLine() const; // the source line (in the whole page) at the top of the screen
|
|
bool moreBelow() const { return page_.windowed && page_.windowStart + page_.text.lineCount() < page_.totalLines; }
|
|
bool moreAbove() const { return page_.windowed && page_.windowStart > 0; }
|
|
void showMessage(const std::string& url, const std::string& gemtext);
|
|
void layout(Canvas& c);
|
|
void drawLine(Canvas& c, size_t index, int firstRow, int y, int rows);
|
|
std::vector<std::string> rowsOf(Canvas& c, size_t index) const;
|
|
gemini::GemLine lineAt(size_t index) const;
|
|
int rowsOnScreen() const;
|
|
void selectLink(int direction);
|
|
void scrollTo(int row);
|
|
|
|
GeminiService& gemini_;
|
|
GeminiPage page_;
|
|
std::vector<uint8_t> types_; // per source line: gemini::LineType
|
|
std::vector<uint32_t> firstRow_; // per source line, plus the total at the end
|
|
std::vector<uint32_t> links_; // source lines that are links
|
|
bool layoutDirty_ = true;
|
|
int topRow_ = 0, hscroll_ = 0;
|
|
int selected_ = -1; // into links_
|
|
int restoreRow_ = -1; // going back: where that page was scrolled to, applied after layout
|
|
bool windowLoading_ = false;
|
|
long keepLine_ = -1; // a new window: the source line to keep at the top, applied after layout
|
|
std::vector<Visit> history_;
|
|
bool loading_ = false;
|
|
std::string loadingUrl_, status_;
|
|
uint32_t statusMs_ = 0;
|
|
bool addressOpen_ = false;
|
|
LineEditor address_{1024};
|
|
std::unique_ptr<DialogModel> certDialog_;
|
|
GeminiPage pendingCert_;
|
|
std::unique_ptr<DialogModel> deleteDialog_;
|
|
bool inputOpen_ = false, inputSensitive_ = false;
|
|
std::string inputPrompt_, inputUrl_;
|
|
LineEditor input_{1024};
|
|
std::string reopen_; // after a refresh: the Saved Page to show again
|
|
};
|
|
|
|
} // namespace roro
|