/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QQUICKTEXTINPUT_P_P_H #define QQUICKTEXTINPUT_P_P_H #include "qquicktextinput_p.h" #include "qquicktext_p.h" #include "qquickimplicitsizeitem_p_p.h" #include "qquicktextutil_p.h" #include #include #include #include #include #include #include #include #include #include #include "qplatformdefs.h" #include // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. QT_BEGIN_NAMESPACE class QQuickTextNode; class QInputControl; class Q_QUICK_PRIVATE_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPrivate { public: Q_DECLARE_PUBLIC(QQuickTextInput) typedef QQuickTextInput Public; struct ExtraData { ExtraData(); qreal padding; qreal topPadding; qreal leftPadding; qreal rightPadding; qreal bottomPadding; bool explicitTopPadding : 1; bool explicitLeftPadding : 1; bool explicitRightPadding : 1; bool explicitBottomPadding : 1; bool implicitResize : 1; }; QLazilyAllocated extra; QQuickTextInputPrivate() : hscroll(0) , vscroll(0) , cursorItem(nullptr) , textNode(nullptr) , m_maskData(nullptr) , color(QRgb(0xFF000000)) , selectionColor(QRgb(0xFF000080)) , selectedTextColor(QRgb(0xFFFFFFFF)) , m_cursor(0) #if QT_CONFIG(im) , m_preeditCursor(0) #endif , m_blinkEnabled(false) , m_blinkTimer(0) , m_maxLength(32767) , m_lastCursorPos(-1) , m_undoState(0) , m_selstart(0) , m_selend(0) #if QT_CONFIG(im) , inputMethodHints(Qt::ImhNone) #endif , hAlign(QQuickTextInput::AlignLeft) , vAlign(QQuickTextInput::AlignTop) , wrapMode(QQuickTextInput::NoWrap) , m_echoMode(QQuickTextInput::Normal) , renderType(QQuickTextUtil::textRenderType()) , updateType(UpdatePaintNode) , mouseSelectionMode(QQuickTextInput::SelectCharacters) , m_layoutDirection(Qt::LayoutDirectionAuto) , m_passwordCharacter(QGuiApplication::styleHints()->passwordMaskCharacter()) , m_passwordMaskDelay(QGuiApplication::styleHints()->passwordMaskDelay()) , focusOnPress(true) , cursorVisible(false) , cursorPending(false) , autoScroll(true) , selectByMouse(false) , canPaste(false) , canPasteValid(false) , canUndo(false) , canRedo(false) , hAlignImplicit(true) , selectPressed(false) , textLayoutDirty(true) , persistentSelection(false) , hasImState(false) , m_separator(0) , m_readOnly(0) , m_textDirty(0) #if QT_CONFIG(im) , m_preeditDirty(0) #endif , m_selDirty(0) , m_validInput(1) , m_acceptableInput(1) , m_blinkStatus(0) , m_passwordEchoEditing(false) , inLayout(false) , requireImplicitWidth(false) , overwriteMode(false) { } ~QQuickTextInputPrivate() { // If this control is used for password input, we don't want the // password data to stay in the process memory, therefore we need // to zero it out if (m_echoMode != QQuickTextInput::Normal) m_text.fill(u'\0'); } void init(); void resetInputMethod(); void startCreatingCursor(); void ensureVisible(int position, int preeditCursor = 0, int preeditLength = 0); void updateHorizontalScroll(); void updateVerticalScroll(); bool determineHorizontalAlignment(); bool setHAlign(QQuickTextInput::HAlignment, bool forceAlign = false); void mirrorChange() override; bool sendMouseEventToInputContext(QMouseEvent *event); #if QT_CONFIG(im) Qt::InputMethodHints effectiveInputMethodHints() const; #endif void handleFocusEvent(QFocusEvent *event); struct MaskInputData { enum Casemode { NoCaseMode, Upper, Lower }; QChar maskChar; // either the separator char or the inputmask bool separator; Casemode caseMode; }; // undo/redo handling enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection, SetSelection }; struct Command { inline Command() {} inline Command(CommandType t, int p, QChar c, int ss, int se) : type(t),uc(c),pos(p),selStart(ss),selEnd(se) {} uint type : 4; QChar uc; int pos, selStart, selEnd; }; enum DrawFlags { DrawText = 0x01, DrawSelections = 0x02, DrawCursor = 0x04, DrawAll = DrawText | DrawSelections | DrawCursor }; QElapsedTimer tripleClickTimer; QSizeF contentSize; QPointF pressPos; QPointF tripleClickStartPoint; QPointer cursorComponent; #if QT_CONFIG(validator) QPointer m_validator; #endif qreal hscroll; qreal vscroll; QTextLayout m_textLayout; QString m_text; QString m_inputMask; QString m_cancelText; QFont font; QFont sourceFont; QQuickItem *cursorItem; QQuickTextNode *textNode; std::unique_ptr m_maskData; QInputControl *m_inputControl; QList m_transactions; QVector m_history; QColor color; QColor selectionColor; QColor selectedTextColor; QBasicTimer m_passwordEchoTimer; int lastSelectionStart; int lastSelectionEnd; int m_cursor; #if QT_CONFIG(im) int m_preeditCursor; #endif bool m_blinkEnabled; int m_blinkTimer; int m_maxLength; int m_lastCursorPos; int m_undoState; int m_selstart; int m_selend; enum UpdateType { UpdateNone, UpdateOnlyPreprocess, UpdatePaintNode }; #if QT_CONFIG(im) Qt::InputMethodHints inputMethodHints; #endif QQuickTextInput::HAlignment hAlign; QQuickTextInput::VAlignment vAlign; QQuickTextInput::WrapMode wrapMode; QQuickTextInput::EchoMode m_echoMode; QQuickTextInput::RenderType renderType; UpdateType updateType; QQuickTextInput::SelectionMode mouseSelectionMode; Qt::LayoutDirection m_layoutDirection; QChar m_blank; QChar m_passwordCharacter; int m_passwordMaskDelay; bool focusOnPress:1; bool cursorVisible:1; bool cursorPending:1; bool autoScroll:1; bool selectByMouse:1; bool canPaste:1; bool canPasteValid:1; bool canUndo:1; bool canRedo:1; bool hAlignImplicit:1; bool selectPressed:1; bool textLayoutDirty:1; bool persistentSelection:1; bool hasImState : 1; bool m_separator : 1; bool m_readOnly : 1; bool m_textDirty : 1; #if QT_CONFIG(im) bool m_preeditDirty : 1; #endif bool m_selDirty : 1; bool m_validInput : 1; bool m_acceptableInput : 1; bool m_blinkStatus : 1; bool m_passwordEchoEditing : 1; bool inLayout:1; bool requireImplicitWidth:1; bool overwriteMode:1; static inline QQuickTextInputPrivate *get(QQuickTextInput *t) { return t->d_func(); } bool hasPendingTripleClick() const { return !tripleClickTimer.hasExpired(QGuiApplication::styleHints()->mouseDoubleClickInterval()); } void setNativeCursorEnabled(bool) { updateCursorBlinking(); } int nextMaskBlank(int pos) { int c = findInMask(pos, true, false); m_separator |= (c != pos); return (c != -1 ? c : m_maxLength); } int prevMaskBlank(int pos) { int c = findInMask(pos, false, false); m_separator |= (c != pos); return (c != -1 ? c : 0); } bool isUndoAvailable() const { return !m_readOnly && m_undoState; } bool isRedoAvailable() const { return !m_readOnly && m_undoState < (int)m_history.size(); } void clearUndo() { m_history.clear(); m_undoState = 0; } bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); } bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; } void setSelection(int start, int length); inline QString selectedText() const { return hasSelectedText() ? m_text.mid(m_selstart, m_selend - m_selstart) : QString(); } QString textBeforeSelection() const { return hasSelectedText() ? m_text.left(m_selstart) : QString(); } QString textAfterSelection() const { return hasSelectedText() ? m_text.mid(m_selend) : QString(); } int selectionStart() const { return hasSelectedText() ? m_selstart : -1; } int selectionEnd() const { return hasSelectedText() ? m_selend : -1; } QRectF anchorRectangle() const; int positionAt(qreal x, qreal y, QTextLine::CursorPosition position) const; int positionAt(const QPointF &point, QTextLine::CursorPosition position = QTextLine::CursorBetweenCharacters) const { return positionAt(point.x(), point.y(), position); } void removeSelection() { int priorState = m_undoState; removeSelectedText(); finishChange(priorState); } int start() const { return 0; } int end() const { return m_text.length(); } QString realText() const; #if QT_CONFIG(clipboard) void copy(QClipboard::Mode mode = QClipboard::Clipboard) const; void paste(QClipboard::Mode mode = QClipboard::Clipboard); #endif #if QT_CONFIG(im) void commitPreedit(); void cancelPreedit(); #endif Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); } void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); } void moveCursor(int pos, bool mark = false); void cursorForward(bool mark, int steps) { int c = m_cursor; if (steps > 0) { while (steps--) c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.rightCursorPosition(c) : m_textLayout.nextCursorPosition(c); } else if (steps < 0) { while (steps++) c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.leftCursorPosition(c) : m_textLayout.previousCursorPosition(c); } moveCursor(c, mark); } void cursorWordForward(bool mark) { moveCursor(m_textLayout.nextCursorPosition(m_cursor, QTextLayout::SkipWords), mark); } void cursorWordBackward(bool mark) { moveCursor(m_textLayout.previousCursorPosition(m_cursor, QTextLayout::SkipWords), mark); } void home(bool mark) { moveCursor(0, mark); } void end(bool mark) { moveCursor(q_func()->text().length(), mark); } void backspace(); void del(); void deselect() { internalDeselect(); finishChange(); } void selectAll() { m_selstart = m_selend = m_cursor = 0; moveCursor(m_text.length(), true); } void insert(const QString &); void clear(); void selectWordAtPos(int); void setCursorPosition(int pos) { if (pos <= m_text.length()) moveCursor(qMax(0, pos)); } bool fixup(); QString inputMask() const { return m_maskData ? m_inputMask + QLatin1Char(';') + m_blank : QString(); } void setInputMask(const QString &mask) { parseInputMask(mask); if (m_maskData) moveCursor(nextMaskBlank(0)); } // input methods #if QT_CONFIG(im) bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); } QString preeditAreaText() const { return m_textLayout.preeditAreaText(); } #endif void updatePasswordEchoEditing(bool editing); void cancelPasswordEchoTimer() { m_passwordEchoTimer.stop(); } Qt::LayoutDirection textDirection() const; Qt::LayoutDirection layoutDirection() const; void setLayoutDirection(Qt::LayoutDirection direction) { if (direction != m_layoutDirection) { m_layoutDirection = direction; updateDisplayText(); } } #if QT_CONFIG(im) void processInputMethodEvent(QInputMethodEvent *event); #endif void processKeyEvent(QKeyEvent* ev); void setBlinkingCursorEnabled(bool enable); void updateCursorBlinking(); void updateLayout(); void updateBaselineOffset(); qreal calculateImplicitWidthForText(const QString &text) const; qreal getImplicitWidth() const override; inline qreal padding() const { return extra.isAllocated() ? extra->padding : 0.0; } void setTopPadding(qreal value, bool reset = false); void setLeftPadding(qreal value, bool reset = false); void setRightPadding(qreal value, bool reset = false); void setBottomPadding(qreal value, bool reset = false); bool isImplicitResizeEnabled() const; void setImplicitResizeEnabled(bool enabled); private: void removeSelectedText(); void internalSetText(const QString &txt, int pos = -1, bool edited = true); void updateDisplayText(bool forceUpdate = false); void internalInsert(const QString &s); void internalDelete(bool wasBackspace = false); void internalRemove(int pos); inline void internalDeselect() { m_selDirty |= (m_selend > m_selstart); m_selstart = m_selend = 0; } void internalUndo(int until = -1); void internalRedo(); void emitUndoRedoChanged(); bool emitCursorPositionChanged(); bool finishChange(int validateFromState = -1, bool update = false, bool edited = true); void addCommand(const Command& cmd); inline void separate() { m_separator = true; } bool separateSelection(); void deleteStartOfWord(); void deleteEndOfWord(); void deleteEndOfLine(); enum ValidatorState { #if QT_CONFIG(validator) InvalidInput = QValidator::Invalid, IntermediateInput = QValidator::Intermediate, AcceptableInput = QValidator::Acceptable #else InvalidInput, IntermediateInput, AcceptableInput #endif }; // masking void parseInputMask(const QString &maskFields); bool isValidInput(QChar key, QChar mask) const; ValidatorState hasAcceptableInput(const QString &text) const; void checkIsValid(); QString maskString(uint pos, const QString &str, bool clear = false) const; QString clearString(uint pos, uint len) const; QString stripString(const QString &str) const; int findInMask(int pos, bool forward, bool findSeparator, QChar searchChar = QChar()) const; }; QT_END_NAMESPACE #endif // QQUICKTEXTINPUT_P_P_H