#pragma once #include #include #include #include namespace roro::notes { // The text of a note while it's edited (F1, Q144, Q145): UTF-8 held whole in memory, a cursor, and // the part of it on screen. Lines wrap at spaces, `cols` characters wide; a line owns the space or // the newline it ends with, so every byte of the text belongs to exactly one line. No index of // lines is kept (a note of newlines alone would need twice its size): where a line starts is // worked out from the start of its paragraph, which is never far. class NoteText { public: static constexpr size_t kMaxBytes = 16 * 1024; // Room for a full note is reserved once, so typing never has to find a bigger block of memory: // on the device a failed allocation is the end. The second form takes over a string the // caller filled (and reserved): a note is never in memory twice. NoteText(int cols, int rows); NoteText(int cols, int rows, std::string&& text); // Copied into the buffer already held. CRLF becomes LF (Q148). False, and nothing changes, if // it's over kMaxBytes. bool setText(const std::string& text); const std::string& text() const { return text_; } size_t cursor() const { return cursor_; } uint32_t revision() const { return revision_; } // changes with every edit: is it saved? bool insert(uint32_t codePoint); // false: the note is full bool insertText(const std::string& s); // all of it or nothing void backspace(); void left(); void right(); void up(); void down(); void pageUp(); void pageDown(); void lineStart(); void lineEnd(); void toStart(); void toEnd(); // The screen, kept around the cursor: its rows (tabs as spaces, the ending newline left out), // and where the cursor is on it, in rows and characters. std::vector rows(); int cursorRow(); int cursorCol(); int percent() const { return text_.empty() ? 0 : static_cast(top_ * 100 / text_.size()); } std::string firstLine() const; // without its newline, for the title and the file's name private: size_t nextLine(size_t start) const; // where the line after the one at `start` starts size_t lineOf(size_t pos) const; // the start of the line `pos` is on size_t lastSpot(size_t start) const; // the last place the cursor can be on that line size_t atColumn(size_t start, int col) const; int columnOf(size_t start, size_t pos) const; bool hasLineAfter(size_t start) const; void moved(bool keepGoal = false); void follow(); // scrolls so the cursor is on screen void dropCarriageReturns(); int cols_, rows_; std::string text_; size_t cursor_ = 0, top_ = 0; int goal_ = -1; // the column Up and Down aim for, across short lines uint32_t revision_ = 0; }; // The file a new note is saved as (Q142): from its first line, "Shopping list!" -> "shopping-list", // or "note-" when that gives nothing. No extension, no folder. std::string nameFromFirstLine(const std::string& firstLine, const std::string& stamp); // A row's title in the Notes list, from the first bytes of a file: its first line that isn't // blank, cut to `maxChars`; "" if there's none. std::string titleFrom(const std::string& head, size_t maxChars); } // namespace roro::notes