#pragma once #include #include #include #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 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 types_; // per source line: gemini::LineType std::vector firstRow_; // per source line, plus the total at the end std::vector 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 history_; bool loading_ = false; std::string loadingUrl_, status_; uint32_t statusMs_ = 0; bool addressOpen_ = false; LineEditor address_{1024}; std::unique_ptr certDialog_; GeminiPage pendingCert_; std::unique_ptr 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