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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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