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