diff options
Diffstat (limited to 'src/declarative/items/qsgtextinput.cpp')
-rw-r--r-- | src/declarative/items/qsgtextinput.cpp | 1294 |
1 files changed, 1294 insertions, 0 deletions
diff --git a/src/declarative/items/qsgtextinput.cpp b/src/declarative/items/qsgtextinput.cpp new file mode 100644 index 0000000000..4eab28bbcf --- /dev/null +++ b/src/declarative/items/qsgtextinput.cpp @@ -0,0 +1,1294 @@ +// Commit: 47712d1f330e4b22ce6dd30e7557288ef7f7fca0 +/**************************************************************************** +** +** 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 QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgtextinput_p.h" +#include "qsgtextinput_p_p.h" +#include "qsgcanvas.h" + +#include <private/qdeclarativeglobal_p.h> +#include <private/qwidget_p.h> + +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtGui/qgraphicssceneevent.h> +#include <QtGui/qinputcontext.h> +#include <QTextBoundaryFinder> +#include <qstyle.h> + +QT_BEGIN_NAMESPACE + +QWidgetPrivate *qt_widget_private(QWidget *widget); + +QSGTextInput::QSGTextInput(QSGItem* parent) +: QSGImplicitSizePaintedItem(*(new QSGTextInputPrivate), parent) +{ + Q_D(QSGTextInput); + d->init(); +} + +QSGTextInput::~QSGTextInput() +{ +} + +QString QSGTextInput::text() const +{ + Q_D(const QSGTextInput); + return d->control->text(); +} + +void QSGTextInput::setText(const QString &s) +{ + Q_D(QSGTextInput); + if(s == text()) + return; + d->control->setText(s); +} + +QFont QSGTextInput::font() const +{ + Q_D(const QSGTextInput); + return d->sourceFont; +} + +void QSGTextInput::setFont(const QFont &font) +{ + Q_D(QSGTextInput); + if (d->sourceFont == font) + return; + + d->sourceFont = font; + QFont oldFont = d->font; + d->font = font; + if (d->font.pointSizeF() != -1) { + // 0.5pt resolution + qreal size = qRound(d->font.pointSizeF()*2.0); + d->font.setPointSizeF(size/2.0); + } + if (oldFont != d->font) { + d->control->setFont(d->font); + if(d->cursorItem){ + d->cursorItem->setHeight(QFontMetrics(d->font).height()); + moveCursor(); + } + updateSize(); + } + emit fontChanged(d->sourceFont); +} + +QColor QSGTextInput::color() const +{ + Q_D(const QSGTextInput); + return d->color; +} + +void QSGTextInput::setColor(const QColor &c) +{ + Q_D(QSGTextInput); + if (c != d->color) { + d->color = c; + update(); + emit colorChanged(c); + } +} + +QColor QSGTextInput::selectionColor() const +{ + Q_D(const QSGTextInput); + return d->selectionColor; +} + +void QSGTextInput::setSelectionColor(const QColor &color) +{ + Q_D(QSGTextInput); + if (d->selectionColor == color) + return; + + d->selectionColor = color; + QPalette p = d->control->palette(); + p.setColor(QPalette::Highlight, d->selectionColor); + d->control->setPalette(p); + if (d->control->hasSelectedText()) + update(); + emit selectionColorChanged(color); +} + +QColor QSGTextInput::selectedTextColor() const +{ + Q_D(const QSGTextInput); + return d->selectedTextColor; +} + +void QSGTextInput::setSelectedTextColor(const QColor &color) +{ + Q_D(QSGTextInput); + if (d->selectedTextColor == color) + return; + + d->selectedTextColor = color; + QPalette p = d->control->palette(); + p.setColor(QPalette::HighlightedText, d->selectedTextColor); + d->control->setPalette(p); + if (d->control->hasSelectedText()) + update(); + emit selectedTextColorChanged(color); +} + +QSGTextInput::HAlignment QSGTextInput::hAlign() const +{ + Q_D(const QSGTextInput); + return d->hAlign; +} + +void QSGTextInput::setHAlign(HAlignment align) +{ + Q_D(QSGTextInput); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + updateRect(); + d->updateHorizontalScroll(); + } +} + +void QSGTextInput::resetHAlign() +{ + Q_D(QSGTextInput); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) { + updateRect(); + d->updateHorizontalScroll(); + } +} + +QSGTextInput::HAlignment QSGTextInput::effectiveHAlign() const +{ + Q_D(const QSGTextInput); + QSGTextInput::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QSGTextInput::AlignLeft: + effectiveAlignment = QSGTextInput::AlignRight; + break; + case QSGTextInput::AlignRight: + effectiveAlignment = QSGTextInput::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QSGTextInputPrivate::setHAlign(QSGTextInput::HAlignment alignment, bool forceAlign) +{ + Q_Q(QSGTextInput); + if ((hAlign != alignment || forceAlign) && alignment <= QSGTextInput::AlignHCenter) { // justify not supported + QSGTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + emit q->horizontalAlignmentChanged(alignment); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + return true; + } + return false; +} + +bool QSGTextInputPrivate::determineHorizontalAlignment() +{ + if (hAlignImplicit) { + // if no explicit alignment has been set, follow the natural layout direction of the text + QString text = control->text(); + bool isRightToLeft = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft(); + return setHAlign(isRightToLeft ? QSGTextInput::AlignRight : QSGTextInput::AlignLeft); + } + return false; +} + +void QSGTextInputPrivate::mirrorChange() +{ + Q_Q(QSGTextInput); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QSGTextInput::AlignRight || hAlign == QSGTextInput::AlignLeft)) { + q->updateRect(); + updateHorizontalScroll(); + emit q->effectiveHorizontalAlignmentChanged(); + } + } +} + +bool QSGTextInput::isReadOnly() const +{ + Q_D(const QSGTextInput); + return d->control->isReadOnly(); +} + +void QSGTextInput::setReadOnly(bool ro) +{ + Q_D(QSGTextInput); + if (d->control->isReadOnly() == ro) + return; + + setFlag(QSGItem::ItemAcceptsInputMethod, !ro); + d->control->setReadOnly(ro); + + emit readOnlyChanged(ro); +} + +int QSGTextInput::maxLength() const +{ + Q_D(const QSGTextInput); + return d->control->maxLength(); +} + +void QSGTextInput::setMaxLength(int ml) +{ + Q_D(QSGTextInput); + if (d->control->maxLength() == ml) + return; + + d->control->setMaxLength(ml); + + emit maximumLengthChanged(ml); +} + +bool QSGTextInput::isCursorVisible() const +{ + Q_D(const QSGTextInput); + return d->cursorVisible; +} + +void QSGTextInput::setCursorVisible(bool on) +{ + Q_D(QSGTextInput); + if (d->cursorVisible == on) + return; + d->cursorVisible = on; + d->control->setCursorBlinkPeriod(on?QApplication::cursorFlashTime():0); + QRect r = d->control->cursorRect(); + if (d->control->inputMask().isEmpty()) + updateRect(r); + else + updateRect(); + emit cursorVisibleChanged(d->cursorVisible); +} + +int QSGTextInput::cursorPosition() const +{ + Q_D(const QSGTextInput); + return d->control->cursor(); +} +void QSGTextInput::setCursorPosition(int cp) +{ + Q_D(QSGTextInput); + if (cp < 0 || cp > d->control->text().length()) + return; + d->control->moveCursor(cp); +} + +QRect QSGTextInput::cursorRectangle() const +{ + Q_D(const QSGTextInput); + QRect r = d->control->cursorRect(); + // Scroll and make consistent with TextEdit + // QLineControl 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; +} + +int QSGTextInput::selectionStart() const +{ + Q_D(const QSGTextInput); + return d->lastSelectionStart; +} + +int QSGTextInput::selectionEnd() const +{ + Q_D(const QSGTextInput); + return d->lastSelectionEnd; +} + +void QSGTextInput::select(int start, int end) +{ + Q_D(QSGTextInput); + if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length()) + return; + d->control->setSelection(start, end-start); +} + +QString QSGTextInput::selectedText() const +{ + Q_D(const QSGTextInput); + return d->control->selectedText(); +} + +bool QSGTextInput::focusOnPress() const +{ + Q_D(const QSGTextInput); + return d->focusOnPress; +} + +void QSGTextInput::setFocusOnPress(bool b) +{ + Q_D(QSGTextInput); + if (d->focusOnPress == b) + return; + + d->focusOnPress = b; + + emit activeFocusOnPressChanged(d->focusOnPress); +} + +bool QSGTextInput::autoScroll() const +{ + Q_D(const QSGTextInput); + return d->autoScroll; +} + +void QSGTextInput::setAutoScroll(bool b) +{ + Q_D(QSGTextInput); + if (d->autoScroll == b) + return; + + d->autoScroll = b; + //We need to repaint so that the scrolling is taking into account. + updateSize(true); + d->updateHorizontalScroll(); + emit autoScrollChanged(d->autoScroll); +} + +#ifndef QT_NO_VALIDATOR +QValidator* QSGTextInput::validator() const +{ + Q_D(const QSGTextInput); + //###const cast isn't good, but needed for property system? + return const_cast<QValidator*>(d->control->validator()); +} + +void QSGTextInput::setValidator(QValidator* v) +{ + Q_D(QSGTextInput); + if (d->control->validator() == v) + return; + + d->control->setValidator(v); + if(!d->control->hasAcceptableInput()){ + d->oldValidity = false; + emit acceptableInputChanged(); + } + + emit validatorChanged(); +} +#endif // QT_NO_VALIDATOR + +QString QSGTextInput::inputMask() const +{ + Q_D(const QSGTextInput); + return d->control->inputMask(); +} + +void QSGTextInput::setInputMask(const QString &im) +{ + Q_D(QSGTextInput); + if (d->control->inputMask() == im) + return; + + d->control->setInputMask(im); + emit inputMaskChanged(d->control->inputMask()); +} + +bool QSGTextInput::hasAcceptableInput() const +{ + Q_D(const QSGTextInput); + return d->control->hasAcceptableInput(); +} + +void QSGTextInputPrivate::updateInputMethodHints() +{ + Q_Q(QSGTextInput); + Qt::InputMethodHints hints = inputMethodHints; + uint echo = control->echoMode(); + if (echo == QSGTextInput::Password || echo == QSGTextInput::NoEcho) + hints |= Qt::ImhHiddenText; + else if (echo == QSGTextInput::PasswordEchoOnEdit) + hints &= ~Qt::ImhHiddenText; + if (echo != QSGTextInput::Normal) + hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText); + q->setInputMethodHints(hints); +} + +QSGTextInput::EchoMode QSGTextInput::echoMode() const +{ + Q_D(const QSGTextInput); + return (QSGTextInput::EchoMode)d->control->echoMode(); +} + +void QSGTextInput::setEchoMode(QSGTextInput::EchoMode echo) +{ + Q_D(QSGTextInput); + if (echoMode() == echo) + return; + d->control->setEchoMode((uint)echo); + d->updateInputMethodHints(); + q_textChanged(); + emit echoModeChanged(echoMode()); +} + +Qt::InputMethodHints QSGTextInput::imHints() const +{ + Q_D(const QSGTextInput); + return d->inputMethodHints; +} + +void QSGTextInput::setIMHints(Qt::InputMethodHints hints) +{ + Q_D(QSGTextInput); + if (d->inputMethodHints == hints) + return; + d->inputMethodHints = hints; + d->updateInputMethodHints(); +} + +QDeclarativeComponent* QSGTextInput::cursorDelegate() const +{ + Q_D(const QSGTextInput); + return d->cursorComponent; +} + +void QSGTextInput::setCursorDelegate(QDeclarativeComponent* c) +{ + Q_D(QSGTextInput); + if (d->cursorComponent == c) + return; + + d->cursorComponent = c; + if(!c){ + //note that the components are owned by something else + disconnect(d->control, SIGNAL(cursorPositionChanged(int,int)), + this, SLOT(moveCursor())); + disconnect(d->control, SIGNAL(updateMicroFocus()), + this, SLOT(moveCursor())); + delete d->cursorItem; + }else{ + d->startCreatingCursor(); + } + + emit cursorDelegateChanged(); +} + +void QSGTextInputPrivate::startCreatingCursor() +{ + Q_Q(QSGTextInput); + q->connect(control, SIGNAL(cursorPositionChanged(int,int)), + q, SLOT(moveCursor()), Qt::UniqueConnection); + q->connect(control, SIGNAL(updateMicroFocus()), + q, SLOT(moveCursor()), Qt::UniqueConnection); + if(cursorComponent->isReady()){ + q->createCursor(); + }else if(cursorComponent->isLoading()){ + q->connect(cursorComponent, SIGNAL(statusChanged(int)), + q, SLOT(createCursor())); + }else {//isError + qmlInfo(q, cursorComponent->errors()) << QSGTextInput::tr("Could not load cursor delegate"); + } +} + +void QSGTextInput::createCursor() +{ + Q_D(QSGTextInput); + if(d->cursorComponent->isError()){ + qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate"); + return; + } + + if(!d->cursorComponent->isReady()) + return; + + if(d->cursorItem) + delete d->cursorItem; + d->cursorItem = qobject_cast<QSGItem*>(d->cursorComponent->create()); + if(!d->cursorItem){ + qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate"); + return; + } + + 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 QLineControl's +1 which is not consistent with Text. +} + +void QSGTextInput::moveCursor() +{ + Q_D(QSGTextInput); + if(!d->cursorItem) + return; + d->updateHorizontalScroll(); + d->cursorItem->setX(d->control->cursorToX() - d->hscroll); +} + +QRectF QSGTextInput::positionToRectangle(int pos) const +{ + Q_D(const QSGTextInput); + 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()); +} + +int QSGTextInput::positionAt(int x) const +{ + return positionAt(x, CursorBetweenCharacters); +} + +int QSGTextInput::positionAt(int x, CursorPosition position) const +{ + Q_D(const QSGTextInput); + int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position)); + const int cursor = d->control->cursor(); + if (pos > cursor) { + const int preeditLength = d->control->preeditAreaText().length(); + pos = pos > cursor + preeditLength + ? pos - preeditLength + : cursor; + } + return pos; +} + +void QSGTextInput::keyPressEvent(QKeyEvent* ev) +{ + Q_D(QSGTextInput); + // Don't allow MacOSX up/down support, and we don't allow a completer. + bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier; + 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(); + 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); + } + if (ignore) { + ev->ignore(); + } else { + d->control->processKeyEvent(ev); + } + if (!ev->isAccepted()) + QSGPaintedItem::keyPressEvent(ev); +} + +void QSGTextInput::inputMethodEvent(QInputMethodEvent *ev) +{ + Q_D(QSGTextInput); + const bool wasComposing = d->control->preeditAreaText().length() > 0; + if (d->control->isReadOnly()) { + ev->ignore(); + } else { + d->control->processInputMethodEvent(ev); + updateSize(); + d->updateHorizontalScroll(); + } + if (!ev->isAccepted()) + QSGPaintedItem::inputMethodEvent(ev); + + if (wasComposing != (d->control->preeditAreaText().length() > 0)) + emit inputMethodComposingChanged(); +} + +void QSGTextInput::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QSGTextInput); + if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonDblClick)) + return; + if (d->selectByMouse) { + int cursor = d->xToPos(event->pos().x()); + d->control->selectWordAtPos(cursor); + event->setAccepted(true); + } else { + QSGPaintedItem::mouseDoubleClickEvent(event); + } +} + +void QSGTextInput::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QSGTextInput); + if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonPress)) + return; + if(d->focusOnPress){ + bool hadActiveFocus = hasActiveFocus(); + forceActiveFocus(); + if (d->showInputPanelOnFocus) { + if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) { + // re-open input panel on press if already focused + openSoftwareInputPanel(); + } + } else { // show input panel on click + if (hasActiveFocus() && !hadActiveFocus) { + d->clickCausedFocus = true; + } + } + } + if (d->selectByMouse) { + setKeepMouseGrab(false); + d->selectPressed = true; + d->pressPos = event->pos(); + } + bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse; + int cursor = d->xToPos(event->pos().x()); + d->control->moveCursor(cursor, mark); + event->setAccepted(true); +} + +void QSGTextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QSGTextInput); + if (d->sendMouseEventToInputContext(event, QEvent::MouseMove)) + return; + if (d->selectPressed) { + if (qAbs(int(event->pos().x() - d->pressPos.x())) > QApplication::startDragDistance()) + setKeepMouseGrab(true); + moveCursorSelection(d->xToPos(event->pos().x()), d->mouseSelectionMode); + event->setAccepted(true); + } else { + QSGPaintedItem::mouseMoveEvent(event); + } +} + +void QSGTextInput::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QSGTextInput); + if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonRelease)) + return; + if (d->selectPressed) { + d->selectPressed = false; + setKeepMouseGrab(false); + } + if (!d->showInputPanelOnFocus) { // input panel on click + if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) { + if (canvas() && canvas() == qApp->focusWidget()) { + qt_widget_private(canvas())->handleSoftwareInputPanel(event->button(), d->clickCausedFocus); + } + } + } + d->clickCausedFocus = false; + d->control->processEvent(event); + if (!event->isAccepted()) + QSGPaintedItem::mouseReleaseEvent(event); +} + +bool QSGTextInputPrivate::sendMouseEventToInputContext( + QGraphicsSceneMouseEvent *event, QEvent::Type eventType) +{ +#if !defined QT_NO_IM + if (event->widget() && control->composeMode()) { + int tmp_cursor = xToPos(event->pos().x()); + int mousePos = tmp_cursor - control->cursor(); + if (mousePos < 0 || mousePos > control->preeditAreaText().length()) { + mousePos = -1; + // don't send move events outside the preedit area + if (eventType == QEvent::MouseMove) + return true; + } + + QInputContext *qic = event->widget()->inputContext(); + if (qic) { + QMouseEvent mouseEvent( + eventType, + event->widget()->mapFromGlobal(event->screenPos()), + event->screenPos(), + event->button(), + event->buttons(), + event->modifiers()); + // may be causing reset() in some input methods + qic->mouseHandler(mousePos, &mouseEvent); + event->setAccepted(mouseEvent.isAccepted()); + } + if (!control->preeditAreaText().isEmpty()) + return true; + } +#else + Q_UNUSED(event); + Q_UNUSED(eventType) +#endif + + return false; +} + +void QSGTextInput::mouseUngrabEvent() +{ + Q_D(QSGTextInput); + d->selectPressed = false; + setKeepMouseGrab(false); +} + +bool QSGTextInput::event(QEvent* ev) +{ + Q_D(QSGTextInput); + //Anything we don't deal with ourselves, pass to the control + bool handled = false; + switch(ev->type()){ + case QEvent::KeyPress: + case QEvent::KeyRelease://###Should the control be doing anything with release? + case QEvent::InputMethod: + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + case QEvent::GraphicsSceneMouseDoubleClick: + break; + default: + handled = d->control->processEvent(ev); + } + if(!handled) + handled = QSGPaintedItem::event(ev); + return handled; +} + +void QSGTextInput::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QSGTextInput); + if (newGeometry.width() != oldGeometry.width()) { + updateSize(); + d->updateHorizontalScroll(); + } + QSGPaintedItem::geometryChanged(newGeometry, oldGeometry); +} + +int QSGTextInputPrivate::calculateTextWidth() +{ + return qRound(control->naturalTextWidth()); +} + +void QSGTextInputPrivate::updateHorizontalScroll() +{ + Q_Q(QSGTextInput); + const int preeditLength = control->preeditAreaText().length(); + int cix = qRound(control->cursorToX(control->cursor() + preeditLength)); + QRect br(q->boundingRect().toRect()); + int widthUsed = calculateTextWidth(); + + QSGTextInput::HAlignment effectiveHAlign = q->effectiveHAlign(); + if (autoScroll) { + if (widthUsed <= br.width()) { + // text fits in br; use hscroll for alignment + switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) { + case Qt::AlignRight: + hscroll = widthUsed - br.width() - 1; + break; + case Qt::AlignHCenter: + hscroll = (widthUsed - br.width()) / 2; + break; + default: + // Left + hscroll = 0; + break; + } + } else if (cix - hscroll >= br.width()) { + // text doesn't fit, cursor is to the right of br (scroll right) + hscroll = cix - br.width() + 1; + } else if (cix - hscroll < 0 && hscroll < widthUsed) { + // text doesn't fit, cursor is to the left of br (scroll left) + hscroll = cix; + } else if (widthUsed - hscroll < br.width()) { + // text doesn't fit, text document is to the left of br; align + // right + hscroll = widthUsed - br.width() + 1; + } + 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))); + if (cix < hscroll) + hscroll = cix; + } + } else { + switch (effectiveHAlign) { + case QSGTextInput::AlignRight: + hscroll = q->width() - widthUsed; + break; + case QSGTextInput::AlignHCenter: + hscroll = (q->width() - widthUsed) / 2; + break; + default: + // Left + hscroll = 0; + break; + } + } +} + +void QSGTextInput::paint(QPainter *p) +{ + // XXX todo + QRect r(0, 0, width(), height()); + + Q_D(QSGTextInput); + p->setRenderHint(QPainter::TextAntialiasing, true); + p->save(); + p->setPen(QPen(d->color)); + int flags = QLineControl::DrawText; + if(!isReadOnly() && d->cursorVisible && !d->cursorItem) + flags |= QLineControl::DrawCursor; + if (d->control->hasSelectedText()) + flags |= QLineControl::DrawSelections; + QPoint offset = QPoint(0,0); + QFontMetrics fm = QFontMetrics(d->font); + 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()); + } else { + offset = QPoint(d->hscroll, 0); + } + d->control->draw(p, offset, r, flags); + p->restore(); +} + +QVariant QSGTextInput::inputMethodQuery(Qt::InputMethodQuery property) const +{ + Q_D(const QSGTextInput); + switch(property) { + case Qt::ImMicroFocus: + return cursorRectangle(); + case Qt::ImFont: + return font(); + case Qt::ImCursorPosition: + return QVariant(d->control->cursor()); + case Qt::ImSurroundingText: + if (d->control->echoMode() == PasswordEchoOnEdit && !d->control->passwordEchoEditing()) + return QVariant(displayText()); + else + return QVariant(text()); + 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()); + else + return QVariant(d->control->selectionStart()); + default: + return QVariant(); + } +} + +void QSGTextInput::deselect() +{ + Q_D(QSGTextInput); + d->control->deselect(); +} + +void QSGTextInput::selectAll() +{ + Q_D(QSGTextInput); + d->control->setSelection(0, d->control->text().length()); +} + +bool QSGTextInput::isRightToLeft(int start, int end) +{ + Q_D(QSGTextInput); + 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(); + } +} + +#ifndef QT_NO_CLIPBOARD +void QSGTextInput::cut() +{ + Q_D(QSGTextInput); + d->control->copy(); + d->control->del(); +} + +void QSGTextInput::copy() +{ + Q_D(QSGTextInput); + d->control->copy(); +} + +void QSGTextInput::paste() +{ + Q_D(QSGTextInput); + if (!d->control->isReadOnly()) + d->control->paste(); +} +#endif // QT_NO_CLIPBOARD + +void QSGTextInput::selectWord() +{ + Q_D(QSGTextInput); + d->control->selectWordAtPos(d->control->cursor()); +} + +QString QSGTextInput::passwordCharacter() const +{ + Q_D(const QSGTextInput); + return QString(d->control->passwordCharacter()); +} + +void QSGTextInput::setPasswordCharacter(const QString &str) +{ + Q_D(QSGTextInput); + if(str.length() < 1) + return; + d->control->setPasswordCharacter(str.constData()[0]); + EchoMode echoMode_ = echoMode(); + if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) { + updateSize(); + } + emit passwordCharacterChanged(); +} + +QString QSGTextInput::displayText() const +{ + Q_D(const QSGTextInput); + return d->control->displayText(); +} + +bool QSGTextInput::selectByMouse() const +{ + Q_D(const QSGTextInput); + return d->selectByMouse; +} + +void QSGTextInput::setSelectByMouse(bool on) +{ + Q_D(QSGTextInput); + if (d->selectByMouse != on) { + d->selectByMouse = on; + emit selectByMouseChanged(on); + } +} + +QSGTextInput::SelectionMode QSGTextInput::mouseSelectionMode() const +{ + Q_D(const QSGTextInput); + return d->mouseSelectionMode; +} + +void QSGTextInput::setMouseSelectionMode(SelectionMode mode) +{ + Q_D(QSGTextInput); + if (d->mouseSelectionMode != mode) { + d->mouseSelectionMode = mode; + emit mouseSelectionModeChanged(mode); + } +} + +bool QSGTextInput::canPaste() const +{ + Q_D(const QSGTextInput); + return d->canPaste; +} + +void QSGTextInput::moveCursorSelection(int position) +{ + Q_D(QSGTextInput); + d->control->moveCursor(position, true); + d->updateHorizontalScroll(); +} + +void QSGTextInput::moveCursorSelection(int pos, SelectionMode mode) +{ + Q_D(QSGTextInput); + + if (mode == SelectCharacters) { + d->control->moveCursor(pos, true); + } else if (pos != d->control->cursor()){ + const int cursor = d->control->cursor(); + int anchor; + if (!d->control->hasSelectedText()) + anchor = d->control->cursor(); + else if (d->control->selectionStart() == d->control->cursor()) + anchor = d->control->selectionEnd(); + else + anchor = d->control->selectionStart(); + + if (anchor < pos || (anchor == pos && cursor < pos)) { + const QString text = d->control->text(); + QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); + finder.setPosition(anchor); + + const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); + if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord) + || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) { + finder.toPreviousBoundary(); + } + anchor = finder.position() != -1 ? finder.position() : 0; + + finder.setPosition(pos); + if (pos > 0 && !finder.boundaryReasons()) + finder.toNextBoundary(); + const int cursor = finder.position() != -1 ? finder.position() : text.length(); + + d->control->setSelection(anchor, cursor - anchor); + } else if (anchor > pos || (anchor == pos && cursor > pos)) { + const QString text = d->control->text(); + QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); + finder.setPosition(anchor); + + const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); + if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord) + || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) { + finder.toNextBoundary(); + } + + anchor = finder.position() != -1 ? finder.position() : text.length(); + + finder.setPosition(pos); + if (pos < text.length() && !finder.boundaryReasons()) + finder.toPreviousBoundary(); + const int cursor = finder.position() != -1 ? finder.position() : 0; + + d->control->setSelection(anchor, cursor - anchor); + } + } +} + +void QSGTextInput::openSoftwareInputPanel() +{ + QEvent event(QEvent::RequestSoftwareInputPanel); + if (qApp) { + if (canvas() && canvas() == qApp->focusWidget()) { + QEvent event(QEvent::RequestSoftwareInputPanel); + QApplication::sendEvent(canvas(), &event); + } + } +} + +void QSGTextInput::closeSoftwareInputPanel() +{ + if (qApp) { + if (canvas() && canvas() == qApp->focusWidget()) { + QEvent event(QEvent::CloseSoftwareInputPanel); + QApplication::sendEvent(canvas(), &event); + } + } +} + +void QSGTextInput::focusInEvent(QFocusEvent *event) +{ + Q_D(const QSGTextInput); + if (d->showInputPanelOnFocus) { + if (d->focusOnPress && !isReadOnly()) { + openSoftwareInputPanel(); + } + } + QSGPaintedItem::focusInEvent(event); +} + +void QSGTextInput::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_D(QSGTextInput); + if (change == ItemActiveFocusHasChanged) { + bool hasFocus = value.boolValue; + d->focused = hasFocus; + setCursorVisible(hasFocus && d->canvas && d->canvas->hasFocus()); + if(echoMode() == QSGTextInput::PasswordEchoOnEdit && !hasFocus) + d->control->updatePasswordEchoEditing(false);//QLineControl sets it on key events, but doesn't deal with focus events + if (!hasFocus) + d->control->deselect(); + } + QSGItem::itemChange(change, value); +} + +bool QSGTextInput::isInputMethodComposing() const +{ + Q_D(const QSGTextInput); + return d->control->preeditAreaText().length() > 0; +} + +void QSGTextInputPrivate::init() +{ + Q_Q(QSGTextInput); + control->setCursorWidth(1); + control->setPasswordCharacter(QLatin1Char('*')); + q->setSmooth(smooth); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QSGItem::ItemAcceptsInputMethod); + 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(QApplication::clipboard(), SIGNAL(dataChanged()), + q, SLOT(q_canPasteChanged())); + canPaste = !control->isReadOnly() && QApplication::clipboard()->text().length() != 0; +#endif // QT_NO_CLIPBOARD + q->connect(control, SIGNAL(updateMicroFocus()), + q, SLOT(updateMicroFocus())); + q->connect(control, SIGNAL(displayTextChanged(QString)), + q, SLOT(updateRect())); + q->updateSize(); + oldValidity = control->hasAcceptableInput(); + lastSelectionStart = 0; + lastSelectionEnd = 0; + QPalette p = control->palette(); + selectedTextColor = p.color(QPalette::HighlightedText); + selectionColor = p.color(QPalette::Highlight); + determineHorizontalAlignment(); +} + +void QSGTextInput::cursorPosChanged() +{ + Q_D(QSGTextInput); + d->updateHorizontalScroll(); + updateRect();//TODO: Only update rect between pos's + updateMicroFocus(); + 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(); + } + } +} + +void QSGTextInput::selectionChanged() +{ + Q_D(QSGTextInput); + updateRect();//TODO: Only update rect in selection + emit selectedTextChanged(); + + if(d->lastSelectionStart != d->control->selectionStart()){ + d->lastSelectionStart = d->control->selectionStart(); + if(d->lastSelectionStart == -1) + d->lastSelectionStart = d->control->cursor(); + emit selectionStartChanged(); + } + if(d->lastSelectionEnd != d->control->selectionEnd()){ + d->lastSelectionEnd = d->control->selectionEnd(); + if(d->lastSelectionEnd == -1) + d->lastSelectionEnd = d->control->cursor(); + emit selectionEndChanged(); + } +} + +void QSGTextInput::q_textChanged() +{ + Q_D(QSGTextInput); + updateSize(); + d->determineHorizontalAlignment(); + d->updateHorizontalScroll(); + updateMicroFocus(); + emit textChanged(); + emit displayTextChanged(); + if(hasAcceptableInput() != d->oldValidity){ + d->oldValidity = hasAcceptableInput(); + emit acceptableInputChanged(); + } +} + +void QSGTextInput::updateRect(const QRect &r) +{ + Q_D(QSGTextInput); + if(r == QRect()) + update(); + else + update(QRect(r.x() - d->hscroll, r.y(), r.width(), r.height())); +} + +QRectF QSGTextInput::boundingRect() const +{ + Q_D(const QSGTextInput); + QRectF r = QSGPaintedItem::boundingRect(); + + int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth(); + + // Could include font max left/right bearings to either side of rectangle. + + r.setRight(r.right() + cursorWidth); + return r; +} + +void QSGTextInput::updateSize(bool needsRedraw) +{ + Q_D(QSGTextInput); + int w = width(); + int h = height(); + setImplicitHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text. + setImplicitWidth(d->calculateTextWidth()); + setContentsSize(QSize(width(), height())); + if(w==width() && h==height() && needsRedraw) + update(); +} + +void QSGTextInput::q_canPasteChanged() +{ + Q_D(QSGTextInput); + bool old = d->canPaste; +#ifndef QT_NO_CLIPBOARD + d->canPaste = !d->control->isReadOnly() && QApplication::clipboard()->text().length() != 0; +#endif + if(d->canPaste != old) + emit canPasteChanged(); +} + +QT_END_NAMESPACE + |