Skip to content

Commit

Permalink
Delay masking the last character in Password echo mode.
Browse files Browse the repository at this point in the history
If QT_GUI_PASSWORD_ECHO_DELAY is defined in qplatformdefs.h with an
integer value in milliseconds, QLineEdit and TextInput will display
the last character entered unmasked for that delay period and then
mask the character as normal.  If QT_GUI_PASSWORD_ECHO_DELAY is
not defined then the behaviour is unchanged.

Task-number: QTBUG-17003
Task-number: QTBUG-20719
(cherry picked from commit f9e7aee2019d321edd655bfde7de43f20a106971)

Change-Id: I9a8647a0adeb94fc6beea949cdce7336671c898e
Reviewed-by: Martin Jones <martin.jones@nokia.com>
  • Loading branch information
Andrew den Exter authored and Qt by Nokia committed Dec 20, 2011
1 parent 59e607a commit 657f6d7
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 8 deletions.
46 changes: 43 additions & 3 deletions src/quick/items/qquicktextinput.cpp
Expand Up @@ -62,6 +62,10 @@ QT_BEGIN_NAMESPACE

DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)

#ifdef QT_GUI_PASSWORD_ECHO_DELAY
static const int qt_passwordEchoDelay = QT_GUI_PASSWORD_ECHO_DELAY;
#endif

