| #ifndef _RIVE_TEXT_INPUT_HPP_ |
| #define _RIVE_TEXT_INPUT_HPP_ |
| |
| #include "rive/generated/text/text_input_base.hpp" |
| #include "rive/advancing_component.hpp" |
| #include "rive/text/raw_text_input.hpp" |
| #include "rive/text/text_interface.hpp" |
| #include "rive/animation/listener_invocation.hpp" |
| #include "rive/input/focusable.hpp" |
| #include <cmath> |
| |
| namespace rive |
| { |
| class TextStyle; |
| class ScrollConstraint; |
| class TextInput : public TextInputBase, |
| public TextInterface, |
| public Focusable, |
| public AdvancingComponent |
| { |
| public: |
| void draw(Renderer* renderer) override; |
| Core* hitTest(HitInfo*, const Mat2D&) override; |
| bool hitTestPoint(const Vec2D& position, |
| bool skipOnUnclipped, |
| bool isPrimaryHit) override; |
| |
| #ifdef WITH_RIVE_TEXT |
| RawTextInput* rawTextInput() { return &m_rawTextInput; } |
| #endif |
| |
| void markPaintDirty() override; |
| void markShapeDirty() override; |
| StatusCode onAddedClean(CoreContext* context) override; |
| |
| AABB localBounds() const override; |
| void update(ComponentDirt value) override; |
| |
| Vec2D measureLayout(float width, |
| LayoutMeasureMode widthMode, |
| float height, |
| LayoutMeasureMode heightMode) override; |
| void controlSize(Vec2D size, |
| LayoutScaleType widthScaleType, |
| LayoutScaleType heightScaleType, |
| LayoutDirection direction) override; |
| |
| bool keyInput(Key value, |
| KeyModifiers modifiers, |
| bool isPressed, |
| bool isRepeat) override; |
| bool textInput(const std::string& text) override; |
| bool selectedText(std::string& outText) const override; |
| bool acceptsTextInput() const override { return true; } |
| bool gamepadDispatch( |
| const ListenerInvocation& invocation, |
| ScriptedDrawable** outDispatchedScriptedDrawable = nullptr) override; |
| void focused() override; |
| void blurred() override; |
| bool worldPosition(Vec2D& outPosition) override; |
| bool worldBounds(AABB& outBounds) override; |
| Artboard* focusableArtboard() const override { return artboard(); } |
| bool acceptsKeyboardInput() const override { return true; } |
| |
| /// Called when the user starts dragging on the text input. |
| /// Places the cursor at the given world position. |
| void startDrag(Vec2D worldPosition); |
| |
| /// Called when the user continues dragging on the text input. |
| /// Extends the selection to the given world position. |
| void drag(Vec2D worldPosition); |
| |
| /// Called when the user ends dragging on the text input. |
| void endDrag(Vec2D worldPosition); |
| |
| /// Selects the word at the current cursor (e.g. on double-click). |
| void selectWord(); |
| |
| /// Selects the visual line at the current cursor (e.g. on triple-click). |
| void selectLine(); |
| |
| /// Advance edge scrolling during drag. Returns true if still scrolling. |
| bool advanceDrag(float elapsedSeconds); |
| bool advanceComponent(float elapsedSeconds, |
| AdvanceFlags flags = AdvanceFlags::Animate | |
| AdvanceFlags::NewFrame) override; |
| |
| /// Whether currently dragging (for hit test to avoid interference). |
| bool isDragging() const { return m_isDragging; } |
| |
| /// Whether this text input currently has focus. |
| bool isFocused() const { return m_focused; } |
| |
| /// Whether the caret should be drawn this frame. Only true while focused |
| /// and while the blink cycle is in its visible phase. |
| bool isCursorVisible() const { return m_focused && m_cursorBlinkVisible; } |
| |
| protected: |
| void textChanged() override; |
| void selectionRadiusChanged() override; |
| void multilineChanged() override; |
| void alignValueChanged() override; |
| void verticalAlignValueChanged() override; |
| void obscuredChanged() override; |
| |
| private: |
| /// Push the current alignment and the width to align it within down to the |
| /// raw text input. Returns true if either actually changed. |
| bool updateAlignment(); |
| |
| /// Convert a world position to local text input coordinates. |
| /// Handles viewport clamping and auto-scroll for scroll constraints. |
| bool worldToLocalWithViewport(Vec2D worldPosition, |
| Vec2D& outLocal, |
| bool enableAutoScroll); |
| |
| float edgeScrollSpeedForDistance(float distanceFromEdge) const; |
| float edgeActivationDistance(float position, float edgeStart) const; |
| |
| /// Show the caret and restart its blink cycle, so it stays solid while the |
| /// user is typing or moving it around. |
| void restartCursorBlink(); |
| |
| /// Toggle the caret when the blink interval elapses. Returns true while |
| /// focused so frames keep coming and the caret keeps blinking. |
| bool advanceCursorBlink(float elapsedSeconds); |
| |
| /// Whether the focus being granted came from Tab or directional traversal. |
| bool focusedByTraversal(); |
| |
| void updateMultiline(bool syncDisplayedText = false); |
| static std::string strippedLineBreaks(const std::string& text); |
| std::string displayedText() const; |
| void syncDisplayedTextFromSource(bool preserveCursor); |
| void syncSourceTextFromRaw(); |
| |
| AABB m_worldBounds; |
| std::string m_sourceText; |
| TextStyle* m_textStyle = nullptr; |
| ScrollConstraint* m_scrollConstraint = nullptr; |
| |
| /// Whether the user is currently dragging to select text. |
| bool m_isDragging = false; |
| |
| /// Whether this text input currently has focus. |
| bool m_focused = false; |
| |
| /// Whether the caret is in the shown phase of its blink cycle. |
| bool m_cursorBlinkVisible = true; |
| |
| /// Time spent in the current phase of the caret's blink cycle. |
| float m_cursorBlinkSeconds = 0.0f; |
| Vec2D m_lastDragWorldPosition = Vec2D(NAN, NAN); |
| |
| /// Scroll velocity for edge scrolling during drag in X. |
| float m_scrollX = 0.0f; |
| |
| /// Scroll velocity for edge scrolling during drag. |
| float m_scrollY = 0.0f; |
| float m_layoutWidth = NAN; |
| float m_layoutHeight = NAN; |
| |
| #ifdef WITH_RIVE_TEXT |
| RawTextInput m_rawTextInput; |
| #endif |
| }; |
| } // namespace rive |
| |
| #endif |