| #ifndef _RIVE_RAW_TEXT_INPUT_HPP_ | |
| #define _RIVE_RAW_TEXT_INPUT_HPP_ | |
| #ifdef WITH_RIVE_TEXT | |
| #include "rive/text/cursor.hpp" | |
| #include "rive/text/text.hpp" | |
| #include "rive/text/fully_shaped_text.hpp" | |
| #include "rive/text/text_selection_path.hpp" | |
| namespace rive | |
| { | |
| class Factory; | |
| enum class CursorBoundary : uint8_t | |
| { | |
| character, | |
| word, | |
| subWord, | |
| line | |
| }; | |
| class RawTextInput | |
| { | |
| public: | |
| #ifdef TESTING | |
| uint32_t measureCount = 0; | |
| // Shapes run to check maxLines. | |
| uint32_t limitShapeCount = 0; | |
| #endif | |
| RawTextInput(); | |
| void draw( | |
| Factory* factory, | |
| Renderer* renderer, | |
| const Mat2D& worldTransform, | |
| RenderPaint* textPaint, | |
| RenderPaint* selectionPaint, | |
| RenderPaint* cursorPaint); | |
| float fontSize() const { return m_textRun.size; } | |
| void fontSize(float value); | |
| float maxWidth() const { return m_maxWidth; } | |
| void maxWidth(float value); | |
| float maxHeight() const { return m_maxHeight; } | |
| void maxHeight(float value); | |
| TextSizing sizing() const { return m_sizing; } | |
| void sizing(TextSizing value); | |
| TextOverflow overflow() const { return m_overflow; } | |
| void overflow(TextOverflow value); | |
| TextAlign align() const { return m_align; } | |
| void align(TextAlign value); | |
| /// The width lines are aligned within when it's wider than the text | |
| /// itself. Text wider than this aligns within its own width, which | |
| /// effectively left aligns it (and leaves horizontal scrolling intact). | |
| float alignWidth() const { return m_alignWidth; } | |
| void alignWidth(float value); | |
| VerticalTextAlign verticalAlign() const { return m_verticalAlign; } | |
| void verticalAlign(VerticalTextAlign value); | |
| /// The height the text block is aligned within, the vertical counterpart | |
| /// to alignWidth: text taller than this stays at the top, leaving | |
| /// vertical scrolling intact. | |
| float alignHeight() const { return m_alignHeight; } | |
| void alignHeight(float value); | |
| rcp<Font> font() const { return m_textRun.font; } | |
| void font(rcp<Font> value); | |
| float paragraphSpacing() const { return m_paragraphSpacing; } | |
| void paragraphSpacing(float value); | |
| float selectionCornerRadius() const { return m_selectionCornerRadius; } | |
| void selectionCornerRadius(float value); | |
| bool separateSelectionText() const; | |
| void separateSelectionText(bool value); | |
| // When obscured, every code point displays as a bullet while the real | |
| // text stays intact for editing. | |
| bool obscured() const; | |
| void obscured(bool value); | |
| // Most code points insert() lets the text grow to, 0 for no limit. Only | |
| // edits are held to it: text set from outside and undo/redo are kept as | |
| // they are, even when they exceed it. | |
| uint32_t maxCharacters() const { return m_maxCharacters; } | |
| void maxCharacters(uint32_t value) { m_maxCharacters = value; } | |
| // Most displayed (wrapped) lines insert() lets the text grow to, 0 for no | |
| // limit. Held to edits only, like maxCharacters. | |
| uint32_t maxLines() const { return m_maxLines; } | |
| void maxLines(uint32_t value) { m_maxLines = value; } | |
| // Whether the layout hugs the text's width, so the field grows with its | |
| // text up to the width measure() was last offered: maxLines then wraps | |
| // there. Otherwise it wraps at maxWidth, where the text displays. | |
| bool hugsWidth() const; | |
| void hugsWidth(bool value); | |
| enum class Flags : uint8_t | |
| { | |
| none = 0, | |
| shapeDirty = 1 << 0, | |
| selectionDirty = 1 << 1, | |
| separateSelectionText = 1 << 2, | |
| measureDirty = 1 << 3, | |
| obscured = 1 << 4, | |
| // measure() has run, so the layout's width is known. | |
| measured = 1 << 5, | |
| hugsWidth = 1 << 6, | |
| // Shaped since update() last built the paths. Shaping clears | |
| // shapeDirty, and editing shapes on demand, so this is what tells | |
| // update() its paths are out of date. | |
| shaped = 1 << 7 | |
| }; | |
| Flags update(Factory* factory); | |
| ShapePaintPath* textPath() { return &m_textPath; } | |
| ShapePaintPath* selectedTextPath() { return &m_selectedTextPath; } | |
| ShapePaintPath* cursorPath() { return &m_cursorPath; } | |
| ShapePaintPath* selectionPath() { return &m_selectionPath; } | |
| rcp<RenderPath> clipRenderPath() { return m_clipRenderPath; } | |
| /// Returns the bounds of the text object (helpful for aligning multiple | |
| /// text objects/procredurally drawn shapes). | |
| AABB bounds() const; | |
| CursorVisualPosition cursorVisualPosition(CursorPosition position) | |
| { | |
| return position.visualPosition(m_shape); | |
| } | |
| CursorVisualPosition cursorVisualPosition() const | |
| { | |
| return m_cursorVisualPosition; | |
| } | |
| // Replace the selection with text, truncated to what fits within | |
| // maxCharacters and maxLines. Returns false, changing nothing, when none | |
| // of it fits. | |
| bool insert(const std::string& text); | |
| bool insert(Unichar codePoint); | |
| void erase(); | |
| void backspace(int32_t direction); | |
| void cursorLeft( | |
| CursorBoundary boundary = CursorBoundary::character, | |
| bool select = false); | |
| void cursorRight( | |
| CursorBoundary boundary = CursorBoundary::character, | |
| bool select = false); | |
| void cursorUp(bool select = false); | |
| void cursorDown(bool select = false); | |
| void moveCursorTo(Vec2D translation, bool select = false); | |
| const FullyShapedText& shape() const { return m_shape; } | |
| const Cursor cursor() const { return m_cursor; } | |
| void cursor(Cursor value); | |
| // Selects the word at the cursor. | |
| void selectWord(); | |
| // Selects the entire text. | |
| void selectAll(); | |
| // Collapses the cursor after the last character. | |
| void moveCursorToEnd(); | |
| // Selects the visual line at the cursor. | |
| void selectLine(); | |
| // Collapses the selection to the cursor's end position. | |
| void clearSelection(); | |
| std::string text() const; | |
| // The text within the cursor's selection range (empty when the selection | |
| // is collapsed). | |
| std::string selectedText() const; | |
| void text(std::string value); | |
| void textPreserveCursor(std::string value); | |
| // Length of the input text. | |
| size_t length() const; | |
| // Returns true if the input is empty. | |
| bool empty() const; | |
| void undo(); | |
| void redo(); | |
| AABB measure(float maxWidth, float maxHeight); | |
| enum class Delineator : uint8_t | |
| { | |
| unknown = 0, | |
| lowercase = 1 << 0, | |
| uppercase = 1 << 1, | |
| symbol = 1 << 2, | |
| underscore = 1 << 3, | |
| whitespace = 1 << 4, | |
| word = lowercase | uppercase | underscore, | |
| any = lowercase | uppercase | symbol | underscore | whitespace | lowercase | |
| }; | |
| private: | |
| void captureJournalEntry(Cursor cursor); | |
| void cursorHorizontal(int32_t offset, CursorBoundary boundary, bool select); | |
| static Delineator classify(Unichar codepoint); | |
| Delineator classify(CursorPosition position) const; | |
| // Hunt for a delineator bit maks from position in direction. Returns what | |
| // was actually found. The position will be updated to the location where | |
| // the Delineator was found. | |
| Delineator find( | |
| uint8_t delineatorMask, | |
| CursorPosition& position, | |
| int32_t direction) const; | |
| CursorPosition findPosition( | |
| uint8_t delineatorMask, | |
| const CursorPosition& position, | |
| int32_t direction) const; | |
| const OrderedLine* orderedLine(CursorPosition position) const; | |
| bool insertCodePoints(Span<const Unichar> codePoints); | |
| size_t fittingCount(Span<const Unichar> codePoints); | |
| uint32_t lineCountWith( | |
| Span<const Unichar> codePoints, | |
| size_t count, | |
| bool withRestOfParagraph); | |
| void ensureShape(); | |
| // The position at a code point index. Its line is resolved now when the | |
| // text can be shaped, else left for update() to resolve once the font | |
| // arrives: a focus can select or place the caret before then. | |
| CursorPosition positionAtIndex(uint32_t codePointIndex); | |
| std::vector<Unichar>& shapeableText(); | |
| void buildTextPaths(Factory* factory); | |
| void computeVisualPositionFromCursor(); | |
| void setTextPrivate(std::string value); | |
| bool flagged(Flags mask) const; | |
| bool unflag(Flags mask); | |
| void flag(Flags mask); | |
| Cursor m_cursor; | |
| TextRun m_textRun; | |
| ShapePaintPath m_textPath; | |
| ShapePaintPath m_selectedTextPath; | |
| ShapePaintPath m_cursorPath; | |
| TextSelectionPath m_selectionPath; | |
| std::vector<Unichar> m_text; | |
| std::vector<Unichar> m_obscuredText; | |
| FullyShapedText m_shape; | |
| std::unique_ptr<FullyShapedText> m_measuringShape; | |
| float m_lastMeasureMaxWidth = 0.0f; | |
| float m_lastMeasureMaxHeight = 0.0f; | |
| Flags m_flags = Flags::none; | |
| float m_paragraphSpacing = 0.0f; | |
| TextOrigin m_origin = TextOrigin::top; | |
| TextSizing m_sizing = TextSizing::autoWidth; | |
| TextOverflow m_overflow = TextOverflow::visible; | |
| TextAlign m_align = TextAlign::left; | |
| TextWrap m_wrap = TextWrap::wrap; | |
| float m_maxWidth = 0.0f; | |
| float m_maxHeight = 0.0f; | |
| float m_alignWidth = 0.0f; | |
| VerticalTextAlign m_verticalAlign = VerticalTextAlign::top; | |
| float m_alignHeight = 0.0f; | |
| rcp<RenderPath> m_clipRenderPath; | |
| float m_idealCursorX = -1.0f; | |
| CursorVisualPosition m_cursorVisualPosition; | |
| std::vector<AABB> m_selectionRects; | |
| float m_selectionCornerRadius = 5.0f; | |
| uint32_t m_maxCharacters = 0; | |
| struct JournalEntry | |
| { | |
| Cursor cursorFrom; | |
| Cursor cursorTo; | |
| std::string text; | |
| }; | |
| std::vector<JournalEntry> m_journal; | |
| uint32_t m_journalIndex = 0; | |
| uint32_t m_maxLines = 0; | |
| }; | |
| // Helper for performing bitwise and with Delineator. | |
| inline uint8_t operator&( | |
| const RawTextInput::Delineator& delineator, | |
| const uint8_t mask) | |
| { | |
| return (uint8_t)delineator & mask; | |
| } | |
| inline uint8_t operator~(const RawTextInput::Delineator& delineator) | |
| { | |
| return ~(uint8_t)delineator; | |
| } | |
| inline RawTextInput::Delineator operator|( | |
| const RawTextInput::Delineator& a, | |
| const RawTextInput::Delineator& b) | |
| { | |
| return (RawTextInput::Delineator)((uint8_t)a | (uint8_t)b); | |
| } | |
| inline RawTextInput::Delineator operator&( | |
| const RawTextInput::Delineator& a, | |
| const RawTextInput::Delineator& b) | |
| { | |
| return (RawTextInput::Delineator)((uint8_t)a & (uint8_t)b); | |
| } | |
| } // namespace rive | |
| #endif | |
| #endif |