aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew den Exter <andrew.den-exter@nokia.com>2011-11-11 13:47:59 +1000
committerQt by Nokia <qt-info@nokia.com>2011-12-16 00:53:17 +0100
commite7fb84adb5d5d9cc5c61db3bbd025f206854bcb4 (patch)
treef5e1d922b8cc0a3ffe01358efdddd15a93867d30 /src
parentd70cca804e88110eae32f92baff7cee957a6a531 (diff)
Merge QQuickLineControl into QQuickTextInput.
There's no clear separation of responsibilty between these classes and keeping them in sync and forwarding signals is a unnecessary overhead that can be avoided by combining them. Task-number: QTBUG-22627 Change-Id: I4350eb3c612b10d4ed34886374889ae893b8183a Reviewed-by: Martin Jones <martin.jones@nokia.com>
Diffstat (limited to 'src')
-rw-r--r--src/quick/items/items.pri2
-rw-r--r--src/quick/items/qquicklinecontrol.cpp1517
-rw-r--r--src/quick/items/qquicklinecontrol_p.h449
-rw-r--r--src/quick/items/qquicktextinput.cpp1791
-rw-r--r--src/quick/items/qquicktextinput_p.h2
-rw-r--r--src/quick/items/qquicktextinput_p_p.h341
6 files changed, 1896 insertions, 2206 deletions
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index ec1de05b36..85ef591ab9 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -16,7 +16,6 @@ HEADERS += \
$$PWD/qquicktext_p.h \
$$PWD/qquicktext_p_p.h \
$$PWD/qquicktextnode_p.h \
- $$PWD/qquicklinecontrol_p.h \
$$PWD/qquicktextinput_p.h \
$$PWD/qquicktextinput_p_p.h \
$$PWD/qquicktextcontrol_p.h \
@@ -85,7 +84,6 @@ SOURCES += \
$$PWD/qquickpainteditem.cpp \
$$PWD/qquicktext.cpp \
$$PWD/qquicktextnode.cpp \
- $$PWD/qquicklinecontrol.cpp \
$$PWD/qquicktextinput.cpp \
$$PWD/qquicktextcontrol.cpp \
$$PWD/qquicktextedit.cpp \
diff --git a/src/quick/items/qquicklinecontrol.cpp b/src/quick/items/qquicklinecontrol.cpp
deleted file mode 100644
index 6daf4b1284..0000000000
--- a/src/quick/items/qquicklinecontrol.cpp
+++ /dev/null
@@ -1,1517 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** GNU Lesser General Public License Usage
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this
-** file. Please review the following information to ensure the GNU Lesser
-** General Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU General
-** Public License version 3.0 as published by the Free Software Foundation
-** and appearing in the file LICENSE.GPL included in the packaging of this
-** file. Please review the following information to ensure the GNU General
-** Public License version 3.0 requirements will be met:
-** http://www.gnu.org/copyleft/gpl.html.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// ###
-#define QT_NO_ACCESSIBILITY
-
-#include "qquicklinecontrol_p.h"
-
-#ifndef QT_NO_LINEEDIT
-
-#include "qclipboard.h"
-#ifndef QT_NO_ACCESSIBILITY
-#include "qaccessible.h"
-#endif
-#include "qguiapplication.h"
-#include "qstylehints.h"
-
-QT_BEGIN_NAMESPACE
-
-// ### these should come from QStyleHints
-const int textCursorWidth = 1;
-const bool fullWidthSelection = true;
-
-/*!
- \internal
-
- Updates the display text based of the current edit text
- If the text has changed will emit displayTextChanged()
-*/
-void QQuickLineControl::updateDisplayText(bool forceUpdate)
-{
- QString orig = m_textLayout.text();
- QString str;
- if (m_echoMode == NoEcho)
- str = QString::fromLatin1("");
- else
- str = m_text;
-
- if (m_echoMode == Password || (m_echoMode == PasswordEchoOnEdit
- && !m_passwordEchoEditing))
- str.fill(m_passwordCharacter);
-
- // replace certain non-printable characters with spaces (to avoid
- // drawing boxes when using fonts that don't have glyphs for such
- // characters)
- QChar* uc = str.data();
- for (int i = 0; i < (int)str.length(); ++i) {
- if ((uc[i] < 0x20 && uc[i] != 0x09)
- || uc[i] == QChar::LineSeparator
- || uc[i] == QChar::ParagraphSeparator
- || uc[i] == QChar::ObjectReplacementCharacter)
- uc[i] = QChar(0x0020);
- }
-
- m_textLayout.setText(str);
-
- QTextOption option = m_textLayout.textOption();
- option.setTextDirection(m_layoutDirection);
- option.setFlags(QTextOption::IncludeTrailingSpaces);
- m_textLayout.setTextOption(option);
-
- m_textLayout.beginLayout();
- QTextLine l = m_textLayout.createLine();
- m_textLayout.endLayout();
- m_ascent = qRound(l.ascent());
-
- if (str != orig || forceUpdate)
- emit displayTextChanged(str);
-}
-
-#ifndef QT_NO_CLIPBOARD
-/*!
- \internal
-
- Copies the currently selected text into the clipboard using the given
- \a mode.
-
- \note If the echo mode is set to a mode other than Normal then copy
- will not work. This is to prevent using copy as a method of bypassing
- password features of the line control.
-*/
-void QQuickLineControl::copy(QClipboard::Mode mode) const
-{
- QString t = selectedText();
- if (!t.isEmpty() && m_echoMode == Normal) {
- disconnect(QGuiApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
- QGuiApplication::clipboard()->setText(t, mode);
- connect(QGuiApplication::clipboard(), SIGNAL(selectionChanged()),
- this, SLOT(_q_clipboardChanged()));
- }
-}
-
-/*!
- \internal
-
- Inserts the text stored in the application clipboard into the line
- control.
-
- \sa insert()
-*/
-void QQuickLineControl::paste(QClipboard::Mode clipboardMode)
-{
- QString clip = QGuiApplication::clipboard()->text(clipboardMode);
- if (!clip.isEmpty() || hasSelectedText()) {
- separate(); //make it a separate undo/redo command
- insert(clip);
- separate();
- }
-}
-
-#endif // !QT_NO_CLIPBOARD
-
-/*!
- \internal
-
- Exits preedit mode and commits parts marked as tentative commit
-*/
-void QQuickLineControl::commitPreedit()
-{
- if (!composeMode())
- return;
-
- qApp->inputPanel()->reset();
-
- if (!m_tentativeCommit.isEmpty()) {
- internalInsert(m_tentativeCommit);
- m_tentativeCommit.clear();
- finishChange(-1, true/*not used, not documented*/, false);
- }
-
- m_preeditCursor = 0;
- setPreeditArea(-1, QString());
- m_textLayout.clearAdditionalFormats();
- updateDisplayText(/*force*/ true);
-}
-
-/*!
- \internal
-
- Handles the behavior for the backspace key or function.
- Removes the current selection if there is a selection, otherwise
- removes the character prior to the cursor position.
-
- \sa del()
-*/
-void QQuickLineControl::backspace()
-{
- int priorState = m_undoState;
- if (hasSelectedText()) {
- removeSelectedText();
- } else if (m_cursor) {
- --m_cursor;
- if (m_maskData)
- m_cursor = prevMaskBlank(m_cursor);
- QChar uc = m_text.at(m_cursor);
- if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
- // second half of a surrogate, check if we have the first half as well,
- // if yes delete both at once
- uc = m_text.at(m_cursor - 1);
- if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) {
- internalDelete(true);
- --m_cursor;
- }
- }
- internalDelete(true);
- }
- finishChange(priorState);
-}
-
-/*!
- \internal
-
- Handles the behavior for the delete key or function.
- Removes the current selection if there is a selection, otherwise
- removes the character after the cursor position.
-
- \sa del()
-*/
-void QQuickLineControl::del()
-{
- int priorState = m_undoState;
- if (hasSelectedText()) {
- removeSelectedText();
- } else {
- int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
- while (n--)
- internalDelete();
- }
- finishChange(priorState);
-}
-
-/*!
- \internal
-
- Inserts the given \a newText at the current cursor position.
- If there is any selected text it is removed prior to insertion of
- the new text.
-*/
-void QQuickLineControl::insert(const QString &newText)
-{
- int priorState = m_undoState;
- removeSelectedText();
- internalInsert(newText);
- finishChange(priorState);
-}
-
-/*!
- \internal
-
- Clears the line control text.
-*/
-void QQuickLineControl::clear()
-{
- int priorState = m_undoState;
- m_selstart = 0;
- m_selend = m_text.length();
- removeSelectedText();
- separate();
- finishChange(priorState, /*update*/false, /*edited*/false);
-}
-
-/*!
- \internal
-
- Sets \a length characters from the given \a start position as selected.
- The given \a start position must be within the current text for
- the line control. If \a length characters cannot be selected, then
- the selection will extend to the end of the current text.
-*/
-void QQuickLineControl::setSelection(int start, int length)
-{
- commitPreedit();
-
- if (start < 0 || start > (int)m_text.length()){
- qWarning("QQuickLineControl::setSelection: Invalid start position");
- return;
- }
-
- if (length > 0) {
- if (start == m_selstart && start + length == m_selend && m_cursor == m_selend)
- return;
- m_selstart = start;
- m_selend = qMin(start + length, (int)m_text.length());
- m_cursor = m_selend;
- } else if (length < 0){
- if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart)
- return;
- m_selstart = qMax(start + length, 0);
- m_selend = start;
- m_cursor = m_selstart;
- } else if (m_selstart != m_selend) {
- m_selstart = 0;
- m_selend = 0;
- m_cursor = start;
- } else {
- m_cursor = start;
- emitCursorPositionChanged();
- return;
- }
- emit selectionChanged();
- emitCursorPositionChanged();
-}
-
-void QQuickLineControl::_q_clipboardChanged()
-{
-}
-
-void QQuickLineControl::_q_deleteSelected()
-{
- if (!hasSelectedText())
- return;
-
- int priorState = m_undoState;
- emit resetInputContext();
- removeSelectedText();
- separate();
- finishChange(priorState);
-}
-
-/*!
- \internal
-
- Initializes the line control with a starting text value of \a txt.
-*/
-void QQuickLineControl::init(const QString &txt)
-{
- m_text = txt;
-
- updateDisplayText();
- m_cursor = m_text.length();
-}
-
-/*!
- \internal
-
- Sets the password echo editing to \a editing. If password echo editing
- is true, then the text of the password is displayed even if the echo
- mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing
- does not affect other echo modes.
-*/
-void QQuickLineControl::updatePasswordEchoEditing(bool editing)
-{
- m_passwordEchoEditing = editing;
- updateDisplayText();
-}
-
-/*!
- \internal
-
- Returns the cursor position of the given \a x pixel value in relation
- to the displayed text. The given \a betweenOrOn specified what kind
- of cursor position is requested.
-*/
-int QQuickLineControl::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
-{
- return m_textLayout.lineAt(0).xToCursor(x, betweenOrOn);
-}
-
-/*!
- \internal
-
- Returns the bounds of the current cursor, as defined as a
- between characters cursor.
-*/
-QRect QQuickLineControl::cursorRect() const
-{
- QTextLine l = m_textLayout.lineAt(0);
- int c = m_cursor;
- if (m_preeditCursor != -1)
- c += m_preeditCursor;
- int cix = qRound(l.cursorToX(c));
- int w = m_cursorWidth;
- int ch = l.height() + 1;
-
- return QRect(cix-5, 0, w+9, ch);
-}
-
-QString QQuickLineControl::text() const
-{
- QString content = m_text;
- if (!m_tentativeCommit.isEmpty())
- content.insert(m_cursor, m_tentativeCommit);
- QString res = m_maskData ? stripString(content) : content;
- return (res.isNull() ? QString::fromLatin1("") : res);
-}
-
-// like text() but doesn't include preedit
-QString QQuickLineControl::realText() const
-{
- QString res = m_maskData ? stripString(m_text) : m_text;
- return (res.isNull() ? QString::fromLatin1("") : res);
-}
-
-void QQuickLineControl::setText(const QString &txt)
-{
- if (composeMode())
- qApp->inputPanel()->reset();
- m_tentativeCommit.clear();
- internalSetText(txt, -1, false);
-}
-
-/*!
- \internal
-
- Fixes the current text so that it is valid given any set validators.
-
- Returns true if the text was changed. Otherwise returns false.
-*/
-bool QQuickLineControl::fixup() // this function assumes that validate currently returns != Acceptable
-{
-#ifndef QT_NO_VALIDATOR
- if (m_validator) {
- QString textCopy = m_text;
- int cursorCopy = m_cursor;
- m_validator->fixup(textCopy);
- if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
- if (textCopy != m_text || cursorCopy != m_cursor)
- internalSetText(textCopy, cursorCopy);
- return true;
- }
- }
-#endif
- return false;
-}
-
-/*!
- \internal
-
- Moves the cursor to the given position \a pos. If \a mark is true will
- adjust the currently selected text.
-*/
-void QQuickLineControl::moveCursor(int pos, bool mark)
-{
- commitPreedit();
-
- if (pos != m_cursor) {
- separate();
- if (m_maskData)
- pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
- }
- if (mark) {
- int anchor;
- if (m_selend > m_selstart && m_cursor == m_selstart)
- anchor = m_selend;
- else if (m_selend > m_selstart && m_cursor == m_selend)
- anchor = m_selstart;
- else
- anchor = m_cursor;
- m_selstart = qMin(anchor, pos);
- m_selend = qMax(anchor, pos);
- updateDisplayText();
- } else {
- internalDeselect();
- }
- m_cursor = pos;
- if (mark || m_selDirty) {
- m_selDirty = false;
- emit selectionChanged();
- }
- emitCursorPositionChanged();
-}
-
-/*!
- \internal
-
- Applies the given input method event \a event to the text of the line
- control
-*/
-void QQuickLineControl::processInputMethodEvent(QInputMethodEvent *event)
-{
- int priorState = -1;
- bool isGettingInput = !event->commitString().isEmpty()
- || event->preeditString() != preeditAreaText()
- || event->replacementLength() > 0;
- bool cursorPositionChanged = false;
- bool selectionChange = false;
-
- if (isGettingInput) {
- // If any text is being input, remove selected text.
- priorState = m_undoState;
- if (echoMode() == PasswordEchoOnEdit && !passwordEchoEditing()) {
- updatePasswordEchoEditing(true);
- m_selstart = 0;
- m_selend = m_text.length();
- }
- removeSelectedText();
- }
-
- int c = m_cursor; // cursor position after insertion of commit string
- if (event->replacementStart() <= 0)
- c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength());
-
- m_cursor += event->replacementStart();
- if (m_cursor < 0)
- m_cursor = 0;
-
- // insert commit string
- if (event->replacementLength()) {
- m_selstart = m_cursor;
- m_selend = m_selstart + event->replacementLength();
- removeSelectedText();
- }
- if (!event->commitString().isEmpty()) {
- internalInsert(event->commitString());
- cursorPositionChanged = true;
- }
-
- m_cursor = qBound(0, c, m_text.length());
-
- for (int i = 0; i < event->attributes().size(); ++i) {
- const QInputMethodEvent::Attribute &a = event->attributes().at(i);
- if (a.type == QInputMethodEvent::Selection) {
- m_cursor = qBound(0, a.start + a.length, m_text.length());
- if (a.length) {
- m_selstart = qMax(0, qMin(a.start, m_text.length()));
- m_selend = m_cursor;
- if (m_selend < m_selstart) {
- qSwap(m_selstart, m_selend);
- }
- selectionChange = true;
- } else {
- m_selstart = m_selend = 0;
- }
- cursorPositionChanged = true;
- }
- }
-#ifndef QT_NO_IM
- setPreeditArea(m_cursor, event->preeditString());
-#endif //QT_NO_IM
- const int oldPreeditCursor = m_preeditCursor;
- m_preeditCursor = event->preeditString().length();
- m_hideCursor = false;
- QList<QTextLayout::FormatRange> formats;
- for (int i = 0; i < event->attributes().size(); ++i) {
- const QInputMethodEvent::Attribute &a = event->attributes().at(i);
- if (a.type == QInputMethodEvent::Cursor) {
- m_preeditCursor = a.start;
- m_hideCursor = !a.length;
- } else if (a.type == QInputMethodEvent::TextFormat) {
- QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
- if (f.isValid()) {
- QTextLayout::FormatRange o;
- o.start = a.start + m_cursor;
- o.length = a.length;
- o.format = f;
- formats.append(o);
- }
- }
- }
- m_textLayout.setAdditionalFormats(formats);
- updateDisplayText(/*force*/ true);
- if (cursorPositionChanged)
- emitCursorPositionChanged();
- else if (m_preeditCursor != oldPreeditCursor)
- emit updateMicroFocus();
-
- bool tentativeCommitChanged = (m_tentativeCommit != event->tentativeCommitString());
-
- if (tentativeCommitChanged) {
- m_textDirty = true;
- m_tentativeCommit = event->tentativeCommitString();
- }
-
- if (isGettingInput || tentativeCommitChanged)
- finishChange(priorState);
-
- if (selectionChange)
- emit selectionChanged();
-}
-
-/*!
- \internal
-
- Sets the selection to cover the word at the given cursor position.
- The word boundaries are defined by the behavior of QTextLayout::SkipWords
- cursor mode.
-*/
-void QQuickLineControl::selectWordAtPos(int cursor)
-{
- int next = cursor + 1;
- if (next > end())
- --next;
- int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords);
- moveCursor(c, false);
- // ## text layout should support end of words.
- int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords);
- while (end > cursor && m_text[end-1].isSpace())
- --end;
- moveCursor(end, true);
-}
-
-/*!
- \internal
-
- Completes a change to the line control text. If the change is not valid
- will undo the line control state back to the given \a validateFromState.
-
- If \a edited is true and the change is valid, will emit textEdited() in
- addition to textChanged(). Otherwise only emits textChanged() on a valid
- change.
-
- The \a update value is currently unused.
-*/
-bool QQuickLineControl::finishChange(int validateFromState, bool update, bool edited)
-{
- Q_UNUSED(update)
-
- if (m_textDirty) {
- // do validation
- bool wasValidInput = m_validInput;
- m_validInput = true;
-#ifndef QT_NO_VALIDATOR
- if (m_validator) {
- QString textCopy = m_text;
- int cursorCopy = m_cursor;
- m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
- if (m_validInput) {
- if (m_text != textCopy) {
- internalSetText(textCopy, cursorCopy);
- return true;
- }
- m_cursor = cursorCopy;
-
- if (!m_tentativeCommit.isEmpty()) {
- textCopy.insert(m_cursor, m_tentativeCommit);
- bool validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
- if (!validInput)
- m_tentativeCommit.clear();
- }
- } else {
- m_tentativeCommit.clear();
- }
- }
-#endif
- if (validateFromState >= 0 && wasValidInput && !m_validInput) {
- if (m_transactions.count())
- return false;
- internalUndo(validateFromState);
- m_history.resize(m_undoState);
- if (m_modifiedState > m_undoState)
- m_modifiedState = -1;
- m_validInput = true;
- m_textDirty = false;
- }
- updateDisplayText();
-
- if (m_textDirty) {
- m_textDirty = false;
- QString actualText = text();
- if (edited)
- emit textEdited(actualText);
- emit textChanged(actualText);
- }
- }
- if (m_selDirty) {
- m_selDirty = false;
- emit selectionChanged();
- }
- emitCursorPositionChanged();
- return true;
-}
-
-/*!
- \internal
-
- An internal function for setting the text of the line control.
-*/
-void QQuickLineControl::internalSetText(const QString &txt, int pos, bool edited)
-{
- internalDeselect();
- emit resetInputContext();
- QString oldText = m_text;
- if (m_maskData) {
- m_text = maskString(0, txt, true);
- m_text += clearString(m_text.length(), m_maxLength - m_text.length());
- } else {
- m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
- }
- m_history.clear();
- m_modifiedState = m_undoState = 0;
- m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
- m_textDirty = (oldText != m_text);
-
- bool changed = finishChange(-1, true, edited);
-#ifdef QT_NO_ACCESSIBILITY
- Q_UNUSED(changed)
-#else
- if (changed)
- QAccessible::updateAccessibility(parent(), 0, QAccessible::TextUpdated);
-#endif
-}
-
-
-/*!
- \internal
-
- Adds the given \a command to the undo history
- of the line control. Does not apply the command.
-*/
-void QQuickLineControl::addCommand(const Command &cmd)
-{
- if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) {
- m_history.resize(m_undoState + 2);
- m_history[m_undoState++] = Command(Separator, m_cursor, 0, m_selstart, m_selend);
- } else {
- m_history.resize(m_undoState + 1);
- }
- m_separator = false;
- m_history[m_undoState++] = cmd;
-}
-
-/*!
- \internal
-
- Inserts the given string \a s into the line
- control.
-
- Also adds the appropriate commands into the undo history.
- This function does not call finishChange(), and may leave the text
- in an invalid state.
-*/
-void QQuickLineControl::internalInsert(const QString &s)
-{
- if (hasSelectedText())
- addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
- if (m_maskData) {
- QString ms = maskString(m_cursor, s);
- for (int i = 0; i < (int) ms.length(); ++i) {
- addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
- addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
- }
- m_text.replace(m_cursor, ms.length(), ms);
- m_cursor += ms.length();
- m_cursor = nextMaskBlank(m_cursor);
- m_textDirty = true;
- } else {
- int remaining = m_maxLength - m_text.length();
- if (remaining != 0) {
- m_text.insert(m_cursor, s.left(remaining));
- for (int i = 0; i < (int) s.left(remaining).length(); ++i)
- addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1));
- m_textDirty = true;
- }
- }
-}
-
-/*!
- \internal
-
- deletes a single character from the current text. If \a wasBackspace,
- the character prior to the cursor is removed. Otherwise the character
- after the cursor is removed.
-
- Also adds the appropriate commands into the undo history.
- This function does not call finishChange(), and may leave the text
- in an invalid state.
-*/
-void QQuickLineControl::internalDelete(bool wasBackspace)
-{
- if (m_cursor < (int) m_text.length()) {
- if (hasSelectedText())
- addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
- addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
- m_cursor, m_text.at(m_cursor), -1, -1));
- if (m_maskData) {
- m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
- addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
- } else {
- m_text.remove(m_cursor, 1);
- }
- m_textDirty = true;
- }
-}
-
-/*!
- \internal
-
- removes the currently selected text from the line control.
-
- Also adds the appropriate commands into the undo history.
- This function does not call finishChange(), and may leave the text
- in an invalid state.
-*/
-void QQuickLineControl::removeSelectedText()
-{
- if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
- separate();
- int i ;
- addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
- if (m_selstart <= m_cursor && m_cursor < m_selend) {
- // cursor is within the selection. Split up the commands
- // to be able to restore the correct cursor position
- for (i = m_cursor; i >= m_selstart; --i)
- addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
- for (i = m_selend - 1; i > m_cursor; --i)
- addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
- } else {
- for (i = m_selend-1; i >= m_selstart; --i)
- addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
- }
- if (m_maskData) {
- m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart));
- for (int i = 0; i < m_selend - m_selstart; ++i)
- addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
- } else {
- m_text.remove(m_selstart, m_selend - m_selstart);
- }
- if (m_cursor > m_selstart)
- m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
- internalDeselect();
- m_textDirty = true;
- }
-}
-
-/*!
- \internal
-
- Parses the input mask specified by \a maskFields to generate
- the mask data used to handle input masks.
-*/
-void QQuickLineControl::parseInputMask(const QString &maskFields)
-{
- int delimiter = maskFields.indexOf(QLatin1Char(';'));
- if (maskFields.isEmpty() || delimiter == 0) {
- if (m_maskData) {
- delete [] m_maskData;
- m_maskData = 0;
- m_maxLength = 32767;
- internalSetText(QString());
- }
- return;
- }
-
- if (delimiter == -1) {
- m_blank = QLatin1Char(' ');
- m_inputMask = maskFields;
- } else {
- m_inputMask = maskFields.left(delimiter);
- m_blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' ');
- }
-
- // calculate m_maxLength / m_maskData length
- m_maxLength = 0;
- QChar c = 0;
- for (int i=0; i<m_inputMask.length(); i++) {
- c = m_inputMask.at(i);
- if (i > 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) {
- m_maxLength++;
- continue;
- }
- if (c != QLatin1Char('\\') && c != QLatin1Char('!') &&
- c != QLatin1Char('<') && c != QLatin1Char('>') &&
- c != QLatin1Char('{') && c != QLatin1Char('}') &&
- c != QLatin1Char('[') && c != QLatin1Char(']'))
- m_maxLength++;
- }
-
- delete [] m_maskData;
- m_maskData = new MaskInputData[m_maxLength];
-
- MaskInputData::Casemode m = MaskInputData::NoCaseMode;
- c = 0;
- bool s;
- bool escape = false;
- int index = 0;
- for (int i = 0; i < m_inputMask.length(); i++) {
- c = m_inputMask.at(i);
- if (escape) {
- s = true;
- m_maskData[index].maskChar = c;
- m_maskData[index].separator = s;
- m_maskData[index].caseMode = m;
- index++;
- escape = false;
- } else if (c == QLatin1Char('<')) {
- m = MaskInputData::Lower;
- } else if (c == QLatin1Char('>')) {
- m = MaskInputData::Upper;
- } else if (c == QLatin1Char('!')) {
- m = MaskInputData::NoCaseMode;
- } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) {
- switch (c.unicode()) {
- case 'A':
- case 'a':
- case 'N':
- case 'n':
- case 'X':
- case 'x':
- case '9':
- case '0':
- case 'D':
- case 'd':
- case '#':
- case 'H':
- case 'h':
- case 'B':
- case 'b':
- s = false;
- break;
- case '\\':
- escape = true;
- default:
- s = true;
- break;
- }
-
- if (!escape) {
- m_maskData[index].maskChar = c;
- m_maskData[index].separator = s;
- m_maskData[index].caseMode = m;
- index++;
- }
- }
- }
- internalSetText(m_text);
-}
-
-
-/*!
- \internal
-
- checks if the key is valid compared to the inputMask
-*/
-bool QQuickLineControl::isValidInput(QChar key, QChar mask) const
-{
- switch (mask.unicode()) {
- case 'A':
- if (key.isLetter())
- return true;
- break;
- case 'a':
- if (key.isLetter() || key == m_blank)
- return true;
- break;
- case 'N':
- if (key.isLetterOrNumber())
- return true;
- break;
- case 'n':
- if (key.isLetterOrNumber() || key == m_blank)
- return true;
- break;
- case 'X':
- if (key.isPrint())
- return true;
- break;
- case 'x':
- if (key.isPrint() || key == m_blank)
- return true;
- break;
- case '9':
- if (key.isNumber())
- return true;
- break;
- case '0':
- if (key.isNumber() || key == m_blank)
- return true;
- break;
- case 'D':
- if (key.isNumber() && key.digitValue() > 0)
- return true;
- break;
- case 'd':
- if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
- return true;
- break;
- case '#':
- if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_blank)
- return true;
- break;
- case 'B':
- if (key == QLatin1Char('0') || key == QLatin1Char('1'))
- return true;
- break;
- case 'b':
- if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == m_blank)
- return true;
- break;
- case 'H':
- if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')))
- return true;
- break;
- case 'h':
- if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == m_blank)
- return true;
- break;
- default:
- break;
- }
- return false;
-}
-
-/*!
- \internal
-
- Returns true if the given text \a str is valid for any
- validator or input mask set for the line control.
-
- Otherwise returns false
-*/
-bool QQuickLineControl::hasAcceptableInput(const QString &str) const
-{
-#ifndef QT_NO_VALIDATOR
- QString textCopy = str;
- int cursorCopy = m_cursor;
- if (m_validator && m_validator->validate(textCopy, cursorCopy)
- != QValidator::Acceptable)
- return false;
-#endif
-
- if (!m_maskData)
- return true;
-
- if (str.length() != m_maxLength)
- return false;
-
- for (int i=0; i < m_maxLength; ++i) {
- if (m_maskData[i].separator) {
- if (str.at(i) != m_maskData[i].maskChar)
- return false;
- } else {
- if (!isValidInput(str.at(i), m_maskData[i].maskChar))
- return false;
- }
- }
- return true;
-}
-
-/*!
- \internal
-
- Applies the inputMask on \a str starting from position \a pos in the mask. \a clear
- specifies from where characters should be gotten when a separator is met in \a str - true means
- that blanks will be used, false that previous input is used.
- Calling this when no inputMask is set is undefined.
-*/
-QString QQuickLineControl::maskString(uint pos, const QString &str, bool clear) const
-{
- if (pos >= (uint)m_maxLength)
- return QString::fromLatin1("");
-
- QString fill;
- fill = clear ? clearString(0, m_maxLength) : m_text;
-
- int strIndex = 0;
- QString s = QString::fromLatin1("");
- int i = pos;
- while (i < m_maxLength) {
- if (strIndex < str.length()) {
- if (m_maskData[i].separator) {
- s += m_maskData[i].maskChar;
- if (str[(int)strIndex] == m_maskData[i].maskChar)
- strIndex++;
- ++i;
- } else {
- if (isValidInput(str[(int)strIndex], m_maskData[i].maskChar)) {
- switch (m_maskData[i].caseMode) {
- case MaskInputData::Upper:
- s += str[(int)strIndex].toUpper();
- break;
- case MaskInputData::Lower:
- s += str[(int)strIndex].toLower();
- break;
- default:
- s += str[(int)strIndex];
- }
- ++i;
- } else {
- // search for separator first
- int n = findInMask(i, true, true, str[(int)strIndex]);
- if (n != -1) {
- if (str.length() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[(int)strIndex]))) {
- s += fill.mid(i, n-i+1);
- i = n + 1; // update i to find + 1
- }
- } else {
- // search for valid m_blank if not
- n = findInMask(i, true, false, str[(int)strIndex]);
- if (n != -1) {
- s += fill.mid(i, n-i);
- switch (m_maskData[n].caseMode) {
- case MaskInputData::Upper:
- s += str[(int)strIndex].toUpper();
- break;
- case MaskInputData::Lower:
- s += str[(int)strIndex].toLower();
- break;
- default:
- s += str[(int)strIndex];
- }
- i = n + 1; // updates i to find + 1
- }
- }
- }
- ++strIndex;
- }
- } else
- break;
- }
-
- return s;
-}
-
-
-
-/*!
- \internal
-
- Returns a "cleared" string with only separators and blank chars.
- Calling this when no inputMask is set is undefined.
-*/
-QString QQuickLineControl::clearString(uint pos, uint len) const
-{
- if (pos >= (uint)m_maxLength)
- return QString();
-
- QString s;
- int end = qMin((uint)m_maxLength, pos + len);
- for (int i = pos; i < end; ++i)
- if (m_maskData[i].separator)
- s += m_maskData[i].maskChar;
- else
- s += m_blank;
-
- return s;
-}
-
-/*!
- \internal
-
- Strips blank parts of the input in a QQuickLineControl when an inputMask is set,
- separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1".
-*/
-QString QQuickLineControl::stripString(const QString &str) const
-{
- if (!m_maskData)
- return str;
-
- QString s;
- int end = qMin(m_maxLength, (int)str.length());
- for (int i = 0; i < end; ++i)
- if (m_maskData[i].separator)
- s += m_maskData[i].maskChar;
- else
- if (str[i] != m_blank)
- s += str[i];
-
- return s;
-}
-
-/*!
- \internal
- searches forward/backward in m_maskData for either a separator or a m_blank
-*/
-int QQuickLineControl::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const
-{
- if (pos >= m_maxLength || pos < 0)
- return -1;
-
- int end = forward ? m_maxLength : -1;
- int step = forward ? 1 : -1;
- int i = pos;
-
- while (i != end) {
- if (findSeparator) {
- if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
- return i;
- } else {
- if (!m_maskData[i].separator) {
- if (searchChar.isNull())
- return i;
- else if (isValidInput(searchChar, m_maskData[i].maskChar))
- return i;
- }
- }
- i += step;
- }
- return -1;
-}
-
-void QQuickLineControl::internalUndo(int until)
-{
- if (!isUndoAvailable())
- return;
- internalDeselect();
- while (m_undoState && m_undoState > until) {
- Command& cmd = m_history[--m_undoState];
- switch (cmd.type) {
- case Insert:
- m_text.remove(cmd.pos, 1);
- m_cursor = cmd.pos;
- break;
- case SetSelection:
- m_selstart = cmd.selStart;
- m_selend = cmd.selEnd;
- m_cursor = cmd.pos;
- break;
- case Remove:
- case RemoveSelection:
- m_text.insert(cmd.pos, cmd.uc);
- m_cursor = cmd.pos + 1;
- break;
- case Delete:
- case DeleteSelection:
- m_text.insert(cmd.pos, cmd.uc);
- m_cursor = cmd.pos;
- break;
- case Separator:
- continue;
- }
- if (until < 0 && m_undoState) {
- Command& next = m_history[m_undoState-1];
- if (next.type != cmd.type && next.type < RemoveSelection
- && (cmd.type < RemoveSelection || next.type == Separator))
- break;
- }
- }
- m_textDirty = true;
- emitCursorPositionChanged();
-}
-
-void QQuickLineControl::internalRedo()
-{
- if (!isRedoAvailable())
- return;
- internalDeselect();
- while (m_undoState < (int)m_history.size()) {
- Command& cmd = m_history[m_undoState++];
- switch (cmd.type) {
- case Insert:
- m_text.insert(cmd.pos, cmd.uc);
- m_cursor = cmd.pos + 1;
- break;
- case SetSelection:
- m_selstart = cmd.selStart;
- m_selend = cmd.selEnd;
- m_cursor = cmd.pos;
- break;
- case Remove:
- case Delete:
- case RemoveSelection:
- case DeleteSelection:
- m_text.remove(cmd.pos, 1);
- m_selstart = cmd.selStart;
- m_selend = cmd.selEnd;
- m_cursor = cmd.pos;
- break;
- case Separator:
- m_selstart = cmd.selStart;
- m_selend = cmd.selEnd;
- m_cursor = cmd.pos;
- break;
- }
- if (m_undoState < (int)m_history.size()) {
- Command& next = m_history[m_undoState];
- if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
- && (next.type < RemoveSelection || cmd.type == Separator))
- break;
- }
- }
- m_textDirty = true;
- emitCursorPositionChanged();
-}
-
-/*!
- \internal
-
- If the current cursor position differs from the last emitted cursor
- position, emits cursorPositionChanged().
-*/
-void QQuickLineControl::emitCursorPositionChanged()
-{
- if (m_cursor != m_lastCursorPos) {
- const int oldLast = m_lastCursorPos;
- m_lastCursorPos = m_cursor;
- cursorPositionChanged(oldLast, m_cursor);
-#ifndef QT_NO_ACCESSIBILITY
- QAccessible::updateAccessibility(parent(), 0, QAccessible::TextCaretMoved);
-#endif
- }
-}
-
-
-void QQuickLineControl::setCursorBlinkPeriod(int msec)
-{
- if (msec == m_blinkPeriod)
- return;
- if (m_blinkTimer) {
- killTimer(m_blinkTimer);
- }
- if (msec) {
- m_blinkTimer = startTimer(msec / 2);
- m_blinkStatus = 1;
- } else {
- m_blinkTimer = 0;
- if (m_blinkStatus == 1)
- emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
- }
- m_blinkPeriod = msec;
-}
-
-void QQuickLineControl::resetCursorBlinkTimer()
-{
- if (m_blinkPeriod == 0 || m_blinkTimer == 0)
- return;
- killTimer(m_blinkTimer);
- m_blinkTimer = startTimer(m_blinkPeriod / 2);
- m_blinkStatus = 1;
-}
-
-void QQuickLineControl::timerEvent(QTimerEvent *event)
-{
- if (event->timerId() == m_blinkTimer) {
- m_blinkStatus = !m_blinkStatus;
- emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
- } else if (event->timerId() == m_deleteAllTimer) {
- killTimer(m_deleteAllTimer);
- m_deleteAllTimer = 0;
- clear();
- }
-}
-
-void QQuickLineControl::processKeyEvent(QKeyEvent* event)
-{
- bool inlineCompletionAccepted = false;
-
- if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
- if (hasAcceptableInput() || fixup()) {
- emit accepted();
- emit editingFinished();
- }
- if (inlineCompletionAccepted)
- event->accept();
- else
- event->ignore();
- return;
- }
-
- if (echoMode() == PasswordEchoOnEdit
- && !passwordEchoEditing()
- && !isReadOnly()
- && !event->text().isEmpty()
- && !(event->modifiers() & Qt::ControlModifier)) {
- // Clear the edit and reset to normal echo mode while editing; the
- // echo mode switches back when the edit loses focus
- // ### resets current content. dubious code; you can
- // navigate with keys up, down, back, and select(?), but if you press
- // "left" or "right" it clears?
- updatePasswordEchoEditing(true);
- clear();
- }
-
- bool unknown = false;
- bool visual = cursorMoveStyle() == Qt::VisualMoveStyle;
-
- if (false) {
- }
-#ifndef QT_NO_SHORTCUT
- else if (event == QKeySequence::Undo) {
- if (!isReadOnly())
- undo();
- }
- else if (event == QKeySequence::Redo) {
- if (!isReadOnly())
- redo();
- }
- else if (event == QKeySequence::SelectAll) {
- selectAll();
- }
-#ifndef QT_NO_CLIPBOARD
- else if (event == QKeySequence::Copy) {
- copy();
- }
- else if (event == QKeySequence::Paste) {
- if (!isReadOnly()) {
- QClipboard::Mode mode = QClipboard::Clipboard;
- paste(mode);
- }
- }
- else if (event == QKeySequence::Cut) {
- if (!isReadOnly()) {
- copy();
- del();
- }
- }
- else if (event == QKeySequence::DeleteEndOfLine) {
- if (!isReadOnly()) {
- setSelection(cursor(), end());
- copy();
- del();
- }
- }
-#endif //QT_NO_CLIPBOARD
- else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
- home(0);
- }
- else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
- end(0);
- }
- else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
- home(1);
- }
- else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
- end(1);
- }
- else if (event == QKeySequence::MoveToNextChar) {
- if (hasSelectedText()) {
- moveCursor(selectionEnd(), false);
- } else {
- cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
- }
- }
- else if (event == QKeySequence::SelectNextChar) {
- cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
- }
- else if (event == QKeySequence::MoveToPreviousChar) {
- if (hasSelectedText()) {
- moveCursor(selectionStart(), false);
- } else {
- cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
- }
- }
- else if (event == QKeySequence::SelectPreviousChar) {
- cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
- }
- else if (event == QKeySequence::MoveToNextWord) {
- if (echoMode() == Normal)
- layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
- else
- layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
- }
- else if (event == QKeySequence::MoveToPreviousWord) {
- if (echoMode() == Normal)
- layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
- else if (!isReadOnly()) {
- layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
- }
- }
- else if (event == QKeySequence::SelectNextWord) {
- if (echoMode() == Normal)
- layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
- else
- layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
- }
- else if (event == QKeySequence::SelectPreviousWord) {
- if (echoMode() == Normal)
- layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
- else
- layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
- }
- else if (event == QKeySequence::Delete) {
- if (!isReadOnly())
- del();
- }
- else if (event == QKeySequence::DeleteEndOfWord) {
- if (!isReadOnly()) {
- cursorWordForward(true);
- del();
- }
- }
- else if (event == QKeySequence::DeleteStartOfWord) {
- if (!isReadOnly()) {
- cursorWordBackward(true);
- del();
- }
- }
-#endif // QT_NO_SHORTCUT
- else {
- bool handled = false;
- if (event->modifiers() & Qt::ControlModifier) {
- switch (event->key()) {
- case Qt::Key_Backspace:
- if (!isReadOnly()) {
- cursorWordBackward(true);
- del();
- }
- break;
- default:
- if (!handled)
- unknown = true;
- }
- } else { // ### check for *no* modifier
- switch (event->key()) {
- case Qt::Key_Backspace:
- if (!isReadOnly()) {
- backspace();
- }
- break;
- default:
- if (!handled)
- unknown = true;
- }
- }
- }
-
- if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
- setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
- unknown = false;
- }
-
- if (unknown && !isReadOnly()) {
- QString t = event->text();
- if (!t.isEmpty() && t.at(0).isPrint()) {
- insert(t);
- event->accept();
- return;
- }
- }
-
- if (unknown)
- event->ignore();
- else
- event->accept();
-}
-
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/quick/items/qquicklinecontrol_p.h b/src/quick/items/qquicklinecontrol_p.h
deleted file mode 100644
index e80fad7266..0000000000
--- a/src/quick/items/qquicklinecontrol_p.h
+++ /dev/null
@@ -1,449 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** GNU Lesser General Public License Usage
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this
-** file. Please review the following information to ensure the GNU Lesser
-** General Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU General
-** Public License version 3.0 as published by the Free Software Foundation
-** and appearing in the file LICENSE.GPL included in the packaging of this
-** file. Please review the following information to ensure the GNU General
-** Public License version 3.0 requirements will be met:
-** http://www.gnu.org/copyleft/gpl.html.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QQUICKLINECONTROL_P_H
-#define QQUICKLINECONTROL_P_H
-
-//
-// 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.
-//
-
-#include "QtCore/qglobal.h"
-
-#include "QtGui/qtextlayout.h"
-#include "QtCore/qpointer.h"
-#include "QtGui/qclipboard.h"
-#include "QtGui/qvalidator.h"
-#include "QtGui/qpalette.h"
-#include "QtGui/qguiapplication.h"
-#include "QtGui/qinputpanel.h"
-#include "QtCore/qpoint.h"
-
-QT_BEGIN_HEADER
-
-QT_BEGIN_NAMESPACE
-
-QT_MODULE(Gui)
-
-class Q_AUTOTEST_EXPORT QQuickLineControl : public QObject
-{
- Q_OBJECT
-
-public:
- QQuickLineControl(const QString &txt = QString())
- : m_cursor(0), m_preeditCursor(0), m_cursorWidth(0), m_layoutDirection(Qt::LayoutDirectionAuto),
- m_hideCursor(false), m_separator(0), m_readOnly(0),
- m_echoMode(Normal), m_textDirty(0), m_selDirty(0),
- m_validInput(1), m_blinkStatus(0), m_blinkPeriod(0), m_blinkTimer(0), m_deleteAllTimer(0),
- m_ascent(0), m_maxLength(32767), m_lastCursorPos(-1),
- m_maskData(0), m_modifiedState(0), m_undoState(0),
- m_selstart(0), m_selend(0), m_passwordEchoEditing(false)
- {
- init(txt);
- }
-
- enum EchoMode {
- Normal,
- NoEcho,
- Password,
- PasswordEchoOnEdit
- };
-
-
- ~QQuickLineControl()
- {
- delete [] m_maskData;
- }
-
- 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_modifiedState = m_undoState = 0; }
-
- bool isModified() const { return m_modifiedState != m_undoState; }
- void setModified(bool modified) { m_modifiedState = modified ? -1 : m_undoState; }
-
- 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; }
-
- int width() const { return qRound(m_textLayout.lineAt(0).width()) + 1; }
- int height() const { return qRound(m_textLayout.lineAt(0).height()) + 1; }
- int ascent() const { return m_ascent; }
- qreal naturalTextWidth() const { return m_textLayout.lineAt(0).naturalTextWidth(); }
-
- 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; }
- bool inSelection(int x) const
- {
- if (m_selstart >= m_selend)
- return false;
- int pos = xToPos(x, QTextLine::CursorOnCharacter);
- return pos >= m_selstart && pos < m_selend;
- }
-
- void removeSelection()
- {
- int priorState = m_undoState;
- removeSelectedText();
- finishChange(priorState);
- }
-
- int start() const { return 0; }
- int end() const { return m_text.length(); }
-
-#ifndef QT_NO_CLIPBOARD
- void copy(QClipboard::Mode mode = QClipboard::Clipboard) const;
- void paste(QClipboard::Mode mode = QClipboard::Clipboard);
-#endif
-
- int cursor() const{ return m_cursor; }
- int preeditCursor() const { return m_preeditCursor; }
-
- int cursorWidth() const { return m_cursorWidth; }
- void setCursorWidth(int value) { m_cursorWidth = value; }
-
- 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(text().length(), mark); }
-
- int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const;
- QRect cursorRect() const;
-
- qreal cursorToX(int cursor) const { return m_textLayout.lineAt(0).cursorToX(cursor); }
- qreal cursorToX() const
- {
- int cursor = m_cursor;
- if (m_preeditCursor != -1)
- cursor += m_preeditCursor;
- return cursorToX(cursor);
- }
-
- bool isReadOnly() const { return m_readOnly; }
- void setReadOnly(bool enable) { m_readOnly = enable; }
-
- QString text() const;
- QString realText() const;
- void setText(const QString &txt);
- void commitPreedit();
-
- QString displayText() const { return m_textLayout.text(); }
-
- 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 undo() { internalUndo(); finishChange(-1, true); }
- void redo() { internalRedo(); finishChange(); }
- void selectWordAtPos(int);
-
- EchoMode echoMode() const { return EchoMode(m_echoMode); }
- void setEchoMode(EchoMode mode)
- {
- m_echoMode = mode;
- m_passwordEchoEditing = false;
- updateDisplayText();
- }
-
- int maxLength() const { return m_maxLength; }
- void setMaxLength(int maxLength)
- {
- if (m_maskData)
- return;
- m_maxLength = maxLength;
- setText(m_text);
- }
-
-#ifndef QT_NO_VALIDATOR
- const QValidator *validator() const { return m_validator; }
- void setValidator(const QValidator *v) { m_validator = const_cast<QValidator*>(v); }
-#endif
-
- int cursorPosition() const { return m_cursor; }
- void setCursorPosition(int pos) { if (pos <= m_text.length()) moveCursor(qMax(0, pos)); }
-
- bool hasAcceptableInput() const { return hasAcceptableInput(m_text); }
- 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
-#ifndef QT_NO_IM
- bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); }
- void setPreeditArea(int cursor, const QString &text) { m_textLayout.setPreeditArea(cursor, text); }
-#endif
-
- QString preeditAreaText() const { return m_textLayout.preeditAreaText(); }
-
- void updatePasswordEchoEditing(bool editing);
- bool passwordEchoEditing() const { return m_passwordEchoEditing; }
-
- QChar passwordCharacter() const { return m_passwordCharacter; }
- void setPasswordCharacter(const QChar &character) { m_passwordCharacter = character; updateDisplayText(); }
-
- Qt::LayoutDirection layoutDirection() const {
- if (m_layoutDirection == Qt::LayoutDirectionAuto) {
- if (m_text.isEmpty())
- return QGuiApplication::keyboardInputDirection();
- return m_text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight;
- }
- return m_layoutDirection;
- }
- void setLayoutDirection(Qt::LayoutDirection direction)
- {
- if (direction != m_layoutDirection) {
- m_layoutDirection = direction;
- updateDisplayText();
- }
- }
-
- void setFont(const QFont &font) { m_textLayout.setFont(font); updateDisplayText(); }
-
- void processInputMethodEvent(QInputMethodEvent *event);
- void processMouseEvent(QMouseEvent* ev);
- void processKeyEvent(QKeyEvent* ev);
-
- int cursorBlinkPeriod() const { return m_blinkPeriod; }
- void setCursorBlinkPeriod(int msec);
- void resetCursorBlinkTimer();
-
- bool cursorBlinkStatus() const { return m_blinkStatus; }
-
- QString cancelText() const { return m_cancelText; }
- void setCancelText(const QString &text) { m_cancelText = text; }
-
- const QPalette &palette() const { return m_palette; }
- void setPalette(const QPalette &p) { m_palette = p; }
-
- enum DrawFlags {
- DrawText = 0x01,
- DrawSelections = 0x02,
- DrawCursor = 0x04,
- DrawAll = DrawText | DrawSelections | DrawCursor
- };
-
- bool processEvent(QEvent *ev);
-
- QTextLayout *textLayout()
- {
- return &m_textLayout;
- }
-
-private:
- void init(const QString &txt);
- 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();
-
- QString m_text;
- QPalette m_palette;
- int m_cursor;
- int m_preeditCursor;
- int m_cursorWidth;
- QString m_tentativeCommit;
- Qt::LayoutDirection m_layoutDirection;
- uint m_hideCursor : 1; // used to hide the m_cursor inside preedit areas
- uint m_separator : 1;
- uint m_readOnly : 1;
- uint m_echoMode : 2;
- uint m_textDirty : 1;
- uint m_selDirty : 1;
- uint m_validInput : 1;
- uint m_blinkStatus : 1;
- int m_blinkPeriod; // 0 for non-blinking cursor
- int m_blinkTimer;
- int m_deleteAllTimer;
- int m_ascent;
- int m_maxLength;
- int m_lastCursorPos;
- QList<int> m_transactions;
- QString m_cancelText;
-
- void emitCursorPositionChanged();
-
- bool finishChange(int validateFromState = -1, bool update = false, bool edited = true);
-
-#ifndef QT_NO_VALIDATOR
- QPointer<QValidator> m_validator;
-#endif
-
- struct MaskInputData {
- enum Casemode { NoCaseMode, Upper, Lower };
- QChar maskChar; // either the separator char or the inputmask
- bool separator;
- Casemode caseMode;
- };
- QString m_inputMask;
- QChar m_blank;
- MaskInputData *m_maskData;
-
- // 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;
- };
- int m_modifiedState;
- int m_undoState;
- QVector<Command> m_history;
- void addCommand(const Command& cmd);
-
- inline void separate() { m_separator = true; }
-
- // selection
- int m_selstart;
- int m_selend;
-
- // masking
- void parseInputMask(const QString &maskFields);
- bool isValidInput(QChar key, QChar mask) const;
- bool hasAcceptableInput(const QString &text) const;
- 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;
-
- // complex text layout
- QTextLayout m_textLayout;
-
- bool m_passwordEchoEditing;
- QChar m_passwordCharacter;
-
-Q_SIGNALS:
- void cursorPositionChanged(int, int);
- void selectionChanged();
-
- void displayTextChanged(const QString &);
- void textChanged(const QString &);
- void textEdited(const QString &);
-
- void resetInputContext();
- void updateMicroFocus();
-
- void accepted();
- void editingFinished();
- void updateNeeded(const QRect &);
-
-protected:
- virtual void timerEvent(QTimerEvent *event);
-
-private Q_SLOTS:
- void _q_clipboardChanged();
- void _q_deleteSelected();
-
-};
-
-QT_END_NAMESPACE
-
-QT_END_HEADER
-
-#endif // QQuickLineControl_P_H
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 37525233e4..649e29d6bc 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -54,6 +54,10 @@
#include <QtGui/qstylehints.h>
#include <QtGui/qinputpanel.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+
QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
@@ -96,7 +100,12 @@ QQuickTextInput::~QQuickTextInput()
QString QQuickTextInput::text() const
{
Q_D(const QQuickTextInput);
- return d->control->text();
+
+ QString content = d->m_text;
+ if (!d->m_tentativeCommit.isEmpty())
+ content.insert(d->m_cursor, d->m_tentativeCommit);
+ QString res = d->m_maskData ? d->stripString(content) : content;
+ return (res.isNull() ? QString::fromLatin1("") : res);
}
void QQuickTextInput::setText(const QString &s)
@@ -104,7 +113,16 @@ void QQuickTextInput::setText(const QString &s)
Q_D(QQuickTextInput);
if (s == text())
return;
- d->control->setText(s);
+ if (d->composeMode())
+ qApp->inputPanel()->reset();
+ d->m_tentativeCommit.clear();
+ d->internalSetText(s, -1, false);
+}
+
+QString QQuickTextInputPrivate::realText() const
+{
+ QString res = m_maskData ? stripString(m_text) : m_text;
+ return (res.isNull() ? QString::fromLatin1("") : res);
}
/*!
@@ -233,7 +251,7 @@ void QQuickTextInput::setFont(const QFont &font)
d->font.setPointSizeF(size/2.0);
}
if (oldFont != d->font) {
- d->control->setFont(d->font);
+ d->updateDisplayText();
updateSize();
updateCursorRectangle();
if (d->cursorItem) {
@@ -284,10 +302,8 @@ void QQuickTextInput::setSelectionColor(const QColor &color)
return;
d->selectionColor = color;
- QPalette p = d->control->palette();
- p.setColor(QPalette::Highlight, d->selectionColor);
- d->control->setPalette(p);
- if (d->control->hasSelectedText()) {
+ d->m_palette.setColor(QPalette::Highlight, d->selectionColor);
+ if (d->hasSelectedText()) {
d->textLayoutDirty = true;
update();
}
@@ -311,10 +327,8 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
return;
d->selectedTextColor = color;
- QPalette p = d->control->palette();
- p.setColor(QPalette::HighlightedText, d->selectedTextColor);
- d->control->setPalette(p);
- if (d->control->hasSelectedText()) {
+ d->m_palette.setColor(QPalette::HighlightedText, d->selectedTextColor);
+ if (d->hasSelectedText()) {
d->textLayoutDirty = true;
update();
}
@@ -406,9 +420,9 @@ bool QQuickTextInputPrivate::determineHorizontalAlignment()
{
if (hAlignImplicit) {
// if no explicit alignment has been set, follow the natural layout direction of the text
- QString text = control->text();
+ QString text = q_func()->text();
if (text.isEmpty())
- text = control->preeditAreaText();
+ text = m_textLayout.preeditAreaText();
bool isRightToLeft = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft();
return setHAlign(isRightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
}
@@ -438,19 +452,19 @@ void QQuickTextInputPrivate::mirrorChange()
bool QQuickTextInput::isReadOnly() const
{
Q_D(const QQuickTextInput);
- return d->control->isReadOnly();
+ return d->m_readOnly;
}
void QQuickTextInput::setReadOnly(bool ro)
{
Q_D(QQuickTextInput);
- if (d->control->isReadOnly() == ro)
+ if (d->m_readOnly == ro)
return;
setFlag(QQuickItem::ItemAcceptsInputMethod, !ro);
- d->control->setReadOnly(ro);
+ d->m_readOnly = ro;
if (!ro)
- d->control->setCursorPosition(d->control->end());
+ d->setCursorPosition(d->end());
emit readOnlyChanged(ro);
}
@@ -466,16 +480,17 @@ void QQuickTextInput::setReadOnly(bool ro)
int QQuickTextInput::maxLength() const
{
Q_D(const QQuickTextInput);
- return d->control->maxLength();
+ return d->m_maxLength;
}
void QQuickTextInput::setMaxLength(int ml)
{
Q_D(QQuickTextInput);
- if (d->control->maxLength() == ml)
+ if (d->m_maxLength == ml || d->m_maskData)
return;
- d->control->setMaxLength(ml);
+ d->m_maxLength = ml;
+ d->internalSetText(d->m_text, -1, false);
emit maximumLengthChanged(ml);
}
@@ -518,9 +533,9 @@ void QQuickTextInput::setCursorVisible(bool on)
if (d->cursorVisible == on)
return;
d->cursorVisible = on;
- d->control->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
- QRect r = d->control->cursorRect();
- if (d->control->inputMask().isEmpty())
+ d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
+ QRect r = cursorRectangle();
+ if (d->inputMask().isEmpty())
updateRect(r);
else
updateRect();
@@ -534,14 +549,15 @@ void QQuickTextInput::setCursorVisible(bool on)
int QQuickTextInput::cursorPosition() const
{
Q_D(const QQuickTextInput);
- return d->control->cursor();
+ return d->m_cursor;
}
+
void QQuickTextInput::setCursorPosition(int cp)
{
Q_D(QQuickTextInput);
- if (cp < 0 || cp > d->control->text().length())
+ if (cp < 0 || cp > text().length())
return;
- d->control->moveCursor(cp);
+ d->moveCursor(cp);
}
/*!
@@ -551,12 +567,11 @@ void QQuickTextInput::setCursorPosition(int cp)
QRect QQuickTextInput::cursorRectangle() const
{
Q_D(const QQuickTextInput);
- QRect r = d->control->cursorRect();
- // Scroll and make consistent with TextEdit
- // QQuickLineControl inexplicably adds 1 to the height and horizontal padding
- // for unicode direction markers.
- r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1);
- return r;
+ QTextLine l = d->m_textLayout.lineAt(0);
+ int c = d->m_cursor;
+ if (d->m_preeditCursor != -1)
+ c += d->m_preeditCursor;
+ return QRect(qRound(l.cursorToX(c)) - d->hscroll, 0, d->m_cursorWidth, l.height());
}
/*!
\qmlproperty int QtQuick2::TextInput::selectionStart
@@ -604,9 +619,9 @@ int QQuickTextInput::selectionEnd() const
void QQuickTextInput::select(int start, int end)
{
Q_D(QQuickTextInput);
- if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length())
+ if (start < 0 || end < 0 || start > text().length() || end > text().length())
return;
- d->control->setSelection(start, end-start);
+ d->setSelection(start, end-start);
}
/*!
@@ -626,7 +641,7 @@ void QQuickTextInput::select(int start, int end)
QString QQuickTextInput::selectedText() const
{
Q_D(const QQuickTextInput);
- return d->control->selectedText();
+ return d->selectedText();
}
/*!
@@ -788,18 +803,17 @@ void QQuickTextInput::setAutoScroll(bool b)
QValidator* QQuickTextInput::validator() const
{
Q_D(const QQuickTextInput);
- //###const cast isn't good, but needed for property system?
- return const_cast<QValidator*>(d->control->validator());
+ return d->m_validator;
}
void QQuickTextInput::setValidator(QValidator* v)
{
Q_D(QQuickTextInput);
- if (d->control->validator() == v)
+ if (d->m_validator == v)
return;
- d->control->setValidator(v);
- if (!d->control->hasAcceptableInput()) {
+ d->m_validator = v;
+ if (!d->hasAcceptableInput(d->m_text)) {
d->oldValidity = false;
emit acceptableInputChanged();
}
@@ -820,17 +834,17 @@ void QQuickTextInput::setValidator(QValidator* v)
QString QQuickTextInput::inputMask() const
{
Q_D(const QQuickTextInput);
- return d->control->inputMask();
+ return d->inputMask();
}
void QQuickTextInput::setInputMask(const QString &im)
{
Q_D(QQuickTextInput);
- if (d->control->inputMask() == im)
+ if (d->inputMask() == im)
return;
- d->control->setInputMask(im);
- emit inputMaskChanged(d->control->inputMask());
+ d->setInputMask(im);
+ emit inputMaskChanged(d->inputMask());
}
/*!
@@ -844,7 +858,7 @@ void QQuickTextInput::setInputMask(const QString &im)
bool QQuickTextInput::hasAcceptableInput() const
{
Q_D(const QQuickTextInput);
- return d->control->hasAcceptableInput();
+ return d->hasAcceptableInput(d->m_text);
}
/*!
@@ -860,12 +874,11 @@ void QQuickTextInputPrivate::updateInputMethodHints()
{
Q_Q(QQuickTextInput);
Qt::InputMethodHints hints = inputMethodHints;
- uint echo = control->echoMode();
- if (echo == QQuickTextInput::Password || echo == QQuickTextInput::NoEcho)
+ if (m_echoMode == QQuickTextInput::Password || m_echoMode == QQuickTextInput::NoEcho)
hints |= Qt::ImhHiddenText;
- else if (echo == QQuickTextInput::PasswordEchoOnEdit)
+ else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit)
hints &= ~Qt::ImhHiddenText;
- if (echo != QQuickTextInput::Normal)
+ if (m_echoMode != QQuickTextInput::Normal)
hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
q->setInputMethodHints(hints);
}
@@ -884,7 +897,7 @@ void QQuickTextInputPrivate::updateInputMethodHints()
QQuickTextInput::EchoMode QQuickTextInput::echoMode() const
{
Q_D(const QQuickTextInput);
- return (QQuickTextInput::EchoMode)d->control->echoMode();
+ return QQuickTextInput::EchoMode(d->m_echoMode);
}
void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
@@ -892,9 +905,13 @@ void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
Q_D(QQuickTextInput);
if (echoMode() == echo)
return;
- d->control->setEchoMode((QQuickLineControl::EchoMode)echo);
+ d->m_echoMode = echo;
+ d->m_passwordEchoEditing = false;
d->updateInputMethodHints();
+
+ d->updateDisplayText();
q_textChanged();
+
emit echoModeChanged(echoMode());
}
@@ -986,8 +1003,8 @@ void QQuickTextInput::createCursor()
QDeclarative_setParent_noEvent(d->cursorItem, this);
d->cursorItem->setParentItem(this);
- d->cursorItem->setX(d->control->cursorToX());
- d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QQuickLineControl's +1 which is not consistent with Text.
+ d->cursorItem->setX(d->cursorToX());
+ d->cursorItem->setHeight(d->calculateTextHeight());
}
/*!
@@ -1002,12 +1019,10 @@ void QQuickTextInput::createCursor()
QRectF QQuickTextInput::positionToRectangle(int pos) const
{
Q_D(const QQuickTextInput);
- if (pos > d->control->cursorPosition())
- pos += d->control->preeditAreaText().length();
- return QRectF(d->control->cursorToX(pos)-d->hscroll,
- 0.0,
- d->control->cursorWidth(),
- cursorRectangle().height());
+ if (pos > d->m_cursor)
+ pos += d->preeditAreaText().length();
+ QTextLine l = d->m_textLayout.lineAt(0);
+ return QRectF( l.cursorToX(pos) - d->hscroll, 0.0, d->m_cursorWidth, l.height());
}
/*!
@@ -1036,10 +1051,10 @@ int QQuickTextInput::positionAt(int x) const
int QQuickTextInput::positionAt(int x, CursorPosition position) const
{
Q_D(const QQuickTextInput);
- int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position));
- const int cursor = d->control->cursor();
+ int pos = d->m_textLayout.lineAt(0).xToCursor(x + d->hscroll, QTextLine::CursorPosition(position));
+ const int cursor = d->m_cursor;
if (pos > cursor) {
- const int preeditLength = d->control->preeditAreaText().length();
+ const int preeditLength = d->preeditAreaText().length();
pos = pos > cursor + preeditLength
? pos - preeditLength
: cursor;
@@ -1055,16 +1070,16 @@ void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) {
// Ignore when moving off the end unless there is a selection,
// because then moving will do something (deselect).
- int cursorPosition = d->control->cursor();
+ int cursorPosition = d->m_cursor;
if (cursorPosition == 0)
- ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
- if (cursorPosition == d->control->text().length())
- ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
+ ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
+ if (cursorPosition == text().length())
+ ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
}
if (ignore) {
ev->ignore();
} else {
- d->control->processKeyEvent(ev);
+ d->processKeyEvent(ev);
}
if (!ev->isAccepted())
QQuickImplicitSizeItem::keyPressEvent(ev);
@@ -1073,16 +1088,16 @@ void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev)
{
Q_D(QQuickTextInput);
- const bool wasComposing = d->control->preeditAreaText().length() > 0;
- if (d->control->isReadOnly()) {
+ const bool wasComposing = d->preeditAreaText().length() > 0;
+ if (d->m_readOnly) {
ev->ignore();
} else {
- d->control->processInputMethodEvent(ev);
+ d->processInputMethodEvent(ev);
}
if (!ev->isAccepted())
QQuickImplicitSizeItem::inputMethodEvent(ev);
- if (wasComposing != (d->control->preeditAreaText().length() > 0))
+ if (wasComposing != (d->m_textLayout.preeditAreaText().length() > 0))
emit inputMethodComposingChanged();
}
@@ -1091,9 +1106,9 @@ void QQuickTextInput::mouseDoubleClickEvent(QMouseEvent *event)
Q_D(QQuickTextInput);
if (d->selectByMouse && event->button() == Qt::LeftButton) {
- d->control->commitPreedit();
+ d->commitPreedit();
int cursor = d->xToPos(event->localPos().x());
- d->control->selectWordAtPos(cursor);
+ d->selectWordAtPos(cursor);
event->setAccepted(true);
if (!d->hasPendingTripleClick()) {
d->tripleClickStartPoint = event->localPos().toPoint();
@@ -1116,7 +1131,7 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
bool hadActiveFocus = hasActiveFocus();
forceActiveFocus();
// re-open input panel on press if already focused
- if (hasActiveFocus() && hadActiveFocus && !isReadOnly())
+ if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
openSoftwareInputPanel();
}
if (d->selectByMouse) {
@@ -1136,7 +1151,7 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
int cursor = d->xToPos(event->localPos().x());
- d->control->moveCursor(cursor, mark);
+ d->moveCursor(cursor, mark);
event->setAccepted(true);
}
@@ -1148,12 +1163,12 @@ void QQuickTextInput::mouseMoveEvent(QMouseEvent *event)
if (qAbs(int(event->localPos().x() - d->pressPos.x())) > qApp->styleHints()->startDragDistance())
setKeepMouseGrab(true);
- if (d->control->composeMode()) {
+ if (d->composeMode()) {
// start selection
int startPos = d->xToPos(d->pressPos.x());
int currentPos = d->xToPos(event->localPos().x());
if (startPos != currentPos)
- d->control->setSelection(startPos, currentPos - startPos);
+ d->setSelection(startPos, currentPos - startPos);
} else {
moveCursorSelection(d->xToPos(event->localPos().x()), d->mouseSelectionMode);
}
@@ -1175,10 +1190,10 @@ void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
#ifndef QT_NO_CLIPBOARD
if (QGuiApplication::clipboard()->supportsSelection()) {
if (event->button() == Qt::LeftButton) {
- d->control->copy(QClipboard::Selection);
- } else if (!isReadOnly() && event->button() == Qt::MidButton) {
- d->control->deselect();
- d->control->insert(QGuiApplication::clipboard()->text(QClipboard::Selection));
+ d->copy(QClipboard::Selection);
+ } else if (!d->m_readOnly && event->button() == Qt::MidButton) {
+ d->deselect();
+ d->insert(QGuiApplication::clipboard()->text(QClipboard::Selection));
}
}
#endif
@@ -1189,10 +1204,10 @@ void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
bool QQuickTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event)
{
#if !defined QT_NO_IM
- if (control->composeMode()) {
+ if (composeMode()) {
int tmp_cursor = xToPos(event->localPos().x());
- int mousePos = tmp_cursor - control->cursor();
- if (mousePos >= 0 && mousePos <= control->preeditAreaText().length()) {
+ int mousePos = tmp_cursor - m_cursor;
+ if (mousePos >= 0 && mousePos <= m_textLayout.preeditAreaText().length()) {
if (event->type() == QEvent::MouseButtonRelease) {
qApp->inputPanel()->invokeAction(QInputPanel::Click, mousePos);
}
@@ -1217,8 +1232,9 @@ void QQuickTextInput::mouseUngrabEvent()
bool QQuickTextInput::event(QEvent* ev)
{
#ifndef QT_NO_SHORTCUT
+ Q_D(QQuickTextInput);
if (ev->type() == QEvent::ShortcutOverride) {
- if (isReadOnly())
+ if (d->m_readOnly)
return false;
QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
if (ke == QKeySequence::Copy
@@ -1275,15 +1291,10 @@ void QQuickTextInput::geometryChanged(const QRectF &newGeometry,
QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
}
-int QQuickTextInputPrivate::calculateTextWidth()
-{
- return qRound(control->naturalTextWidth());
-}
-
void QQuickTextInputPrivate::updateHorizontalScroll()
{
Q_Q(QQuickTextInput);
- const int preeditLength = control->preeditAreaText().length();
+ const int preeditLength = m_textLayout.preeditAreaText().length();
const int width = q->width();
int widthUsed = calculateTextWidth();
@@ -1303,7 +1314,7 @@ void QQuickTextInputPrivate::updateHorizontalScroll()
break;
}
} else {
- int cix = qRound(control->cursorToX(control->cursor() + preeditLength));
+ int cix = qRound(cursorToX(m_cursor + preeditLength));
if (cix - hscroll >= width) {
// text doesn't fit, cursor is to the right of br (scroll right)
hscroll = cix - width;
@@ -1318,8 +1329,8 @@ void QQuickTextInputPrivate::updateHorizontalScroll()
if (preeditLength > 0) {
// check to ensure long pre-edit text doesn't push the cursor
// off to the left
- cix = qRound(control->cursorToX(
- control->cursor() + qMax(0, control->preeditCursor() - 1)));
+ cix = qRound(cursorToX(
+ m_cursor + qMax(0, m_preeditCursor - 1)));
if (cix < hscroll)
hscroll = cix;
}
@@ -1341,8 +1352,7 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
if (cursorNode != 0 && !isReadOnly()) {
cursorNode->setRect(cursorRectangle());
- if (!d->cursorVisible
- || (!d->control->cursorBlinkStatus() && d->control->cursorBlinkPeriod() > 0)) {
+ if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
d->hideCursor();
} else {
d->showCursor();
@@ -1357,25 +1367,23 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
QRect br(boundingRect().toRect());
if (d->autoScroll) {
// the y offset is there to keep the baseline constant in case we have script changes in the text.
- offset = br.topLeft() - QPoint(d->hscroll, d->control->ascent() - fm.ascent());
+ offset = br.topLeft() - QPoint(d->hscroll, d->ascent() - fm.ascent());
} else {
offset = QPoint(d->hscroll, 0);
}
- QTextLayout *textLayout = d->control->textLayout();
- if (!textLayout->text().isEmpty()) {
- node->addTextLayout(offset, textLayout, d->color,
+ if (!d->m_textLayout.text().isEmpty()) {
+ node->addTextLayout(offset, &d->m_textLayout, d->color,
QQuickText::Normal, QColor(),
d->selectionColor, d->selectedTextColor,
- d->control->selectionStart(),
- d->control->selectionEnd() - 1); // selectionEnd() returns first char after
+ d->selectionStart(),
+ d->selectionEnd() - 1); // selectionEnd() returns first char after
// selection
}
if (!isReadOnly() && d->cursorItem == 0) {
node->setCursor(cursorRectangle(), d->color);
- if (!d->cursorVisible
- || (!d->control->cursorBlinkStatus() && d->control->cursorBlinkPeriod() > 0)) {
+ if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
d->hideCursor();
} else {
d->showCursor();
@@ -1401,25 +1409,24 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
case Qt::ImFont:
return font();
case Qt::ImCursorPosition:
- return QVariant(d->control->cursor());
+ return QVariant(d->m_cursor);
case Qt::ImSurroundingText:
- if (d->control->echoMode() == QQuickLineControl::PasswordEchoOnEdit
- && !d->control->passwordEchoEditing()) {
+ if (d->m_echoMode == PasswordEchoOnEdit && !d->m_passwordEchoEditing) {
return QVariant(displayText());
} else {
- return QVariant(d->control->realText());
+ return QVariant(d->realText());
}
case Qt::ImCurrentSelection:
return QVariant(selectedText());
case Qt::ImMaximumTextLength:
return QVariant(maxLength());
case Qt::ImAnchorPosition:
- if (d->control->selectionStart() == d->control->selectionEnd())
- return QVariant(d->control->cursor());
- else if (d->control->selectionStart() == d->control->cursor())
- return QVariant(d->control->selectionEnd());
+ if (d->selectionStart() == d->selectionEnd())
+ return QVariant(d->m_cursor);
+ else if (d->selectionStart() == d->m_cursor)
+ return QVariant(d->selectionEnd());
else
- return QVariant(d->control->selectionStart());
+ return QVariant(d->selectionStart());
default:
return QVariant();
}
@@ -1433,7 +1440,7 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
void QQuickTextInput::deselect()
{
Q_D(QQuickTextInput);
- d->control->deselect();
+ d->deselect();
}
/*!
@@ -1444,7 +1451,7 @@ void QQuickTextInput::deselect()
void QQuickTextInput::selectAll()
{
Q_D(QQuickTextInput);
- d->control->setSelection(0, d->control->text().length());
+ d->setSelection(0, text().length());
}
/*!
@@ -1455,12 +1462,11 @@ void QQuickTextInput::selectAll()
*/
bool QQuickTextInput::isRightToLeft(int start, int end)
{
- Q_D(QQuickTextInput);
if (start > end) {
qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
return false;
} else {
- return d->control->text().mid(start, end - start).isRightToLeft();
+ return text().mid(start, end - start).isRightToLeft();
}
}
@@ -1473,8 +1479,8 @@ bool QQuickTextInput::isRightToLeft(int start, int end)
void QQuickTextInput::cut()
{
Q_D(QQuickTextInput);
- d->control->copy();
- d->control->del();
+ d->copy();
+ d->del();
}
/*!
@@ -1485,7 +1491,7 @@ void QQuickTextInput::cut()
void QQuickTextInput::copy()
{
Q_D(QQuickTextInput);
- d->control->copy();
+ d->copy();
}
/*!
@@ -1496,8 +1502,8 @@ void QQuickTextInput::copy()
void QQuickTextInput::paste()
{
Q_D(QQuickTextInput);
- if (!d->control->isReadOnly())
- d->control->paste();
+ if (!d->m_readOnly)
+ d->paste();
}
#endif // QT_NO_CLIPBOARD
@@ -1509,7 +1515,7 @@ void QQuickTextInput::paste()
void QQuickTextInput::selectWord()
{
Q_D(QQuickTextInput);
- d->control->selectWordAtPos(d->control->cursor());
+ d->selectWordAtPos(d->m_cursor);
}
/*!
@@ -1539,7 +1545,7 @@ void QQuickTextInput::selectWord()
QString QQuickTextInput::passwordCharacter() const
{
Q_D(const QQuickTextInput);
- return QString(d->control->passwordCharacter());
+ return QString(d->m_passwordCharacter);
}
void QQuickTextInput::setPasswordCharacter(const QString &str)
@@ -1547,9 +1553,9 @@ void QQuickTextInput::setPasswordCharacter(const QString &str)
Q_D(QQuickTextInput);
if (str.length() < 1)
return;
- d->control->setPasswordCharacter(str.constData()[0]);
- EchoMode echoMode_ = echoMode();
- if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) {
+ d->m_passwordCharacter = str.constData()[0];
+ d->updateDisplayText();
+ if (d->m_echoMode == Password || d->m_echoMode == PasswordEchoOnEdit) {
updateSize();
}
emit passwordCharacterChanged();
@@ -1568,7 +1574,7 @@ void QQuickTextInput::setPasswordCharacter(const QString &str)
QString QQuickTextInput::displayText() const
{
Q_D(const QQuickTextInput);
- return d->control->displayText();
+ return d->m_textLayout.text();
}
/*!
@@ -1639,7 +1645,7 @@ bool QQuickTextInput::canPaste() const
void QQuickTextInput::moveCursorSelection(int position)
{
Q_D(QQuickTextInput);
- d->control->moveCursor(position, true);
+ d->moveCursor(position, true);
}
/*!
@@ -1684,19 +1690,19 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
Q_D(QQuickTextInput);
if (mode == SelectCharacters) {
- d->control->moveCursor(pos, true);
- } else if (pos != d->control->cursor()){
- const int cursor = d->control->cursor();
+ d->moveCursor(pos, true);
+ } else if (pos != d->m_cursor){
+ const int cursor = d->m_cursor;
int anchor;
- if (!d->control->hasSelectedText())
- anchor = d->control->cursor();
- else if (d->control->selectionStart() == d->control->cursor())
- anchor = d->control->selectionEnd();
+ if (!d->hasSelectedText())
+ anchor = d->m_cursor;
+ else if (d->selectionStart() == d->m_cursor)
+ anchor = d->selectionEnd();
else
- anchor = d->control->selectionStart();
+ anchor = d->selectionStart();
if (anchor < pos || (anchor == pos && cursor < pos)) {
- const QString text = d->control->text();
+ const QString text = this->text();
QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
finder.setPosition(anchor);
@@ -1712,9 +1718,9 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
finder.toNextBoundary();
const int cursor = finder.position() != -1 ? finder.position() : text.length();
- d->control->setSelection(anchor, cursor - anchor);
+ d->setSelection(anchor, cursor - anchor);
} else if (anchor > pos || (anchor == pos && cursor > pos)) {
- const QString text = d->control->text();
+ const QString text = this->text();
QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
finder.setPosition(anchor);
@@ -1731,7 +1737,7 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
finder.toPreviousBoundary();
const int cursor = finder.position() != -1 ? finder.position() : 0;
- d->control->setSelection(anchor, cursor - anchor);
+ d->setSelection(anchor, cursor - anchor);
}
}
}
@@ -1825,7 +1831,7 @@ void QQuickTextInput::closeSoftwareInputPanel()
void QQuickTextInput::focusInEvent(QFocusEvent *event)
{
Q_D(const QQuickTextInput);
- if (d->focusOnPress && !isReadOnly())
+ if (d->focusOnPress && !d->m_readOnly)
openSoftwareInputPanel();
QQuickImplicitSizeItem::focusInEvent(event);
}
@@ -1838,9 +1844,9 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
d->focused = hasFocus;
setCursorVisible(hasFocus); // ### refactor: && d->canvas && d->canvas->hasFocus()
if (echoMode() == QQuickTextInput::PasswordEchoOnEdit && !hasFocus)
- d->control->updatePasswordEchoEditing(false);//QQuickLineControl sets it on key events, but doesn't deal with focus events
+ d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
if (!hasFocus)
- d->control->deselect();
+ d->deselect();
}
QQuickItem::itemChange(change, value);
}
@@ -1860,76 +1866,37 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
bool QQuickTextInput::isInputMethodComposing() const
{
Q_D(const QQuickTextInput);
- return d->control->preeditAreaText().length() > 0;
+ return d->preeditAreaText().length() > 0;
}
void QQuickTextInputPrivate::init()
{
Q_Q(QQuickTextInput);
- control->setParent(q);//Now mandatory due to accessibility changes
- control->setCursorWidth(1);
- control->setPasswordCharacter(QLatin1Char('*'));
q->setSmooth(smooth);
q->setAcceptedMouseButtons(Qt::LeftButton);
q->setFlag(QQuickItem::ItemAcceptsInputMethod);
q->setFlag(QQuickItem::ItemHasContents);
- q->connect(control, SIGNAL(cursorPositionChanged(int,int)),
- q, SLOT(cursorPosChanged()));
- q->connect(control, SIGNAL(selectionChanged()),
- q, SLOT(selectionChanged()));
- q->connect(control, SIGNAL(textChanged(QString)),
- q, SLOT(q_textChanged()));
- q->connect(control, SIGNAL(accepted()),
- q, SIGNAL(accepted()));
- q->connect(control, SIGNAL(updateNeeded(QRect)),
- q, SLOT(updateRect(QRect)));
#ifndef QT_NO_CLIPBOARD
q->connect(q, SIGNAL(readOnlyChanged(bool)),
q, SLOT(q_canPasteChanged()));
q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()),
q, SLOT(q_canPasteChanged()));
- canPaste = !control->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
+ canPaste = !m_readOnly && QGuiApplication::clipboard()->text().length() != 0;
#endif // QT_NO_CLIPBOARD
- q->connect(control, SIGNAL(updateMicroFocus()),
- q, SLOT(updateCursorRectangle()));
- q->connect(control, SIGNAL(displayTextChanged(QString)),
- q, SLOT(updateRect()));
+ updateDisplayText();
q->updateSize();
imHints &= ~Qt::ImhMultiLine;
- oldValidity = control->hasAcceptableInput();
+ oldValidity = hasAcceptableInput(m_text);
lastSelectionStart = 0;
lastSelectionEnd = 0;
- QPalette p = control->palette();
- selectedTextColor = p.color(QPalette::HighlightedText);
- selectionColor = p.color(QPalette::Highlight);
+ selectedTextColor = m_palette.color(QPalette::HighlightedText);
+ selectionColor = m_palette.color(QPalette::Highlight);
determineHorizontalAlignment();
if (!qmlDisableDistanceField()) {
- QTextOption option = control->textLayout()->textOption();
+ QTextOption option = m_textLayout.textOption();
option.setUseDesignMetrics(true);
- control->textLayout()->setTextOption(option);
- }
-}
-
-void QQuickTextInput::cursorPosChanged()
-{
- Q_D(QQuickTextInput);
- updateCursorRectangle();
- emit cursorPositionChanged();
- // XXX todo - not in 4.8?
-#if 0
- d->control->resetCursorBlinkTimer();
-#endif
-
- if (!d->control->hasSelectedText()) {
- if (d->lastSelectionStart != d->control->cursor()) {
- d->lastSelectionStart = d->control->cursor();
- emit selectionStartChanged();
- }
- if (d->lastSelectionEnd != d->control->cursor()) {
- d->lastSelectionEnd = d->control->cursor();
- emit selectionEndChanged();
- }
+ m_textLayout.setTextOption(option);
}
}
@@ -1942,7 +1909,7 @@ void QQuickTextInput::updateCursorRectangle()
updateMicroFocus();
emit cursorRectangleChanged();
if (d->cursorItem)
- d->cursorItem->setX(d->control->cursorToX() - d->hscroll);
+ d->cursorItem->setX(d->cursorToX() - d->hscroll);
}
void QQuickTextInput::selectionChanged()
@@ -1951,16 +1918,16 @@ void QQuickTextInput::selectionChanged()
updateRect();//TODO: Only update rect in selection
emit selectedTextChanged();
- if (d->lastSelectionStart != d->control->selectionStart()) {
- d->lastSelectionStart = d->control->selectionStart();
+ if (d->lastSelectionStart != d->selectionStart()) {
+ d->lastSelectionStart = d->selectionStart();
if (d->lastSelectionStart == -1)
- d->lastSelectionStart = d->control->cursor();
+ d->lastSelectionStart = d->m_cursor;
emit selectionStartChanged();
}
- if (d->lastSelectionEnd != d->control->selectionEnd()) {
- d->lastSelectionEnd = d->control->selectionEnd();
+ if (d->lastSelectionEnd != d->selectionEnd()) {
+ d->lastSelectionEnd = d->selectionEnd();
if (d->lastSelectionEnd == -1)
- d->lastSelectionEnd = d->control->cursor();
+ d->lastSelectionEnd = d->m_cursor;
emit selectionEndChanged();
}
}
@@ -2010,7 +1977,7 @@ QRectF QQuickTextInput::boundingRect() const
Q_D(const QQuickTextInput);
QRectF r = QQuickImplicitSizeItem::boundingRect();
- int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth();
+ int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->m_cursorWidth;
// Could include font max left/right bearings to either side of rectangle.
@@ -2023,7 +1990,7 @@ void QQuickTextInput::updateSize(bool needsRedraw)
Q_D(QQuickTextInput);
int w = width();
int h = height();
- setImplicitSize(d->calculateTextWidth(), d->control->height()-1); // -1 to counter QQuickLineControl's +1 which is not consistent with Text.
+ setImplicitSize(d->calculateTextWidth(), d->calculateTextHeight());
if (w==width() && h==height() && needsRedraw)
update();
}
@@ -2033,11 +2000,1433 @@ void QQuickTextInput::q_canPasteChanged()
Q_D(QQuickTextInput);
bool old = d->canPaste;
#ifndef QT_NO_CLIPBOARD
- d->canPaste = !d->control->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
+ d->canPaste = !d->m_readOnly && QGuiApplication::clipboard()->text().length() != 0;
#endif
if (d->canPaste != old)
emit canPasteChanged();
}
+// ### these should come from QStyleHints
+const int textCursorWidth = 1;
+const bool fullWidthSelection = true;
+
+/*!
+ \internal
+
+ Updates the display text based of the current edit text
+ If the text has changed will emit displayTextChanged()
+*/
+void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
+{
+ QString orig = m_textLayout.text();
+ QString str;
+ if (m_echoMode == QQuickTextInput::NoEcho)
+ str = QString::fromLatin1("");
+ else
+ str = m_text;
+
+ if (m_echoMode == QQuickTextInput::Password
+ || (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing))
+ str.fill(m_passwordCharacter);
+
+ // replace certain non-printable characters with spaces (to avoid
+ // drawing boxes when using fonts that don't have glyphs for such
+ // characters)
+ QChar* uc = str.data();
+ for (int i = 0; i < (int)str.length(); ++i) {
+ if ((uc[i] < 0x20 && uc[i] != 0x09)
+ || uc[i] == QChar::LineSeparator
+ || uc[i] == QChar::ParagraphSeparator
+ || uc[i] == QChar::ObjectReplacementCharacter)
+ uc[i] = QChar(0x0020);
+ }
+
+ m_textLayout.setText(str);
+
+ QTextOption option = m_textLayout.textOption();
+ option.setTextDirection(m_layoutDirection);
+ option.setFlags(QTextOption::IncludeTrailingSpaces);
+ m_textLayout.setTextOption(option);
+
+ m_textLayout.beginLayout();
+ QTextLine l = m_textLayout.createLine();
+ m_textLayout.endLayout();
+ m_ascent = qRound(l.ascent());
+
+ if (str != orig || forceUpdate)
+ emit q_func()->displayTextChanged();
+}
+
+#ifndef QT_NO_CLIPBOARD
+/*!
+ \internal
+
+ Copies the currently selected text into the clipboard using the given
+ \a mode.
+
+ \note If the echo mode is set to a mode other than Normal then copy
+ will not work. This is to prevent using copy as a method of bypassing
+ password features of the line control.
+*/
+void QQuickTextInputPrivate::copy(QClipboard::Mode mode) const
+{
+ QString t = selectedText();
+ if (!t.isEmpty() && m_echoMode == QQuickTextInput::Normal) {
+ QGuiApplication::clipboard()->setText(t, mode);
+ }
+}
+
+/*!
+ \internal
+
+ Inserts the text stored in the application clipboard into the line
+ control.
+
+ \sa insert()
+*/
+void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode)
+{
+ QString clip = QGuiApplication::clipboard()->text(clipboardMode);
+ if (!clip.isEmpty() || hasSelectedText()) {
+ separate(); //make it a separate undo/redo command
+ insert(clip);
+ separate();
+ }
+}
+
+#endif // !QT_NO_CLIPBOARD
+
+/*!
+ \internal
+
+ Exits preedit mode and commits parts marked as tentative commit
+*/
+void QQuickTextInputPrivate::commitPreedit()
+{
+ if (!composeMode())
+ return;
+
+ qApp->inputPanel()->reset();
+
+ if (!m_tentativeCommit.isEmpty()) {
+ internalInsert(m_tentativeCommit);
+ m_tentativeCommit.clear();
+ finishChange(-1, true/*not used, not documented*/, false);
+ }
+
+ m_preeditCursor = 0;
+ m_textLayout.setPreeditArea(-1, QString());
+ m_textLayout.clearAdditionalFormats();
+ updateDisplayText(/*force*/ true);
+}
+
+/*!
+ \internal
+
+ Handles the behavior for the backspace key or function.
+ Removes the current selection if there is a selection, otherwise
+ removes the character prior to the cursor position.
+
+ \sa del()
+*/
+void QQuickTextInputPrivate::backspace()
+{
+ int priorState = m_undoState;
+ if (hasSelectedText()) {
+ removeSelectedText();
+ } else if (m_cursor) {
+ --m_cursor;
+ if (m_maskData)
+ m_cursor = prevMaskBlank(m_cursor);
+ QChar uc = m_text.at(m_cursor);
+ if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
+ // second half of a surrogate, check if we have the first half as well,
+ // if yes delete both at once
+ uc = m_text.at(m_cursor - 1);
+ if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) {
+ internalDelete(true);
+ --m_cursor;
+ }
+ }
+ internalDelete(true);
+ }
+ finishChange(priorState);
+}
+
+/*!
+ \internal
+
+ Handles the behavior for the delete key or function.
+ Removes the current selection if there is a selection, otherwise
+ removes the character after the cursor position.
+
+ \sa del()
+*/
+void QQuickTextInputPrivate::del()
+{
+ int priorState = m_undoState;
+ if (hasSelectedText()) {
+ removeSelectedText();
+ } else {
+ int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
+ while (n--)
+ internalDelete();
+ }
+ finishChange(priorState);
+}
+
+/*!
+ \internal
+
+ Inserts the given \a newText at the current cursor position.
+ If there is any selected text it is removed prior to insertion of
+ the new text.
+*/
+void QQuickTextInputPrivate::insert(const QString &newText)
+{
+ int priorState = m_undoState;
+ removeSelectedText();
+ internalInsert(newText);
+ finishChange(priorState);
+}
+
+/*!
+ \internal
+
+ Clears the line control text.
+*/
+void QQuickTextInputPrivate::clear()
+{
+ int priorState = m_undoState;
+ m_selstart = 0;
+ m_selend = m_text.length();
+ removeSelectedText();
+ separate();
+ finishChange(priorState, /*update*/false, /*edited*/false);
+}
+
+/*!
+ \internal
+
+ Sets \a length characters from the given \a start position as selected.
+ The given \a start position must be within the current text for
+ the line control. If \a length characters cannot be selected, then
+ the selection will extend to the end of the current text.
+*/
+void QQuickTextInputPrivate::setSelection(int start, int length)
+{
+ Q_Q(QQuickTextInput);
+ commitPreedit();
+
+ if (start < 0 || start > (int)m_text.length()){
+ qWarning("QQuickTextInputPrivate::setSelection: Invalid start position");
+ return;
+ }
+
+ if (length > 0) {
+ if (start == m_selstart && start + length == m_selend && m_cursor == m_selend)
+ return;
+ m_selstart = start;
+ m_selend = qMin(start + length, (int)m_text.length());
+ m_cursor = m_selend;
+ } else if (length < 0){
+ if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart)
+ return;
+ m_selstart = qMax(start + length, 0);
+ m_selend = start;
+ m_cursor = m_selstart;
+ } else if (m_selstart != m_selend) {
+ m_selstart = 0;
+ m_selend = 0;
+ m_cursor = start;
+ } else {
+ m_cursor = start;
+ emitCursorPositionChanged();
+ return;
+ }
+ emit q->selectionChanged();
+ emitCursorPositionChanged();
+}
+
+/*!
+ \internal
+
+ Initializes the line control with a starting text value of \a txt.
+*/
+void QQuickTextInputPrivate::init(const QString &txt)
+{
+ m_text = txt;
+
+ updateDisplayText();
+ m_cursor = m_text.length();
+}
+
+/*!
+ \internal
+
+ Sets the password echo editing to \a editing. If password echo editing
+ is true, then the text of the password is displayed even if the echo
+ mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing
+ does not affect other echo modes.
+*/
+void QQuickTextInputPrivate::updatePasswordEchoEditing(bool editing)
+{
+ m_passwordEchoEditing = editing;
+ updateDisplayText();
+}
+
+/*!
+ \internal
+
+ Returns the cursor position of the given \a x pixel value in relation
+ to the displayed text. The given \a betweenOrOn specified what kind
+ of cursor position is requested.
+*/
+int QQuickTextInputPrivate::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
+{
+ Q_Q(const QQuickTextInput);
+ QRect cr = q->boundingRect().toRect();
+ x-= cr.x() - hscroll;
+ return m_textLayout.lineAt(0).xToCursor(x, betweenOrOn);
+}
+
+/*!
+ \internal
+
+ Fixes the current text so that it is valid given any set validators.
+
+ Returns true if the text was changed. Otherwise returns false.
+*/
+bool QQuickTextInputPrivate::fixup() // this function assumes that validate currently returns != Acceptable
+{
+#ifndef QT_NO_VALIDATOR
+ if (m_validator) {
+ QString textCopy = m_text;
+ int cursorCopy = m_cursor;
+ m_validator->fixup(textCopy);
+ if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
+ if (textCopy != m_text || cursorCopy != m_cursor)
+ internalSetText(textCopy, cursorCopy);
+ return true;
+ }
+ }
+#endif
+ return false;
+}
+
+/*!
+ \internal
+
+ Moves the cursor to the given position \a pos. If \a mark is true will
+ adjust the currently selected text.
+*/
+void QQuickTextInputPrivate::moveCursor(int pos, bool mark)
+{
+ Q_Q(QQuickTextInput);
+ commitPreedit();
+
+ if (pos != m_cursor) {
+ separate();
+ if (m_maskData)
+ pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
+ }
+ if (mark) {
+ int anchor;
+ if (m_selend > m_selstart && m_cursor == m_selstart)
+ anchor = m_selend;
+ else if (m_selend > m_selstart && m_cursor == m_selend)
+ anchor = m_selstart;
+ else
+ anchor = m_cursor;
+ m_selstart = qMin(anchor, pos);
+ m_selend = qMax(anchor, pos);
+ updateDisplayText();
+ } else {
+ internalDeselect();
+ }
+ m_cursor = pos;
+ if (mark || m_selDirty) {
+ m_selDirty = false;
+ emit q->selectionChanged();
+ }
+ emitCursorPositionChanged();
+}
+
+/*!
+ \internal
+
+ Applies the given input method event \a event to the text of the line
+ control
+*/
+void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
+{
+ Q_Q(QQuickTextInput);
+
+ int priorState = -1;
+ bool isGettingInput = !event->commitString().isEmpty()
+ || event->preeditString() != preeditAreaText()
+ || event->replacementLength() > 0;
+ bool cursorPositionChanged = false;
+ bool selectionChange = false;
+
+ if (isGettingInput) {
+ // If any text is being input, remove selected text.
+ priorState = m_undoState;
+ if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
+ updatePasswordEchoEditing(true);
+ m_selstart = 0;
+ m_selend = m_text.length();
+ }
+ removeSelectedText();
+ }
+
+ int c = m_cursor; // cursor position after insertion of commit string
+ if (event->replacementStart() <= 0)
+ c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength());
+
+ m_cursor += event->replacementStart();
+ if (m_cursor < 0)
+ m_cursor = 0;
+
+ // insert commit string
+ if (event->replacementLength()) {
+ m_selstart = m_cursor;
+ m_selend = m_selstart + event->replacementLength();
+ removeSelectedText();
+ }
+ if (!event->commitString().isEmpty()) {
+ internalInsert(event->commitString());
+ cursorPositionChanged = true;
+ }
+
+ m_cursor = qBound(0, c, m_text.length());
+
+ for (int i = 0; i < event->attributes().size(); ++i) {
+ const QInputMethodEvent::Attribute &a = event->attributes().at(i);
+ if (a.type == QInputMethodEvent::Selection) {
+ m_cursor = qBound(0, a.start + a.length, m_text.length());
+ if (a.length) {
+ m_selstart = qMax(0, qMin(a.start, m_text.length()));
+ m_selend = m_cursor;
+ if (m_selend < m_selstart) {
+ qSwap(m_selstart, m_selend);
+ }
+ selectionChange = true;
+ } else {
+ m_selstart = m_selend = 0;
+ }
+ cursorPositionChanged = true;
+ }
+ }
+#ifndef QT_NO_IM
+ m_textLayout.setPreeditArea(m_cursor, event->preeditString());
+#endif //QT_NO_IM
+ const int oldPreeditCursor = m_preeditCursor;
+ m_preeditCursor = event->preeditString().length();
+ m_hideCursor = false;
+ QList<QTextLayout::FormatRange> formats;
+ for (int i = 0; i < event->attributes().size(); ++i) {
+ const QInputMethodEvent::Attribute &a = event->attributes().at(i);
+ if (a.type == QInputMethodEvent::Cursor) {
+ m_preeditCursor = a.start;
+ m_hideCursor = !a.length;
+ } else if (a.type == QInputMethodEvent::TextFormat) {
+ QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
+ if (f.isValid()) {
+ QTextLayout::FormatRange o;
+ o.start = a.start + m_cursor;
+ o.length = a.length;
+ o.format = f;
+ formats.append(o);
+ }
+ }
+ }
+ m_textLayout.setAdditionalFormats(formats);
+ updateDisplayText(/*force*/ true);
+ if (cursorPositionChanged)
+ emitCursorPositionChanged();
+ else if (m_preeditCursor != oldPreeditCursor)
+ q->updateCursorRectangle();
+
+ bool tentativeCommitChanged = m_tentativeCommit != event->tentativeCommitString();
+
+ if (tentativeCommitChanged) {
+ m_textDirty = true;
+ m_tentativeCommit = event->tentativeCommitString();
+ }
+
+ if (isGettingInput || tentativeCommitChanged)
+ finishChange(priorState);
+
+ if (selectionChange)
+ emit q->selectionChanged();
+}
+
+/*!
+ \internal
+
+ Sets the selection to cover the word at the given cursor position.
+ The word boundaries are defined by the behavior of QTextLayout::SkipWords
+ cursor mode.
+*/
+void QQuickTextInputPrivate::selectWordAtPos(int cursor)
+{
+ int next = cursor + 1;
+ if (next > end())
+ --next;
+ int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords);
+ moveCursor(c, false);
+ // ## text layout should support end of words.
+ int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords);
+ while (end > cursor && m_text[end-1].isSpace())
+ --end;
+ moveCursor(end, true);
+}
+
+/*!
+ \internal
+
+ Completes a change to the line control text. If the change is not valid
+ will undo the line control state back to the given \a validateFromState.
+
+ If \a edited is true and the change is valid, will emit textEdited() in
+ addition to textChanged(). Otherwise only emits textChanged() on a valid
+ change.
+
+ The \a update value is currently unused.
+*/
+bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bool /*edited*/)
+{
+ Q_Q(QQuickTextInput);
+
+ Q_UNUSED(update)
+
+ if (m_textDirty) {
+ // do validation
+ bool wasValidInput = m_validInput;
+ m_validInput = true;
+#ifndef QT_NO_VALIDATOR
+ if (m_validator) {
+ QString textCopy = m_text;
+ int cursorCopy = m_cursor;
+ m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
+ if (m_validInput) {
+ if (m_text != textCopy) {
+ internalSetText(textCopy, cursorCopy);
+ return true;
+ }
+ m_cursor = cursorCopy;
+
+ if (!m_tentativeCommit.isEmpty()) {
+ textCopy.insert(m_cursor, m_tentativeCommit);
+ bool validInput = m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid;
+ if (!validInput)
+ m_tentativeCommit.clear();
+ }
+ } else {
+ m_tentativeCommit.clear();
+ }
+ }
+#endif
+ if (validateFromState >= 0 && wasValidInput && !m_validInput) {
+ if (m_transactions.count())
+ return false;
+ internalUndo(validateFromState);
+ m_history.resize(m_undoState);
+ if (m_modifiedState > m_undoState)
+ m_modifiedState = -1;
+ m_validInput = true;
+ m_textDirty = false;
+ }
+ updateDisplayText();
+
+ if (m_textDirty) {
+ m_textDirty = false;
+ q_func()->q_textChanged();
+ }
+ }
+ if (m_selDirty) {
+ m_selDirty = false;
+ emit q->selectionChanged();
+ }
+ emitCursorPositionChanged();
+ return true;
+}
+
+/*!
+ \internal
+
+ An internal function for setting the text of the line control.
+*/
+void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool edited)
+{
+ Q_Q(QQuickTextInput);
+ internalDeselect();
+ QString oldText = m_text;
+ if (m_maskData) {
+ m_text = maskString(0, txt, true);
+ m_text += clearString(m_text.length(), m_maxLength - m_text.length());
+ } else {
+ m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
+ }
+ m_history.clear();
+ m_modifiedState = m_undoState = 0;
+ m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
+ m_textDirty = (oldText != m_text);
+
+ bool changed = finishChange(-1, true, edited);
+#ifdef QT_NO_ACCESSIBILITY
+ Q_UNUSED(changed)
+#else
+ if (changed)
+ QAccessible::updateAccessibility(q, 0, QAccessible::TextUpdated);
+#endif
+}
+
+
+/*!
+ \internal
+
+ Adds the given \a command to the undo history
+ of the line control. Does not apply the command.
+*/
+void QQuickTextInputPrivate::addCommand(const Command &cmd)
+{
+ if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) {
+ m_history.resize(m_undoState + 2);
+ m_history[m_undoState++] = Command(Separator, m_cursor, 0, m_selstart, m_selend);
+ } else {
+ m_history.resize(m_undoState + 1);
+ }
+ m_separator = false;
+ m_history[m_undoState++] = cmd;
+}
+
+/*!
+ \internal
+
+ Inserts the given string \a s into the line
+ control.
+
+ Also adds the appropriate commands into the undo history.
+ This function does not call finishChange(), and may leave the text
+ in an invalid state.
+*/
+void QQuickTextInputPrivate::internalInsert(const QString &s)
+{
+ if (hasSelectedText())
+ addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
+ if (m_maskData) {
+ QString ms = maskString(m_cursor, s);
+ for (int i = 0; i < (int) ms.length(); ++i) {
+ addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
+ addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
+ }
+ m_text.replace(m_cursor, ms.length(), ms);
+ m_cursor += ms.length();
+ m_cursor = nextMaskBlank(m_cursor);
+ m_textDirty = true;
+ } else {
+ int remaining = m_maxLength - m_text.length();
+ if (remaining != 0) {
+ m_text.insert(m_cursor, s.left(remaining));
+ for (int i = 0; i < (int) s.left(remaining).length(); ++i)
+ addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1));
+ m_textDirty = true;
+ }
+ }
+}
+
+/*!
+ \internal
+
+ deletes a single character from the current text. If \a wasBackspace,
+ the character prior to the cursor is removed. Otherwise the character
+ after the cursor is removed.
+
+ Also adds the appropriate commands into the undo history.
+ This function does not call finishChange(), and may leave the text
+ in an invalid state.
+*/
+void QQuickTextInputPrivate::internalDelete(bool wasBackspace)
+{
+ if (m_cursor < (int) m_text.length()) {
+ if (hasSelectedText())
+ addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
+ addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
+ m_cursor, m_text.at(m_cursor), -1, -1));
+ if (m_maskData) {
+ m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
+ addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
+ } else {
+ m_text.remove(m_cursor, 1);
+ }
+ m_textDirty = true;
+ }
+}
+
+/*!
+ \internal
+
+ removes the currently selected text from the line control.
+
+ Also adds the appropriate commands into the undo history.
+ This function does not call finishChange(), and may leave the text
+ in an invalid state.
+*/
+void QQuickTextInputPrivate::removeSelectedText()
+{
+ if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
+ separate();
+ int i ;
+ addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
+ if (m_selstart <= m_cursor && m_cursor < m_selend) {
+ // cursor is within the selection. Split up the commands
+ // to be able to restore the correct cursor position
+ for (i = m_cursor; i >= m_selstart; --i)
+ addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
+ for (i = m_selend - 1; i > m_cursor; --i)
+ addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
+ } else {
+ for (i = m_selend-1; i >= m_selstart; --i)
+ addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
+ }
+ if (m_maskData) {
+ m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart));
+ for (int i = 0; i < m_selend - m_selstart; ++i)
+ addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
+ } else {
+ m_text.remove(m_selstart, m_selend - m_selstart);
+ }
+ if (m_cursor > m_selstart)
+ m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
+ internalDeselect();
+ m_textDirty = true;
+ }
+}
+
+/*!
+ \internal
+
+ Parses the input mask specified by \a maskFields to generate
+ the mask data used to handle input masks.
+*/
+void QQuickTextInputPrivate::parseInputMask(const QString &maskFields)
+{
+ int delimiter = maskFields.indexOf(QLatin1Char(';'));
+ if (maskFields.isEmpty() || delimiter == 0) {
+ if (m_maskData) {
+ delete [] m_maskData;
+ m_maskData = 0;
+ m_maxLength = 32767;
+ internalSetText(QString());
+ }
+ return;
+ }
+
+ if (delimiter == -1) {
+ m_blank = QLatin1Char(' ');
+ m_inputMask = maskFields;
+ } else {
+ m_inputMask = maskFields.left(delimiter);
+ m_blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' ');
+ }
+
+ // calculate m_maxLength / m_maskData length
+ m_maxLength = 0;
+ QChar c = 0;
+ for (int i=0; i<m_inputMask.length(); i++) {
+ c = m_inputMask.at(i);
+ if (i > 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) {
+ m_maxLength++;
+ continue;
+ }
+ if (c != QLatin1Char('\\') && c != QLatin1Char('!') &&
+ c != QLatin1Char('<') && c != QLatin1Char('>') &&
+ c != QLatin1Char('{') && c != QLatin1Char('}') &&
+ c != QLatin1Char('[') && c != QLatin1Char(']'))
+ m_maxLength++;
+ }
+
+ delete [] m_maskData;
+ m_maskData = new MaskInputData[m_maxLength];
+
+ MaskInputData::Casemode m = MaskInputData::NoCaseMode;
+ c = 0;
+ bool s;
+ bool escape = false;
+ int index = 0;
+ for (int i = 0; i < m_inputMask.length(); i++) {
+ c = m_inputMask.at(i);
+ if (escape) {
+ s = true;
+ m_maskData[index].maskChar = c;
+ m_maskData[index].separator = s;
+ m_maskData[index].caseMode = m;
+ index++;
+ escape = false;
+ } else if (c == QLatin1Char('<')) {
+ m = MaskInputData::Lower;
+ } else if (c == QLatin1Char('>')) {
+ m = MaskInputData::Upper;
+ } else if (c == QLatin1Char('!')) {
+ m = MaskInputData::NoCaseMode;
+ } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) {
+ switch (c.unicode()) {
+ case 'A':
+ case 'a':
+ case 'N':
+ case 'n':
+ case 'X':
+ case 'x':
+ case '9':
+ case '0':
+ case 'D':
+ case 'd':
+ case '#':
+ case 'H':
+ case 'h':
+ case 'B':
+ case 'b':
+ s = false;
+ break;
+ case '\\':
+ escape = true;
+ default:
+ s = true;
+ break;
+ }
+
+ if (!escape) {
+ m_maskData[index].maskChar = c;
+ m_maskData[index].separator = s;
+ m_maskData[index].caseMode = m;
+ index++;
+ }
+ }
+ }
+ internalSetText(m_text);
+}
+
+
+/*!
+ \internal
+
+ checks if the key is valid compared to the inputMask
+*/
+bool QQuickTextInputPrivate::isValidInput(QChar key, QChar mask) const
+{
+ switch (mask.unicode()) {
+ case 'A':
+ if (key.isLetter())
+ return true;
+ break;
+ case 'a':
+ if (key.isLetter() || key == m_blank)
+ return true;
+ break;
+ case 'N':
+ if (key.isLetterOrNumber())
+ return true;
+ break;
+ case 'n':
+ if (key.isLetterOrNumber() || key == m_blank)
+ return true;
+ break;
+ case 'X':
+ if (key.isPrint())
+ return true;
+ break;
+ case 'x':
+ if (key.isPrint() || key == m_blank)
+ return true;
+ break;
+ case '9':
+ if (key.isNumber())
+ return true;
+ break;
+ case '0':
+ if (key.isNumber() || key == m_blank)
+ return true;
+ break;
+ case 'D':
+ if (key.isNumber() && key.digitValue() > 0)
+ return true;
+ break;
+ case 'd':
+ if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
+ return true;
+ break;
+ case '#':
+ if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_blank)
+ return true;
+ break;
+ case 'B':
+ if (key == QLatin1Char('0') || key == QLatin1Char('1'))
+ return true;
+ break;
+ case 'b':
+ if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == m_blank)
+ return true;
+ break;
+ case 'H':
+ if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')))
+ return true;
+ break;
+ case 'h':
+ if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == m_blank)
+ return true;
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+/*!
+ \internal
+
+ Returns true if the given text \a str is valid for any
+ validator or input mask set for the line control.
+
+ Otherwise returns false
+*/
+bool QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
+{
+#ifndef QT_NO_VALIDATOR
+ QString textCopy = str;
+ int cursorCopy = m_cursor;
+ if (m_validator && m_validator->validate(textCopy, cursorCopy)
+ != QValidator::Acceptable)
+ return false;
+#endif
+
+ if (!m_maskData)
+ return true;
+
+ if (str.length() != m_maxLength)
+ return false;
+
+ for (int i=0; i < m_maxLength; ++i) {
+ if (m_maskData[i].separator) {
+ if (str.at(i) != m_maskData[i].maskChar)
+ return false;
+ } else {
+ if (!isValidInput(str.at(i), m_maskData[i].maskChar))
+ return false;
+ }
+ }
+ return true;
+}
+
+/*!
+ \internal
+
+ Applies the inputMask on \a str starting from position \a pos in the mask. \a clear
+ specifies from where characters should be gotten when a separator is met in \a str - true means
+ that blanks will be used, false that previous input is used.
+ Calling this when no inputMask is set is undefined.
+*/
+QString QQuickTextInputPrivate::maskString(uint pos, const QString &str, bool clear) const
+{
+ if (pos >= (uint)m_maxLength)
+ return QString::fromLatin1("");
+
+ QString fill;
+ fill = clear ? clearString(0, m_maxLength) : m_text;
+
+ int strIndex = 0;
+ QString s = QString::fromLatin1("");
+ int i = pos;
+ while (i < m_maxLength) {
+ if (strIndex < str.length()) {
+ if (m_maskData[i].separator) {
+ s += m_maskData[i].maskChar;
+ if (str[(int)strIndex] == m_maskData[i].maskChar)
+ strIndex++;
+ ++i;
+ } else {
+ if (isValidInput(str[(int)strIndex], m_maskData[i].maskChar)) {
+ switch (m_maskData[i].caseMode) {
+ case MaskInputData::Upper:
+ s += str[(int)strIndex].toUpper();
+ break;
+ case MaskInputData::Lower:
+ s += str[(int)strIndex].toLower();
+ break;
+ default:
+ s += str[(int)strIndex];
+ }
+ ++i;
+ } else {
+ // search for separator first
+ int n = findInMask(i, true, true, str[(int)strIndex]);
+ if (n != -1) {
+ if (str.length() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[(int)strIndex]))) {
+ s += fill.mid(i, n-i+1);
+ i = n + 1; // update i to find + 1
+ }
+ } else {
+ // search for valid m_blank if not
+ n = findInMask(i, true, false, str[(int)strIndex]);
+ if (n != -1) {
+ s += fill.mid(i, n-i);
+ switch (m_maskData[n].caseMode) {
+ case MaskInputData::Upper:
+ s += str[(int)strIndex].toUpper();
+ break;
+ case MaskInputData::Lower:
+ s += str[(int)strIndex].toLower();
+ break;
+ default:
+ s += str[(int)strIndex];
+ }
+ i = n + 1; // updates i to find + 1
+ }
+ }
+ }
+ ++strIndex;
+ }
+ } else
+ break;
+ }
+
+ return s;
+}
+
+
+
+/*!
+ \internal
+
+ Returns a "cleared" string with only separators and blank chars.
+ Calling this when no inputMask is set is undefined.
+*/
+QString QQuickTextInputPrivate::clearString(uint pos, uint len) const
+{
+ if (pos >= (uint)m_maxLength)
+ return QString();
+
+ QString s;
+ int end = qMin((uint)m_maxLength, pos + len);
+ for (int i = pos; i < end; ++i)
+ if (m_maskData[i].separator)
+ s += m_maskData[i].maskChar;
+ else
+ s += m_blank;
+
+ return s;
+}
+
+/*!
+ \internal
+
+ Strips blank parts of the input in a QQuickTextInputPrivate when an inputMask is set,
+ separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1".
+*/
+QString QQuickTextInputPrivate::stripString(const QString &str) const
+{
+ if (!m_maskData)
+ return str;
+
+ QString s;
+ int end = qMin(m_maxLength, (int)str.length());
+ for (int i = 0; i < end; ++i)
+ if (m_maskData[i].separator)
+ s += m_maskData[i].maskChar;
+ else
+ if (str[i] != m_blank)
+ s += str[i];
+
+ return s;
+}
+
+/*!
+ \internal
+ searches forward/backward in m_maskData for either a separator or a m_blank
+*/
+int QQuickTextInputPrivate::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const
+{
+ if (pos >= m_maxLength || pos < 0)
+ return -1;
+
+ int end = forward ? m_maxLength : -1;
+ int step = forward ? 1 : -1;
+ int i = pos;
+
+ while (i != end) {
+ if (findSeparator) {
+ if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
+ return i;
+ } else {
+ if (!m_maskData[i].separator) {
+ if (searchChar.isNull())
+ return i;
+ else if (isValidInput(searchChar, m_maskData[i].maskChar))
+ return i;
+ }
+ }
+ i += step;
+ }
+ return -1;
+}
+
+void QQuickTextInputPrivate::internalUndo(int until)
+{
+ if (!isUndoAvailable())
+ return;
+ internalDeselect();
+ while (m_undoState && m_undoState > until) {
+ Command& cmd = m_history[--m_undoState];
+ switch (cmd.type) {
+ case Insert:
+ m_text.remove(cmd.pos, 1);
+ m_cursor = cmd.pos;
+ break;
+ case SetSelection:
+ m_selstart = cmd.selStart;
+ m_selend = cmd.selEnd;
+ m_cursor = cmd.pos;
+ break;
+ case Remove:
+ case RemoveSelection:
+ m_text.insert(cmd.pos, cmd.uc);
+ m_cursor = cmd.pos + 1;
+ break;
+ case Delete:
+ case DeleteSelection:
+ m_text.insert(cmd.pos, cmd.uc);
+ m_cursor = cmd.pos;
+ break;
+ case Separator:
+ continue;
+ }
+ if (until < 0 && m_undoState) {
+ Command& next = m_history[m_undoState-1];
+ if (next.type != cmd.type && next.type < RemoveSelection
+ && (cmd.type < RemoveSelection || next.type == Separator))
+ break;
+ }
+ }
+ m_textDirty = true;
+ emitCursorPositionChanged();
+}
+
+void QQuickTextInputPrivate::internalRedo()
+{
+ if (!isRedoAvailable())
+ return;
+ internalDeselect();
+ while (m_undoState < (int)m_history.size()) {
+ Command& cmd = m_history[m_undoState++];
+ switch (cmd.type) {
+ case Insert:
+ m_text.insert(cmd.pos, cmd.uc);
+ m_cursor = cmd.pos + 1;
+ break;
+ case SetSelection:
+ m_selstart = cmd.selStart;
+ m_selend = cmd.selEnd;
+ m_cursor = cmd.pos;
+ break;
+ case Remove:
+ case Delete:
+ case RemoveSelection:
+ case DeleteSelection:
+ m_text.remove(cmd.pos, 1);
+ m_selstart = cmd.selStart;
+ m_selend = cmd.selEnd;
+ m_cursor = cmd.pos;
+ break;
+ case Separator:
+ m_selstart = cmd.selStart;
+ m_selend = cmd.selEnd;
+ m_cursor = cmd.pos;
+ break;
+ }
+ if (m_undoState < (int)m_history.size()) {
+ Command& next = m_history[m_undoState];
+ if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
+ && (next.type < RemoveSelection || cmd.type == Separator))
+ break;
+ }
+ }
+ m_textDirty = true;
+ emitCursorPositionChanged();
+}
+
+/*!
+ \internal
+
+ If the current cursor position differs from the last emitted cursor
+ position, emits cursorPositionChanged().
+*/
+void QQuickTextInputPrivate::emitCursorPositionChanged()
+{
+ Q_Q(QQuickTextInput);
+ if (m_cursor != m_lastCursorPos) {
+ m_lastCursorPos = m_cursor;
+
+ q->updateCursorRectangle();
+ emit q->cursorPositionChanged();
+ // XXX todo - not in 4.8?
+ #if 0
+ resetCursorBlinkTimer();
+ #endif
+
+ if (!hasSelectedText()) {
+ if (lastSelectionStart != m_cursor) {
+ lastSelectionStart = m_cursor;
+ emit q->selectionStartChanged();
+ }
+ if (lastSelectionEnd != m_cursor) {
+ lastSelectionEnd = m_cursor;
+ emit q->selectionEndChanged();
+ }
+ }
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(q, 0, QAccessible::TextCaretMoved);
+#endif
+ }
+}
+
+
+void QQuickTextInputPrivate::setCursorBlinkPeriod(int msec)
+{
+ Q_Q(QQuickTextInput);
+ if (msec == m_blinkPeriod)
+ return;
+ if (m_blinkTimer) {
+ q->killTimer(m_blinkTimer);
+ }
+ if (msec) {
+ m_blinkTimer = q->startTimer(msec / 2);
+ m_blinkStatus = 1;
+ } else {
+ m_blinkTimer = 0;
+ if (m_blinkStatus == 1)
+ emit q->updateRect(inputMask().isEmpty() ? q->cursorRectangle() : QRect());
+ }
+ m_blinkPeriod = msec;
+}
+
+void QQuickTextInputPrivate::resetCursorBlinkTimer()
+{
+ Q_Q(QQuickTextInput);
+ if (m_blinkPeriod == 0 || m_blinkTimer == 0)
+ return;
+ q->killTimer(m_blinkTimer);
+ m_blinkTimer = q->startTimer(m_blinkPeriod / 2);
+ m_blinkStatus = 1;
+}
+
+void QQuickTextInput::timerEvent(QTimerEvent *event)
+{
+ Q_D(QQuickTextInput);
+ if (event->timerId() == d->m_blinkTimer) {
+ d->m_blinkStatus = !d->m_blinkStatus;
+ updateRect(inputMask().isEmpty() ? cursorRectangle() : QRect());
+ } else if (event->timerId() == d->m_deleteAllTimer) {
+ killTimer(d->m_deleteAllTimer);
+ d->m_deleteAllTimer = 0;
+ d->clear();
+ }
+}
+
+void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
+{
+ Q_Q(QQuickTextInput);
+ bool inlineCompletionAccepted = false;
+
+ if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
+ if (hasAcceptableInput(m_text) || fixup()) {
+ emit q->accepted();
+ }
+ if (inlineCompletionAccepted)
+ event->accept();
+ else
+ event->ignore();
+ return;
+ }
+
+ if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit
+ && !m_passwordEchoEditing
+ && !m_readOnly
+ && !event->text().isEmpty()
+ && !(event->modifiers() & Qt::ControlModifier)) {
+ // Clear the edit and reset to normal echo mode while editing; the
+ // echo mode switches back when the edit loses focus
+ // ### resets current content. dubious code; you can
+ // navigate with keys up, down, back, and select(?), but if you press
+ // "left" or "right" it clears?
+ updatePasswordEchoEditing(true);
+ clear();
+ }
+
+ bool unknown = false;
+ bool visual = cursorMoveStyle() == Qt::VisualMoveStyle;
+
+ if (false) {
+ }
+#ifndef QT_NO_SHORTCUT
+ else if (event == QKeySequence::Undo) {
+ if (!m_readOnly)
+ undo();
+ }
+ else if (event == QKeySequence::Redo) {
+ if (!m_readOnly)
+ redo();
+ }
+ else if (event == QKeySequence::SelectAll) {
+ selectAll();
+ }
+#ifndef QT_NO_CLIPBOARD
+ else if (event == QKeySequence::Copy) {
+ copy();
+ }
+ else if (event == QKeySequence::Paste) {
+ if (!m_readOnly) {
+ QClipboard::Mode mode = QClipboard::Clipboard;
+ paste(mode);
+ }
+ }
+ else if (event == QKeySequence::Cut) {
+ if (!m_readOnly) {
+ copy();
+ del();
+ }
+ }
+ else if (event == QKeySequence::DeleteEndOfLine) {
+ if (!m_readOnly) {
+ setSelection(m_cursor, end());
+ copy();
+ del();
+ }
+ }
+#endif //QT_NO_CLIPBOARD
+ else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
+ home(0);
+ }
+ else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
+ end(0);
+ }
+ else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
+ home(1);
+ }
+ else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
+ end(1);
+ }
+ else if (event == QKeySequence::MoveToNextChar) {
+ if (hasSelectedText()) {
+ moveCursor(selectionEnd(), false);
+ } else {
+ cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
+ }
+ }
+ else if (event == QKeySequence::SelectNextChar) {
+ cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
+ }
+ else if (event == QKeySequence::MoveToPreviousChar) {
+ if (hasSelectedText()) {
+ moveCursor(selectionStart(), false);
+ } else {
+ cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
+ }
+ }
+ else if (event == QKeySequence::SelectPreviousChar) {
+ cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
+ }
+ else if (event == QKeySequence::MoveToNextWord) {
+ if (m_echoMode == QQuickTextInput::Normal)
+ layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
+ else
+ layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
+ }
+ else if (event == QKeySequence::MoveToPreviousWord) {
+ if (m_echoMode == QQuickTextInput::Normal)
+ layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
+ else if (!m_readOnly) {
+ layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
+ }
+ }
+ else if (event == QKeySequence::SelectNextWord) {
+ if (m_echoMode == QQuickTextInput::Normal)
+ layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
+ else
+ layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
+ }
+ else if (event == QKeySequence::SelectPreviousWord) {
+ if (m_echoMode == QQuickTextInput::Normal)
+ layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
+ else
+ layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
+ }
+ else if (event == QKeySequence::Delete) {
+ if (!m_readOnly)
+ del();
+ }
+ else if (event == QKeySequence::DeleteEndOfWord) {
+ if (!m_readOnly) {
+ cursorWordForward(true);
+ del();
+ }
+ }
+ else if (event == QKeySequence::DeleteStartOfWord) {
+ if (!m_readOnly) {
+ cursorWordBackward(true);
+ del();
+ }
+ }
+#endif // QT_NO_SHORTCUT
+ else {
+ bool handled = false;
+ if (event->modifiers() & Qt::ControlModifier) {
+ switch (event->key()) {
+ case Qt::Key_Backspace:
+ if (!m_readOnly) {
+ cursorWordBackward(true);
+ del();
+ }
+ break;
+ default:
+ if (!handled)
+ unknown = true;
+ }
+ } else { // ### check for *no* modifier
+ switch (event->key()) {
+ case Qt::Key_Backspace:
+ if (!m_readOnly) {
+ backspace();
+ }
+ break;
+ default:
+ if (!handled)
+ unknown = true;
+ }
+ }
+ }
+
+ if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
+ setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
+ unknown = false;
+ }
+
+ if (unknown && !m_readOnly) {
+ QString t = event->text();
+ if (!t.isEmpty() && t.at(0).isPrint()) {
+ insert(t);
+ event->accept();
+ return;
+ }
+ }
+
+ if (unknown)
+ event->ignore();
+ else
+ event->accept();
+}
+
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index 3ea86477a1..7a07de60a3 100644
--- a/src/quick/items/qquicktextinput_p.h
+++ b/src/quick/items/qquicktextinput_p.h
@@ -256,6 +256,7 @@ protected:
void mouseUngrabEvent();
bool event(QEvent *e);
void focusInEvent(QFocusEvent *event);
+ void timerEvent(QTimerEvent *event);
virtual void itemChange(ItemChange, const ItemChangeData &);
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data);
@@ -276,7 +277,6 @@ private Q_SLOTS:
void q_textChanged();
void selectionChanged();
void createCursor();
- void cursorPosChanged();
void updateCursorRectangle();
void updateRect(const QRect &r = QRect());
void q_canPasteChanged();
diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h
index 119df5fd39..b410bfd187 100644
--- a/src/quick/items/qquicktextinput_p_p.h
+++ b/src/quick/items/qquicktextinput_p_p.h
@@ -47,12 +47,13 @@
#include "qquicktext_p.h"
#include "qquickimplicitsizeitem_p_p.h"
-#include "qquicklinecontrol_p.h"
-
#include <QtDeclarative/qdeclarative.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qpointer.h>
+#include <QtGui/qclipboard.h>
#include <QtGui/qguiapplication.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qtextlayout.h>
#include <QtGui/qstylehints.h>
@@ -75,26 +76,50 @@ class Q_AUTOTEST_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPr
Q_DECLARE_PUBLIC(QQuickTextInput)
public:
QQuickTextInputPrivate()
- : control(new QQuickLineControl(QString()))
- , color((QRgb)0)
- , style(QQuickText::Normal)
- , styleColor((QRgb)0)
- , hAlign(QQuickTextInput::AlignLeft)
- , mouseSelectionMode(QQuickTextInput::SelectCharacters)
- , inputMethodHints(Qt::ImhNone)
- , textNode(0)
- , hscroll(0)
- , oldScroll(0)
- , oldValidity(false)
- , focused(false)
- , focusOnPress(true)
- , cursorVisible(false)
- , autoScroll(true)
- , selectByMouse(false)
- , canPaste(false)
- , hAlignImplicit(true)
- , selectPressed(false)
- , textLayoutDirty(true)
+ : color((QRgb)0)
+ , styleColor((QRgb)0)
+ , textNode(0)
+ , m_maskData(0)
+ , hscroll(0)
+ , oldScroll(0)
+ , m_cursor(0)
+ , m_preeditCursor(0)
+ , m_cursorWidth(1)
+ , m_blinkPeriod(0)
+ , m_blinkTimer(0)
+ , m_deleteAllTimer(0)
+ , m_ascent(0)
+ , m_maxLength(32767)
+ , m_lastCursorPos(-1)
+ , m_modifiedState(0)
+ , m_undoState(0)
+ , m_selstart(0)
+ , m_selend(0)
+ , style(QQuickText::Normal)
+ , hAlign(QQuickTextInput::AlignLeft)
+ , mouseSelectionMode(QQuickTextInput::SelectCharacters)
+ , inputMethodHints(Qt::ImhNone)
+ , m_layoutDirection(Qt::LayoutDirectionAuto)
+ , m_passwordCharacter(QLatin1Char('*'))
+ , oldValidity(false)
+ , focused(false)
+ , focusOnPress(true)
+ , cursorVisible(false)
+ , autoScroll(true)
+ , selectByMouse(false)
+ , canPaste(false)
+ , hAlignImplicit(true)
+ , selectPressed(false)
+ , textLayoutDirty(true)
+ , m_hideCursor(false)
+ , m_separator(0)
+ , m_readOnly(0)
+ , m_echoMode(QQuickTextInput::Normal)
+ , m_textDirty(0)
+ , m_selDirty(0)
+ , m_validInput(1)
+ , m_blinkStatus(0)
+ , m_passwordEchoEditing(false)
{
}
@@ -102,44 +127,65 @@ public:
{
}
- int xToPos(int x, QTextLine::CursorPosition betweenOrOn = QTextLine::CursorBetweenCharacters) const
- {
- Q_Q(const QQuickTextInput);
- QRect cr = q->boundingRect().toRect();
- x-= cr.x() - hscroll;
- return control->xToPos(x, betweenOrOn);
- }
-
void init();
void startCreatingCursor();
void updateHorizontalScroll();
bool determineHorizontalAlignment();
bool setHAlign(QQuickTextInput::HAlignment, bool forceAlign = false);
void mirrorChange();
- int calculateTextWidth();
bool sendMouseEventToInputContext(QMouseEvent *event);
void updateInputMethodHints();
void hideCursor();
void showCursor();
- QQuickLineControl* control;
+ 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
+ };
+
+ QTextLayout m_textLayout;
+ QString m_text;
+ QString m_inputMask;
+ QString m_cancelText;
+ QString m_tentativeCommit;
+ QPalette m_palette;
QFont font;
QFont sourceFont;
QColor color;
QColor selectionColor;
QColor selectedTextColor;
- QQuickText::TextStyle style;
QColor styleColor;
- QQuickTextInput::HAlignment hAlign;
- QQuickTextInput::SelectionMode mouseSelectionMode;
- Qt::InputMethodHints inputMethodHints;
QPointer<QDeclarativeComponent> cursorComponent;
QPointer<QQuickItem> cursorItem;
+#ifndef QT_NO_VALIDATOR
+ QPointer<QValidator> m_validator;
+#endif
QPointF pressPos;
QQuickTextNode *textNode;
+ MaskInputData *m_maskData;
QElapsedTimer tripleClickTimer;
QPoint tripleClickStartPoint;
+ QList<int> m_transactions;
+ QVector<Command> m_history;
int lastSelectionStart;
int lastSelectionEnd;
@@ -147,6 +193,28 @@ public:
int oldWidth;
int hscroll;
int oldScroll;
+ int m_cursor;
+ int m_preeditCursor;
+ int m_cursorWidth;
+ int m_blinkPeriod; // 0 for non-blinking cursor
+ int m_blinkTimer;
+ int m_deleteAllTimer;
+ int m_ascent;
+ int m_maxLength;
+ int m_lastCursorPos;
+ int m_modifiedState;
+ int m_undoState;
+ int m_selstart;
+ int m_selend;
+
+ QQuickText::TextStyle style;
+ QQuickTextInput::HAlignment hAlign;
+ QQuickTextInput::SelectionMode mouseSelectionMode;
+ Qt::InputMethodHints inputMethodHints;
+ Qt::LayoutDirection m_layoutDirection;
+
+ QChar m_blank;
+ QChar m_passwordCharacter;
bool oldValidity:1;
bool focused:1;
@@ -159,12 +227,213 @@ public:
bool selectPressed:1;
bool textLayoutDirty:1;
+ uint m_hideCursor : 1; // used to hide the m_cursor inside preedit areas
+ uint m_separator : 1;
+ uint m_readOnly : 1;
+ uint m_echoMode : 2;
+ uint m_textDirty : 1;
+ uint m_selDirty : 1;
+ uint m_validInput : 1;
+ uint m_blinkStatus : 1;
+ uint m_passwordEchoEditing;
+
static inline QQuickTextInputPrivate *get(QQuickTextInput *t) {
return t->d_func();
}
bool hasPendingTripleClick() const {
return !tripleClickTimer.hasExpired(qApp->styleHints()->mouseDoubleClickInterval());
}
+
+
+ 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_modifiedState = m_undoState = 0; }
+
+ bool isModified() const { return m_modifiedState != m_undoState; }
+ void setModified(bool modified) { m_modifiedState = modified ? -1 : m_undoState; }
+
+ 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; }
+
+ int calculateTextHeight() const { return qRound(m_textLayout.lineAt(0).height()); }
+ int calculateTextWidth() const { return qRound(m_textLayout.lineAt(0).naturalTextWidth()); }
+ int ascent() const { return m_ascent; }
+
+ 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; }
+ bool inSelection(int x) const
+ {
+ if (m_selstart >= m_selend)
+ return false;
+ int pos = xToPos(x, QTextLine::CursorOnCharacter);
+ return pos >= m_selstart && pos < m_selend;
+ }
+
+ void removeSelection()
+ {
+ int priorState = m_undoState;
+ removeSelectedText();
+ finishChange(priorState);
+ }
+
+ int start() const { return 0; }
+ int end() const { return m_text.length(); }
+
+ QString realText() const;
+
+#ifndef QT_NO_CLIPBOARD
+ void copy(QClipboard::Mode mode = QClipboard::Clipboard) const;
+ void paste(QClipboard::Mode mode = QClipboard::Clipboard);
+#endif
+
+ void commitPreedit();
+
+ 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); }
+
+ int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const;
+
+ qreal cursorToX(int cursor) const { return m_textLayout.lineAt(0).cursorToX(cursor); }
+ qreal cursorToX() const
+ {
+ int cursor = m_cursor;
+ if (m_preeditCursor != -1)
+ cursor += m_preeditCursor;
+ return cursorToX(cursor);
+ }
+
+ 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 undo() { internalUndo(); finishChange(-1, true); }
+ void redo() { internalRedo(); finishChange(); }
+ 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
+#ifndef QT_NO_IM
+ bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); }
+#endif
+
+ QString preeditAreaText() const { return m_textLayout.preeditAreaText(); }
+
+ void updatePasswordEchoEditing(bool editing);
+
+ Qt::LayoutDirection layoutDirection() const {
+ if (m_layoutDirection == Qt::LayoutDirectionAuto) {
+ if (m_text.isEmpty())
+ return QGuiApplication::keyboardInputDirection();
+ return m_text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight;
+ }
+ return m_layoutDirection;
+ }
+ void setLayoutDirection(Qt::LayoutDirection direction)
+ {
+ if (direction != m_layoutDirection) {
+ m_layoutDirection = direction;
+ updateDisplayText();
+ }
+ }
+
+ void processInputMethodEvent(QInputMethodEvent *event);
+ void processKeyEvent(QKeyEvent* ev);
+
+ void setCursorBlinkPeriod(int msec);
+ void resetCursorBlinkTimer();
+
+private:
+ void init(const QString &txt);
+ 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 emitCursorPositionChanged();
+
+ bool finishChange(int validateFromState = -1, bool update = false, bool edited = true);
+
+ void addCommand(const Command& cmd);
+
+ inline void separate() { m_separator = true; }
+
+
+ // masking
+ void parseInputMask(const QString &maskFields);
+ bool isValidInput(QChar key, QChar mask) const;
+ bool hasAcceptableInput(const QString &text) const;
+ 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