1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QQUICKTEXTINPUT_P_P_H
41#define QQUICKTEXTINPUT_P_P_H
42
43#include "qquicktextinput_p.h"
44#include "qquicktext_p.h"
45#include "qquickimplicitsizeitem_p_p.h"
46#include "qquicktextutil_p.h"
47
48#include <QtQml/qqml.h>
49#include <QtCore/qelapsedtimer.h>
50#include <QtCore/qpointer.h>
51#include <QtCore/qbasictimer.h>
52#include <QtGui/qclipboard.h>
53#include <QtGui/qguiapplication.h>
54#include <QtGui/qpalette.h>
55#include <QtGui/qtextlayout.h>
56#include <QtGui/qstylehints.h>
57#include <private/qlazilyallocated_p.h>
58
59#include "qplatformdefs.h"
60
61#include <memory>
62
63//
64// W A R N I N G
65// -------------
66//
67// This file is not part of the Qt API. It exists purely as an
68// implementation detail. This header file may change from version to
69// version without notice, or even be removed.
70//
71// We mean it.
72
73QT_BEGIN_NAMESPACE
74
75class QQuickTextNode;
76class QInputControl;
77
78class Q_QUICK_PRIVATE_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPrivate
79{
80public:
81 Q_DECLARE_PUBLIC(QQuickTextInput)
82
83 typedef QQuickTextInput Public;
84
85 struct ExtraData {
86 ExtraData();
87
88 qreal padding;
89 qreal topPadding;
90 qreal leftPadding;
91 qreal rightPadding;
92 qreal bottomPadding;
93 bool explicitTopPadding : 1;
94 bool explicitLeftPadding : 1;
95 bool explicitRightPadding : 1;
96 bool explicitBottomPadding : 1;
97 bool implicitResize : 1;
98 };
99 QLazilyAllocated<ExtraData> extra;
100
101 QQuickTextInputPrivate()
102 : hscroll(0)
103 , vscroll(0)
104 , cursorItem(nullptr)
105 , textNode(nullptr)
106 , m_maskData(nullptr)
107 , color(QRgb(0xFF000000))
108 , selectionColor(QRgb(0xFF000080))
109 , selectedTextColor(QRgb(0xFFFFFFFF))
110 , m_cursor(0)
111#if QT_CONFIG(im)
112 , m_preeditCursor(0)
113 , m_undoPreeditState(-1)
114#endif
115 , m_blinkEnabled(false)
116 , m_blinkTimer(0)
117 , m_maxLength(32767)
118 , m_lastCursorPos(-1)
119 , m_undoState(0)
120 , m_selstart(0)
121 , m_selend(0)
122#if QT_CONFIG(im)
123 , inputMethodHints(Qt::ImhNone)
124#endif
125 , hAlign(QQuickTextInput::AlignLeft)
126 , vAlign(QQuickTextInput::AlignTop)
127 , wrapMode(QQuickTextInput::NoWrap)
128 , m_echoMode(QQuickTextInput::Normal)
129 , renderType(QQuickTextUtil::textRenderType<QQuickTextInput>())
130 , updateType(UpdatePaintNode)
131 , mouseSelectionMode(QQuickTextInput::SelectCharacters)
132 , m_layoutDirection(Qt::LayoutDirectionAuto)
133 , m_passwordCharacter(QGuiApplication::styleHints()->passwordMaskCharacter())
134 , m_passwordMaskDelay(QGuiApplication::styleHints()->passwordMaskDelay())
135 , focusOnPress(true)
136 , cursorVisible(false)
137 , cursorPending(false)
138 , autoScroll(true)
139 , selectByMouse(false)
140 , canPaste(false)
141 , canPasteValid(false)
142 , canUndo(false)
143 , canRedo(false)
144 , hAlignImplicit(true)
145 , selectPressed(false)
146 , textLayoutDirty(true)
147 , persistentSelection(false)
148 , hasImState(false)
149 , m_separator(0)
150 , m_readOnly(0)
151 , m_textDirty(0)
152#if QT_CONFIG(im)
153 , m_preeditDirty(0)
154#endif
155 , m_selDirty(0)
156 , m_validInput(1)
157 , m_acceptableInput(1)
158 , m_blinkStatus(0)
159 , m_passwordEchoEditing(false)
160 , inLayout(false)
161 , requireImplicitWidth(false)
162 , overwriteMode(false)
163 {
164 }
165
166 ~QQuickTextInputPrivate()
167 {
168 // If this control is used for password input, we don't want the
169 // password data to stay in the process memory, therefore we need
170 // to zero it out
171 if (m_echoMode != QQuickTextInput::Normal)
172 m_text.fill(c: 0);
173 }
174
175 void init();
176 void cancelInput();
177 void startCreatingCursor();
178 void ensureVisible(int position, int preeditCursor = 0, int preeditLength = 0);
179 void updateHorizontalScroll();
180 void updateVerticalScroll();
181 bool determineHorizontalAlignment();
182 bool setHAlign(QQuickTextInput::HAlignment, bool forceAlign = false);
183 void mirrorChange() override;
184 bool sendMouseEventToInputContext(QMouseEvent *event);
185#if QT_CONFIG(im)
186 Qt::InputMethodHints effectiveInputMethodHints() const;
187#endif
188 void handleFocusEvent(QFocusEvent *event);
189
190 struct MaskInputData {
191 enum Casemode { NoCaseMode, Upper, Lower };
192 QChar maskChar; // either the separator char or the inputmask
193 bool separator;
194 Casemode caseMode;
195 };
196
197 // undo/redo handling
198 enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection, SetSelection };
199 struct Command {
200 inline Command() {}
201 inline Command(CommandType t, int p, QChar c, int ss, int se) : type(t),uc(c),pos(p),selStart(ss),selEnd(se) {}
202 uint type : 4;
203 QChar uc;
204 int pos, selStart, selEnd;
205 };
206
207 enum DrawFlags {
208 DrawText = 0x01,
209 DrawSelections = 0x02,
210 DrawCursor = 0x04,
211 DrawAll = DrawText | DrawSelections | DrawCursor
212 };
213
214 QElapsedTimer tripleClickTimer;
215 QSizeF contentSize;
216 QPointF pressPos;
217 QPointF tripleClickStartPoint;
218
219 QPointer<QQmlComponent> cursorComponent;
220#if QT_CONFIG(validator)
221 QPointer<QValidator> m_validator;
222#endif
223
224 qreal hscroll;
225 qreal vscroll;
226
227 QTextLayout m_textLayout;
228 QString m_text;
229 QString m_inputMask;
230 QString m_cancelText;
231 QFont font;
232 QFont sourceFont;
233
234 QQuickItem *cursorItem;
235 QQuickTextNode *textNode;
236 std::unique_ptr<MaskInputData[]> m_maskData;
237 QInputControl *m_inputControl;
238
239 QList<int> m_transactions;
240 QVector<Command> m_history;
241
242 QColor color;
243 QColor selectionColor;
244 QColor selectedTextColor;
245
246 QBasicTimer m_passwordEchoTimer;
247 int lastSelectionStart;
248 int lastSelectionEnd;
249 int m_cursor;
250#if QT_CONFIG(im)
251 int m_preeditCursor;
252 int m_undoPreeditState;
253#endif
254 bool m_blinkEnabled;
255 int m_blinkTimer;
256 int m_maxLength;
257 int m_lastCursorPos;
258 int m_undoState;
259 int m_selstart;
260 int m_selend;
261
262 enum UpdateType {
263 UpdateNone,
264 UpdateOnlyPreprocess,
265 UpdatePaintNode
266 };
267
268#if QT_CONFIG(im)
269 Qt::InputMethodHints inputMethodHints;
270#endif
271 QQuickTextInput::HAlignment hAlign;
272 QQuickTextInput::VAlignment vAlign;
273 QQuickTextInput::WrapMode wrapMode;
274 QQuickTextInput::EchoMode m_echoMode;
275 QQuickTextInput::RenderType renderType;
276 UpdateType updateType;
277 QQuickTextInput::SelectionMode mouseSelectionMode;
278 Qt::LayoutDirection m_layoutDirection;
279
280 QChar m_blank;
281 QChar m_passwordCharacter;
282 int m_passwordMaskDelay;
283
284 bool focusOnPress:1;
285 bool cursorVisible:1;
286 bool cursorPending:1;
287 bool autoScroll:1;
288 bool selectByMouse:1;
289 bool canPaste:1;
290 bool canPasteValid:1;
291 bool canUndo:1;
292 bool canRedo:1;
293 bool hAlignImplicit:1;
294 bool selectPressed:1;
295 bool textLayoutDirty:1;
296 bool persistentSelection:1;
297 bool hasImState : 1;
298 bool m_separator : 1;
299 bool m_readOnly : 1;
300 bool m_textDirty : 1;
301#if QT_CONFIG(im)
302 bool m_preeditDirty : 1;
303#endif
304 bool m_selDirty : 1;
305 bool m_validInput : 1;
306 bool m_acceptableInput : 1;
307 bool m_blinkStatus : 1;
308 bool m_passwordEchoEditing : 1;
309 bool inLayout:1;
310 bool requireImplicitWidth:1;
311 bool overwriteMode:1;
312
313 static inline QQuickTextInputPrivate *get(QQuickTextInput *t) {
314 return t->d_func();
315 }
316 bool hasPendingTripleClick() const {
317 return !tripleClickTimer.hasExpired(timeout: QGuiApplication::styleHints()->mouseDoubleClickInterval());
318 }
319
320 void setNativeCursorEnabled(bool) {
321 updateCursorBlinking();
322 }
323
324 int nextMaskBlank(int pos)
325 {
326 int c = findInMask(pos, forward: true, findSeparator: false);
327 m_separator |= (c != pos);
328 return (c != -1 ? c : m_maxLength);
329 }
330
331 int prevMaskBlank(int pos)
332 {
333 int c = findInMask(pos, forward: false, findSeparator: false);
334 m_separator |= (c != pos);
335 return (c != -1 ? c : 0);
336 }
337
338 bool isUndoAvailable() const { return !m_readOnly && m_undoState; }
339 bool isRedoAvailable() const { return !m_readOnly && m_undoState < (int)m_history.size(); }
340 void clearUndo() {
341 m_history.clear();
342 m_undoState = 0;
343#if QT_CONFIG(im)
344 m_undoPreeditState = -1;
345#endif
346 }
347
348 bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); }
349 bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; }
350
351 void setSelection(int start, int length);
352
353 inline QString selectedText() const { return hasSelectedText() ? m_text.mid(position: m_selstart, n: m_selend - m_selstart) : QString(); }
354 QString textBeforeSelection() const { return hasSelectedText() ? m_text.left(n: m_selstart) : QString(); }
355 QString textAfterSelection() const { return hasSelectedText() ? m_text.mid(position: m_selend) : QString(); }
356
357 int selectionStart() const { return hasSelectedText() ? m_selstart : -1; }
358 int selectionEnd() const { return hasSelectedText() ? m_selend : -1; }
359
360 QRectF anchorRectangle() const;
361
362 int positionAt(qreal x, qreal y, QTextLine::CursorPosition position) const;
363 int positionAt(const QPointF &point, QTextLine::CursorPosition position = QTextLine::CursorBetweenCharacters) const {
364 return positionAt(x: point.x(), y: point.y(), position);
365 }
366
367 void removeSelection()
368 {
369 int priorState = m_undoState;
370 removeSelectedText();
371 finishChange(validateFromState: priorState);
372 }
373
374 int start() const { return 0; }
375 int end() const { return m_text.length(); }
376
377 QString realText() const;
378
379#if QT_CONFIG(clipboard)
380 void copy(QClipboard::Mode mode = QClipboard::Clipboard) const;
381 void paste(QClipboard::Mode mode = QClipboard::Clipboard);
382#endif
383
384#if QT_CONFIG(im)
385 void commitPreedit();
386 void cancelPreedit();
387#endif
388
389 Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); }
390 void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); }
391
392 void moveCursor(int pos, bool mark = false);
393 void cursorForward(bool mark, int steps)
394 {
395 int c = m_cursor;
396 if (steps > 0) {
397 while (steps--)
398 c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.rightCursorPosition(oldPos: c)
399 : m_textLayout.nextCursorPosition(oldPos: c);
400 } else if (steps < 0) {
401 while (steps++)
402 c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.leftCursorPosition(oldPos: c)
403 : m_textLayout.previousCursorPosition(oldPos: c);
404 }
405 moveCursor(pos: c, mark);
406 }
407
408 void cursorWordForward(bool mark) { moveCursor(pos: m_textLayout.nextCursorPosition(oldPos: m_cursor, mode: QTextLayout::SkipWords), mark); }
409 void cursorWordBackward(bool mark) { moveCursor(pos: m_textLayout.previousCursorPosition(oldPos: m_cursor, mode: QTextLayout::SkipWords), mark); }
410
411 void home(bool mark) { moveCursor(pos: 0, mark); }
412 void end(bool mark) { moveCursor(pos: q_func()->text().length(), mark); }
413
414 void backspace();
415 void del();
416 void deselect() { internalDeselect(); finishChange(); }
417 void selectAll() { m_selstart = m_selend = m_cursor = 0; moveCursor(pos: m_text.length(), mark: true); }
418
419 void insert(const QString &);
420 void clear();
421 void selectWordAtPos(int);
422
423 void setCursorPosition(int pos) { if (pos <= m_text.length()) moveCursor(pos: qMax(a: 0, b: pos)); }
424
425 bool fixup();
426
427 QString inputMask() const { return m_maskData ? m_inputMask + QLatin1Char(';') + m_blank : QString(); }
428 void setInputMask(const QString &mask)
429 {
430 parseInputMask(maskFields: mask);
431 if (m_maskData)
432 moveCursor(pos: nextMaskBlank(pos: 0));
433 }
434
435 // input methods
436#if QT_CONFIG(im)
437 bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); }
438
439 QString preeditAreaText() const { return m_textLayout.preeditAreaText(); }
440#endif
441
442 void updatePasswordEchoEditing(bool editing);
443
444 void cancelPasswordEchoTimer() {
445 m_passwordEchoTimer.stop();
446 }
447
448 Qt::LayoutDirection textDirection() const;
449 Qt::LayoutDirection layoutDirection() const;
450 void setLayoutDirection(Qt::LayoutDirection direction)
451 {
452 if (direction != m_layoutDirection) {
453 m_layoutDirection = direction;
454 updateDisplayText();
455 }
456 }
457
458#if QT_CONFIG(im)
459 void processInputMethodEvent(QInputMethodEvent *event);
460#endif
461 void processKeyEvent(QKeyEvent* ev);
462
463 void setBlinkingCursorEnabled(bool enable);
464 void updateCursorBlinking();
465
466 void updateLayout();
467 void updateBaselineOffset();
468
469 qreal getImplicitWidth() const override;
470
471 inline qreal padding() const { return extra.isAllocated() ? extra->padding : 0.0; }
472 void setTopPadding(qreal value, bool reset = false);
473 void setLeftPadding(qreal value, bool reset = false);
474 void setRightPadding(qreal value, bool reset = false);
475 void setBottomPadding(qreal value, bool reset = false);
476
477 bool isImplicitResizeEnabled() const;
478 void setImplicitResizeEnabled(bool enabled);
479
480private:
481 void removeSelectedText();
482 void internalSetText(const QString &txt, int pos = -1, bool edited = true);
483 void updateDisplayText(bool forceUpdate = false);
484
485 void internalInsert(const QString &s);
486 void internalDelete(bool wasBackspace = false);
487 void internalRemove(int pos);
488
489 inline void internalDeselect()
490 {
491 m_selDirty |= (m_selend > m_selstart);
492 m_selstart = m_selend = 0;
493 }
494
495 void internalUndo(int until = -1);
496 void internalRedo();
497 void emitUndoRedoChanged();
498
499 bool emitCursorPositionChanged();
500
501 bool finishChange(int validateFromState = -1, bool update = false, bool edited = true);
502
503 void addCommand(const Command& cmd);
504
505 inline void separate() { m_separator = true; }
506
507 bool separateSelection();
508 void deleteStartOfWord();
509 void deleteEndOfWord();
510 void deleteEndOfLine();
511
512 enum ValidatorState {
513#if QT_CONFIG(validator)
514 InvalidInput = QValidator::Invalid,
515 IntermediateInput = QValidator::Intermediate,
516 AcceptableInput = QValidator::Acceptable
517#else
518 InvalidInput,
519 IntermediateInput,
520 AcceptableInput
521#endif
522 };
523
524 // masking
525 void parseInputMask(const QString &maskFields);
526 bool isValidInput(QChar key, QChar mask) const;
527 ValidatorState hasAcceptableInput(const QString &text) const;
528 void checkIsValid();
529 QString maskString(uint pos, const QString &str, bool clear = false) const;
530 QString clearString(uint pos, uint len) const;
531 QString stripString(const QString &str) const;
532 int findInMask(int pos, bool forward, bool findSeparator, QChar searchChar = QChar()) const;
533};
534
535QT_END_NAMESPACE
536
537#endif // QQUICKTEXTINPUT_P_P_H
538

source code of qtdeclarative/src/quick/items/qquicktextinput_p_p.h