/*!
\qmlclass TextInput QQuickTextInput
\inqmlmodule QtQuick 2
Expand Down Expand Up @@ -976,6 +980,7 @@ void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
Q_D(QQuickTextInput);
if (echoMode() == echo)
return;
d->cancelPasswordEchoTimer();
d->m_echoMode = echo;
d->m_passwordEchoEditing = false;
d->updateInputMethodHints();
Expand Down Expand Up @@ -1992,8 +1997,13 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
bool hasFocus = value.boolValue;
d->focused = hasFocus;
setCursorVisible(hasFocus); // ### refactor: && d->canvas && d->canvas->hasFocus()
if (echoMode() == QQuickTextInput::PasswordEchoOnEdit && !hasFocus)
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
if (!hasFocus && (d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive())) {
#else
if (!hasFocus && d->m_passwordEchoEditing) {
#endif
d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
}
if (!hasFocus)
d->deselect();
}
Expand Down Expand Up @@ -2158,9 +2168,25 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
else
str = m_text;

if (m_echoMode == QQuickTextInput::Password
|| (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing))
if (m_echoMode == QQuickTextInput::Password) {
str.fill(m_passwordCharacter);
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
if (m_passwordEchoTimer.isActive() && m_cursor > 0 && m_cursor <= m_text.length()) {
int cursor = m_cursor - 1;
QChar uc = m_text.at(cursor);
str[cursor] = uc;
if (cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
// second half of a surrogate, check if we have the first half as well,
// if yes restore both at once
uc = m_text.at(cursor - 1);
if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00)
str[cursor - 1] = uc;
}
}
#endif
} else if (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
Expand Down Expand Up @@ -2437,6 +2463,7 @@ void QQuickTextInputPrivate::init(const QString &txt)
*/
void QQuickTextInputPrivate::updatePasswordEchoEditing(bool editing)
{
cancelPasswordEchoTimer();
m_passwordEchoEditing = editing;
updateDisplayText();
}
Expand Down Expand Up @@ -2775,6 +2802,11 @@ void QQuickTextInputPrivate::addCommand(const Command &cmd)
*/
void QQuickTextInputPrivate::internalInsert(const QString &s)
{
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
Q_Q(QQuickTextInput);
if (m_echoMode == QQuickTextInput::Password)
m_passwordEchoTimer.start(qt_passwordEchoDelay, q);
#endif
if (hasSelectedText())
addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
if (m_maskData) {
Expand Down Expand Up @@ -2812,6 +2844,7 @@ void QQuickTextInputPrivate::internalInsert(const QString &s)
void QQuickTextInputPrivate::internalDelete(bool wasBackspace)
{
if (m_cursor < (int) m_text.length()) {
cancelPasswordEchoTimer();
if (hasSelectedText())
addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
Expand All @@ -2838,6 +2871,7 @@ void QQuickTextInputPrivate::internalDelete(bool wasBackspace)
void QQuickTextInputPrivate::removeSelectedText()
{
if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
cancelPasswordEchoTimer();
separate();
int i ;
addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
Expand Down Expand Up @@ -3236,6 +3270,7 @@ void QQuickTextInputPrivate::internalUndo(int until)
{
if (!isUndoAvailable())
return;
cancelPasswordEchoTimer();
internalDeselect();
while (m_undoState && m_undoState > until) {
Command& cmd = m_history[--m_undoState];
Expand Down Expand Up @@ -3392,6 +3427,11 @@ void QQuickTextInput::timerEvent(QTimerEvent *event)
killTimer(d->m_deleteAllTimer);
d->m_deleteAllTimer = 0;
d->clear();
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
} else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
d->m_passwordEchoTimer.stop();
d->updateDisplayText();
#endif
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/quick/items/qquicktextinput_p_p.h
Expand Up @@ -50,12 +50,14 @@
#include <QtDeclarative/qdeclarative.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qpointer.h>
#include <QtCore/qbasictimer.h>
#include <QtGui/qclipboard.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qpalette.h>
#include <QtGui/qtextlayout.h>
#include <QtGui/qstylehints.h>

#include "qplatformdefs.h"

//
// W A R N I N G
Expand Down Expand Up @@ -204,6 +206,9 @@ class Q_AUTOTEST_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPr
int m_blinkPeriod; // 0 for non-blinking cursor
int m_blinkTimer;
int m_deleteAllTimer;
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
QBasicTimer m_passwordEchoTimer;
#endif
int m_ascent;
int m_maxLength;
int m_lastCursorPos;
Expand Down Expand Up @@ -367,6 +372,12 @@ class Q_AUTOTEST_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPr

void updatePasswordEchoEditing(bool editing);

void cancelPasswordEchoTimer() {
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
m_passwordEchoTimer.stop();
#endif
}

Qt::LayoutDirection layoutDirection() const {
if (m_layoutDirection == Qt::LayoutDirectionAuto) {
if (m_text.isEmpty())
Expand Down
16 changes: 11 additions & 5 deletions tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp
Expand Up @@ -2227,12 +2227,11 @@ void tst_qquicktextinput::echoMode()
}

#ifdef QT_GUI_PASSWORD_ECHO_DELAY
void tst_qdeclarativetextinput::passwordEchoDelay()
void tst_qquicktextinput::passwordEchoDelay()
{
QQuickView canvas(QUrl::fromLocalFile(TESTDATA("echoMode.qml")));
canvas.show();
canvas.setFocus();
QGuiApplication::setActiveWindow(&canvas);
canvas.requestActivateWindow();
QTest::qWaitForWindowShown(&canvas);
QTRY_COMPARE(&canvas, qGuiApp->focusWindow());

Expand All @@ -2242,7 +2241,7 @@ void tst_qdeclarativetextinput::passwordEchoDelay()

QChar fillChar = QLatin1Char('*');

input->setEchoMode(QDeclarativeTextInput::Password);
input->setEchoMode(QQuickTextInput::Password);
QCOMPARE(input->displayText(), QString(8, fillChar));
input->setText(QString());
QCOMPARE(input->displayText(), QString());
Expand Down Expand Up @@ -2273,8 +2272,15 @@ void tst_qdeclarativetextinput::passwordEchoDelay()

QInputMethodEvent ev;
ev.setCommitString(QLatin1String("7"));
QGuiApplication::sendEvent(&canvas, &ev);
QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev);
QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));

input->setCursorPosition(3);
QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
QTest::keyPress(&canvas, 'a');
QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
QTest::keyPress(&canvas, Qt::Key_Backspace);
QCOMPARE(input->displayText(), QString(8, fillChar));
}
#endif

Expand Down

0 comments on commit 657f6d7

Please sign in to comment.