1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsgtextinput_p.h"
43 #include "qsgtextinput_p_p.h"
44 #include "qsgcanvas.h"
46 #include <private/qdeclarativeglobal_p.h>
47 #include <private/qwidget_p.h>
49 #include <QtDeclarative/qdeclarativeinfo.h>
50 #include <QtWidgets/qgraphicssceneevent.h>
51 #include <QtWidgets/qinputcontext.h>
52 #include <QTextBoundaryFinder>
57 QWidgetPrivate *qt_widget_private(QWidget *widget);
59 QSGTextInput::QSGTextInput(QSGItem* parent)
60 : QSGImplicitSizePaintedItem(*(new QSGTextInputPrivate), parent)
66 QSGTextInput::~QSGTextInput()
70 QString QSGTextInput::text() const
72 Q_D(const QSGTextInput);
73 return d->control->text();
76 void QSGTextInput::setText(const QString &s)
81 d->control->setText(s);
84 QFont QSGTextInput::font() const
86 Q_D(const QSGTextInput);
90 void QSGTextInput::setFont(const QFont &font)
93 if (d->sourceFont == font)
97 QFont oldFont = d->font;
99 if (d->font.pointSizeF() != -1) {
101 qreal size = qRound(d->font.pointSizeF()*2.0);
102 d->font.setPointSizeF(size/2.0);
104 if (oldFont != d->font) {
105 d->control->setFont(d->font);
107 updateCursorRectangle();
109 d->cursorItem->setHeight(QFontMetrics(d->font).height());
112 emit fontChanged(d->sourceFont);
115 QColor QSGTextInput::color() const
117 Q_D(const QSGTextInput);
121 void QSGTextInput::setColor(const QColor &c)
127 emit colorChanged(c);
131 QColor QSGTextInput::selectionColor() const
133 Q_D(const QSGTextInput);
134 return d->selectionColor;
137 void QSGTextInput::setSelectionColor(const QColor &color)
140 if (d->selectionColor == color)
143 d->selectionColor = color;
144 QPalette p = d->control->palette();
145 p.setColor(QPalette::Highlight, d->selectionColor);
146 d->control->setPalette(p);
147 if (d->control->hasSelectedText())
149 emit selectionColorChanged(color);
152 QColor QSGTextInput::selectedTextColor() const
154 Q_D(const QSGTextInput);
155 return d->selectedTextColor;
158 void QSGTextInput::setSelectedTextColor(const QColor &color)
161 if (d->selectedTextColor == color)
164 d->selectedTextColor = color;
165 QPalette p = d->control->palette();
166 p.setColor(QPalette::HighlightedText, d->selectedTextColor);
167 d->control->setPalette(p);
168 if (d->control->hasSelectedText())
170 emit selectedTextColorChanged(color);
173 QSGTextInput::HAlignment QSGTextInput::hAlign() const
175 Q_D(const QSGTextInput);
179 void QSGTextInput::setHAlign(HAlignment align)
182 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
183 d->hAlignImplicit = false;
184 if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
185 updateCursorRectangle();
189 void QSGTextInput::resetHAlign()
192 d->hAlignImplicit = true;
193 if (d->determineHorizontalAlignment() && isComponentComplete()) {
194 updateCursorRectangle();
198 QSGTextInput::HAlignment QSGTextInput::effectiveHAlign() const
200 Q_D(const QSGTextInput);
201 QSGTextInput::HAlignment effectiveAlignment = d->hAlign;
202 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
204 case QSGTextInput::AlignLeft:
205 effectiveAlignment = QSGTextInput::AlignRight;
207 case QSGTextInput::AlignRight:
208 effectiveAlignment = QSGTextInput::AlignLeft;
214 return effectiveAlignment;
217 bool QSGTextInputPrivate::setHAlign(QSGTextInput::HAlignment alignment, bool forceAlign)
220 if ((hAlign != alignment || forceAlign) && alignment <= QSGTextInput::AlignHCenter) { // justify not supported
221 QSGTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
223 emit q->horizontalAlignmentChanged(alignment);
224 if (oldEffectiveHAlign != q->effectiveHAlign())
225 emit q->effectiveHorizontalAlignmentChanged();
231 bool QSGTextInputPrivate::determineHorizontalAlignment()
233 if (hAlignImplicit) {
234 // if no explicit alignment has been set, follow the natural layout direction of the text
235 QString text = control->text();
236 bool isRightToLeft = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft();
237 return setHAlign(isRightToLeft ? QSGTextInput::AlignRight : QSGTextInput::AlignLeft);
242 void QSGTextInputPrivate::mirrorChange()
245 if (q->isComponentComplete()) {
246 if (!hAlignImplicit && (hAlign == QSGTextInput::AlignRight || hAlign == QSGTextInput::AlignLeft)) {
247 q->updateCursorRectangle();
248 emit q->effectiveHorizontalAlignmentChanged();
253 bool QSGTextInput::isReadOnly() const
255 Q_D(const QSGTextInput);
256 return d->control->isReadOnly();
259 void QSGTextInput::setReadOnly(bool ro)
262 if (d->control->isReadOnly() == ro)
265 setFlag(QSGItem::ItemAcceptsInputMethod, !ro);
266 d->control->setReadOnly(ro);
268 d->control->setCursorPosition(d->control->end());
270 emit readOnlyChanged(ro);
273 int QSGTextInput::maxLength() const
275 Q_D(const QSGTextInput);
276 return d->control->maxLength();
279 void QSGTextInput::setMaxLength(int ml)
282 if (d->control->maxLength() == ml)
285 d->control->setMaxLength(ml);
287 emit maximumLengthChanged(ml);
290 bool QSGTextInput::isCursorVisible() const
292 Q_D(const QSGTextInput);
293 return d->cursorVisible;
296 void QSGTextInput::setCursorVisible(bool on)
299 if (d->cursorVisible == on)
301 d->cursorVisible = on;
302 d->control->setCursorBlinkPeriod(on?QApplication::cursorFlashTime():0);
303 QRect r = d->control->cursorRect();
304 if (d->control->inputMask().isEmpty())
308 emit cursorVisibleChanged(d->cursorVisible);
311 int QSGTextInput::cursorPosition() const
313 Q_D(const QSGTextInput);
314 return d->control->cursor();
316 void QSGTextInput::setCursorPosition(int cp)
319 if (cp < 0 || cp > d->control->text().length())
321 d->control->moveCursor(cp);
324 QRect QSGTextInput::cursorRectangle() const
326 Q_D(const QSGTextInput);
327 QRect r = d->control->cursorRect();
328 // Scroll and make consistent with TextEdit
329 // QLineControl inexplicably adds 1 to the height and horizontal padding
330 // for unicode direction markers.
331 r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1);
335 int QSGTextInput::selectionStart() const
337 Q_D(const QSGTextInput);
338 return d->lastSelectionStart;
341 int QSGTextInput::selectionEnd() const
343 Q_D(const QSGTextInput);
344 return d->lastSelectionEnd;
347 void QSGTextInput::select(int start, int end)
350 if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length())
352 d->control->setSelection(start, end-start);
355 QString QSGTextInput::selectedText() const
357 Q_D(const QSGTextInput);
358 return d->control->selectedText();
361 bool QSGTextInput::focusOnPress() const
363 Q_D(const QSGTextInput);
364 return d->focusOnPress;
367 void QSGTextInput::setFocusOnPress(bool b)
370 if (d->focusOnPress == b)
375 emit activeFocusOnPressChanged(d->focusOnPress);
378 bool QSGTextInput::autoScroll() const
380 Q_D(const QSGTextInput);
381 return d->autoScroll;
384 void QSGTextInput::setAutoScroll(bool b)
387 if (d->autoScroll == b)
391 //We need to repaint so that the scrolling is taking into account.
393 updateCursorRectangle();
394 emit autoScrollChanged(d->autoScroll);
397 #ifndef QT_NO_VALIDATOR
398 QValidator* QSGTextInput::validator() const
400 Q_D(const QSGTextInput);
401 //###const cast isn't good, but needed for property system?
402 return const_cast<QValidator*>(d->control->validator());
405 void QSGTextInput::setValidator(QValidator* v)
408 if (d->control->validator() == v)
411 d->control->setValidator(v);
412 if(!d->control->hasAcceptableInput()){
413 d->oldValidity = false;
414 emit acceptableInputChanged();
417 emit validatorChanged();
419 #endif // QT_NO_VALIDATOR
421 QString QSGTextInput::inputMask() const
423 Q_D(const QSGTextInput);
424 return d->control->inputMask();
427 void QSGTextInput::setInputMask(const QString &im)
430 if (d->control->inputMask() == im)
433 d->control->setInputMask(im);
434 emit inputMaskChanged(d->control->inputMask());
437 bool QSGTextInput::hasAcceptableInput() const
439 Q_D(const QSGTextInput);
440 return d->control->hasAcceptableInput();
443 void QSGTextInputPrivate::updateInputMethodHints()
446 Qt::InputMethodHints hints = inputMethodHints;
447 uint echo = control->echoMode();
448 if (echo == QSGTextInput::Password || echo == QSGTextInput::NoEcho)
449 hints |= Qt::ImhHiddenText;
450 else if (echo == QSGTextInput::PasswordEchoOnEdit)
451 hints &= ~Qt::ImhHiddenText;
452 if (echo != QSGTextInput::Normal)
453 hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
454 q->setInputMethodHints(hints);
457 QSGTextInput::EchoMode QSGTextInput::echoMode() const
459 Q_D(const QSGTextInput);
460 return (QSGTextInput::EchoMode)d->control->echoMode();
463 void QSGTextInput::setEchoMode(QSGTextInput::EchoMode echo)
466 if (echoMode() == echo)
468 d->control->setEchoMode((QLineControl::EchoMode)echo);
469 d->updateInputMethodHints();
471 emit echoModeChanged(echoMode());
474 Qt::InputMethodHints QSGTextInput::imHints() const
476 Q_D(const QSGTextInput);
477 return d->inputMethodHints;
480 void QSGTextInput::setIMHints(Qt::InputMethodHints hints)
483 if (d->inputMethodHints == hints)
485 d->inputMethodHints = hints;
486 d->updateInputMethodHints();
489 QDeclarativeComponent* QSGTextInput::cursorDelegate() const
491 Q_D(const QSGTextInput);
492 return d->cursorComponent;
495 void QSGTextInput::setCursorDelegate(QDeclarativeComponent* c)
498 if (d->cursorComponent == c)
501 d->cursorComponent = c;
503 //note that the components are owned by something else
504 delete d->cursorItem;
506 d->startCreatingCursor();
509 emit cursorDelegateChanged();
512 void QSGTextInputPrivate::startCreatingCursor()
515 if(cursorComponent->isReady()){
517 }else if(cursorComponent->isLoading()){
518 q->connect(cursorComponent, SIGNAL(statusChanged(int)),
519 q, SLOT(createCursor()));
521 qmlInfo(q, cursorComponent->errors()) << QSGTextInput::tr("Could not load cursor delegate");
525 void QSGTextInput::createCursor()
528 if(d->cursorComponent->isError()){
529 qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate");
533 if(!d->cursorComponent->isReady())
537 delete d->cursorItem;
538 d->cursorItem = qobject_cast<QSGItem*>(d->cursorComponent->create());
540 qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate");
544 QDeclarative_setParent_noEvent(d->cursorItem, this);
545 d->cursorItem->setParentItem(this);
546 d->cursorItem->setX(d->control->cursorToX());
547 d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text.
550 QRectF QSGTextInput::positionToRectangle(int pos) const
552 Q_D(const QSGTextInput);
553 if (pos > d->control->cursorPosition())
554 pos += d->control->preeditAreaText().length();
555 return QRectF(d->control->cursorToX(pos)-d->hscroll,
557 d->control->cursorWidth(),
558 cursorRectangle().height());
561 int QSGTextInput::positionAt(int x) const
563 return positionAt(x, CursorBetweenCharacters);
566 int QSGTextInput::positionAt(int x, CursorPosition position) const
568 Q_D(const QSGTextInput);
569 int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position));
570 const int cursor = d->control->cursor();
572 const int preeditLength = d->control->preeditAreaText().length();
573 pos = pos > cursor + preeditLength
574 ? pos - preeditLength
580 void QSGTextInput::keyPressEvent(QKeyEvent* ev)
583 // Don't allow MacOSX up/down support, and we don't allow a completer.
584 bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier;
585 if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) {
586 // Ignore when moving off the end unless there is a selection,
587 // because then moving will do something (deselect).
588 int cursorPosition = d->control->cursor();
589 if (cursorPosition == 0)
590 ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
591 if (cursorPosition == d->control->text().length())
592 ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
597 d->control->processKeyEvent(ev);
599 if (!ev->isAccepted())
600 QSGPaintedItem::keyPressEvent(ev);
603 void QSGTextInput::inputMethodEvent(QInputMethodEvent *ev)
606 const bool wasComposing = d->control->preeditAreaText().length() > 0;
607 if (d->control->isReadOnly()) {
610 d->control->processInputMethodEvent(ev);
612 if (!ev->isAccepted())
613 QSGPaintedItem::inputMethodEvent(ev);
615 if (wasComposing != (d->control->preeditAreaText().length() > 0))
616 emit inputMethodComposingChanged();
619 void QSGTextInput::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
622 if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonDblClick))
624 if (d->selectByMouse) {
625 int cursor = d->xToPos(event->pos().x());
626 d->control->selectWordAtPos(cursor);
627 event->setAccepted(true);
629 QSGPaintedItem::mouseDoubleClickEvent(event);
633 void QSGTextInput::mousePressEvent(QGraphicsSceneMouseEvent *event)
636 if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonPress))
639 bool hadActiveFocus = hasActiveFocus();
641 if (d->showInputPanelOnFocus) {
642 if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) {
643 // re-open input panel on press if already focused
644 openSoftwareInputPanel();
646 } else { // show input panel on click
647 if (hasActiveFocus() && !hadActiveFocus) {
648 d->clickCausedFocus = true;
652 if (d->selectByMouse) {
653 setKeepMouseGrab(false);
654 d->selectPressed = true;
655 d->pressPos = event->pos();
657 bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
658 int cursor = d->xToPos(event->pos().x());
659 d->control->moveCursor(cursor, mark);
660 event->setAccepted(true);
663 void QSGTextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
666 if (d->sendMouseEventToInputContext(event, QEvent::MouseMove))
668 if (d->selectPressed) {
669 if (qAbs(int(event->pos().x() - d->pressPos.x())) > QApplication::startDragDistance())
670 setKeepMouseGrab(true);
671 moveCursorSelection(d->xToPos(event->pos().x()), d->mouseSelectionMode);
672 event->setAccepted(true);
674 QSGPaintedItem::mouseMoveEvent(event);
678 void QSGTextInput::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
681 if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonRelease))
683 if (d->selectPressed) {
684 d->selectPressed = false;
685 setKeepMouseGrab(false);
687 if (!d->showInputPanelOnFocus) { // input panel on click
688 if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) {
689 if (canvas() && canvas() == QGuiApplication::activeWindow()) {
690 // ### refactor: implement virtual keyboard properly..
691 qDebug("QSGTextInput: virtual keyboard no implemented...");
692 // qt_widget_private(canvas())->handleSoftwareInputPanel(event->button(), d->clickCausedFocus);
696 d->clickCausedFocus = false;
697 d->control->processEvent(event);
698 if (!event->isAccepted())
699 QSGPaintedItem::mouseReleaseEvent(event);
702 bool QSGTextInputPrivate::sendMouseEventToInputContext(
703 QGraphicsSceneMouseEvent *event, QEvent::Type eventType)
705 #if !defined QT_NO_IM
706 if (event->widget() && control->composeMode()) {
707 int tmp_cursor = xToPos(event->pos().x());
708 int mousePos = tmp_cursor - control->cursor();
709 if (mousePos < 0 || mousePos > control->preeditAreaText().length()) {
711 // don't send move events outside the preedit area
712 if (eventType == QEvent::MouseMove)
716 QInputContext *qic = event->widget()->inputContext();
718 QMouseEvent mouseEvent(
720 event->widget()->mapFromGlobal(event->screenPos()),
725 // may be causing reset() in some input methods
726 qic->mouseHandler(mousePos, &mouseEvent);
727 event->setAccepted(mouseEvent.isAccepted());
729 if (!control->preeditAreaText().isEmpty())
740 void QSGTextInput::mouseUngrabEvent()
743 d->selectPressed = false;
744 setKeepMouseGrab(false);
747 bool QSGTextInput::event(QEvent* ev)
750 //Anything we don't deal with ourselves, pass to the control
751 bool handled = false;
753 case QEvent::KeyPress:
754 case QEvent::KeyRelease://###Should the control be doing anything with release?
755 case QEvent::InputMethod:
756 case QEvent::GraphicsSceneMousePress:
757 case QEvent::GraphicsSceneMouseMove:
758 case QEvent::GraphicsSceneMouseRelease:
759 case QEvent::GraphicsSceneMouseDoubleClick:
762 handled = d->control->processEvent(ev);
765 handled = QSGPaintedItem::event(ev);
769 void QSGTextInput::geometryChanged(const QRectF &newGeometry,
770 const QRectF &oldGeometry)
772 if (newGeometry.width() != oldGeometry.width()) {
774 updateCursorRectangle();
776 QSGPaintedItem::geometryChanged(newGeometry, oldGeometry);
779 int QSGTextInputPrivate::calculateTextWidth()
781 return qRound(control->naturalTextWidth());
784 void QSGTextInputPrivate::updateHorizontalScroll()
787 const int preeditLength = control->preeditAreaText().length();
788 const int width = q->width();
789 int widthUsed = calculateTextWidth();
791 if (!autoScroll || widthUsed <= width) {
792 QSGTextInput::HAlignment effectiveHAlign = q->effectiveHAlign();
793 // text fits in br; use hscroll for alignment
794 switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) {
796 hscroll = widthUsed - width;
798 case Qt::AlignHCenter:
799 hscroll = (widthUsed - width) / 2;
807 int cix = qRound(control->cursorToX(control->cursor() + preeditLength));
808 if (cix - hscroll >= width) {
809 // text doesn't fit, cursor is to the right of br (scroll right)
810 hscroll = cix - width;
811 } else if (cix - hscroll < 0 && hscroll < widthUsed) {
812 // text doesn't fit, cursor is to the left of br (scroll left)
814 } else if (widthUsed - hscroll < width) {
815 // text doesn't fit, text document is to the left of br; align
817 hscroll = widthUsed - width;
819 if (preeditLength > 0) {
820 // check to ensure long pre-edit text doesn't push the cursor
822 cix = qRound(control->cursorToX(
823 control->cursor() + qMax(0, control->preeditCursor() - 1)));
830 void QSGTextInput::paint(QPainter *p)
833 QRect r = boundingRect().toRect();
836 p->setRenderHint(QPainter::TextAntialiasing, true);
838 p->setPen(QPen(d->color));
839 int flags = QLineControl::DrawText;
840 if(!isReadOnly() && d->cursorVisible && !d->cursorItem)
841 flags |= QLineControl::DrawCursor;
842 if (d->control->hasSelectedText())
843 flags |= QLineControl::DrawSelections;
844 QFontMetrics fm = QFontMetrics(d->font);
845 // the y offset is there to keep the baseline constant in case we have script changes in the text.
846 QPoint offset(-d->hscroll, fm.ascent() - d->control->ascent());
847 d->control->draw(p, offset, r, flags);
851 QVariant QSGTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
853 Q_D(const QSGTextInput);
855 case Qt::ImMicroFocus:
856 return cursorRectangle();
859 case Qt::ImCursorPosition:
860 return QVariant(d->control->cursor());
861 case Qt::ImSurroundingText:
862 if (d->control->echoMode() == PasswordEchoOnEdit && !d->control->passwordEchoEditing())
863 return QVariant(displayText());
865 return QVariant(text());
866 case Qt::ImCurrentSelection:
867 return QVariant(selectedText());
868 case Qt::ImMaximumTextLength:
869 return QVariant(maxLength());
870 case Qt::ImAnchorPosition:
871 if (d->control->selectionStart() == d->control->selectionEnd())
872 return QVariant(d->control->cursor());
873 else if (d->control->selectionStart() == d->control->cursor())
874 return QVariant(d->control->selectionEnd());
876 return QVariant(d->control->selectionStart());
882 void QSGTextInput::deselect()
885 d->control->deselect();
888 void QSGTextInput::selectAll()
891 d->control->setSelection(0, d->control->text().length());
894 bool QSGTextInput::isRightToLeft(int start, int end)
898 qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
901 return d->control->text().mid(start, end - start).isRightToLeft();
905 #ifndef QT_NO_CLIPBOARD
906 void QSGTextInput::cut()
913 void QSGTextInput::copy()
919 void QSGTextInput::paste()
922 if (!d->control->isReadOnly())
925 #endif // QT_NO_CLIPBOARD
927 void QSGTextInput::selectWord()
930 d->control->selectWordAtPos(d->control->cursor());
933 QString QSGTextInput::passwordCharacter() const
935 Q_D(const QSGTextInput);
936 return QString(d->control->passwordCharacter());
939 void QSGTextInput::setPasswordCharacter(const QString &str)
944 d->control->setPasswordCharacter(str.constData()[0]);
945 EchoMode echoMode_ = echoMode();
946 if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) {
949 emit passwordCharacterChanged();
952 QString QSGTextInput::displayText() const
954 Q_D(const QSGTextInput);
955 return d->control->displayText();
958 bool QSGTextInput::selectByMouse() const
960 Q_D(const QSGTextInput);
961 return d->selectByMouse;
964 void QSGTextInput::setSelectByMouse(bool on)
967 if (d->selectByMouse != on) {
968 d->selectByMouse = on;
969 emit selectByMouseChanged(on);
973 QSGTextInput::SelectionMode QSGTextInput::mouseSelectionMode() const
975 Q_D(const QSGTextInput);
976 return d->mouseSelectionMode;
979 void QSGTextInput::setMouseSelectionMode(SelectionMode mode)
982 if (d->mouseSelectionMode != mode) {
983 d->mouseSelectionMode = mode;
984 emit mouseSelectionModeChanged(mode);
988 bool QSGTextInput::canPaste() const
990 Q_D(const QSGTextInput);
994 void QSGTextInput::moveCursorSelection(int position)
997 d->control->moveCursor(position, true);
1000 void QSGTextInput::moveCursorSelection(int pos, SelectionMode mode)
1004 if (mode == SelectCharacters) {
1005 d->control->moveCursor(pos, true);
1006 } else if (pos != d->control->cursor()){
1007 const int cursor = d->control->cursor();
1009 if (!d->control->hasSelectedText())
1010 anchor = d->control->cursor();
1011 else if (d->control->selectionStart() == d->control->cursor())
1012 anchor = d->control->selectionEnd();
1014 anchor = d->control->selectionStart();
1016 if (anchor < pos || (anchor == pos && cursor < pos)) {
1017 const QString text = d->control->text();
1018 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
1019 finder.setPosition(anchor);
1021 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
1022 if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord)
1023 || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) {
1024 finder.toPreviousBoundary();
1026 anchor = finder.position() != -1 ? finder.position() : 0;
1028 finder.setPosition(pos);
1029 if (pos > 0 && !finder.boundaryReasons())
1030 finder.toNextBoundary();
1031 const int cursor = finder.position() != -1 ? finder.position() : text.length();
1033 d->control->setSelection(anchor, cursor - anchor);
1034 } else if (anchor > pos || (anchor == pos && cursor > pos)) {
1035 const QString text = d->control->text();
1036 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
1037 finder.setPosition(anchor);
1039 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
1040 if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord)
1041 || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) {
1042 finder.toNextBoundary();
1045 anchor = finder.position() != -1 ? finder.position() : text.length();
1047 finder.setPosition(pos);
1048 if (pos < text.length() && !finder.boundaryReasons())
1049 finder.toPreviousBoundary();
1050 const int cursor = finder.position() != -1 ? finder.position() : 0;
1052 d->control->setSelection(anchor, cursor - anchor);
1057 void QSGTextInput::openSoftwareInputPanel()
1061 QEvent event(QEvent::RequestSoftwareInputPanel);
1062 QApplication::sendEvent(canvas(), &event);
1067 void QSGTextInput::closeSoftwareInputPanel()
1071 QEvent event(QEvent::CloseSoftwareInputPanel);
1072 QApplication::sendEvent(canvas(), &event);
1077 void QSGTextInput::focusInEvent(QFocusEvent *event)
1079 Q_D(const QSGTextInput);
1080 if (d->showInputPanelOnFocus) {
1081 if (d->focusOnPress && !isReadOnly()) {
1082 openSoftwareInputPanel();
1085 QSGPaintedItem::focusInEvent(event);
1088 void QSGTextInput::itemChange(ItemChange change, const ItemChangeData &value)
1091 if (change == ItemActiveFocusHasChanged) {
1092 bool hasFocus = value.boolValue;
1093 d->focused = hasFocus;
1094 setCursorVisible(hasFocus); // ### refactor: && d->canvas && d->canvas->hasFocus()
1095 if(echoMode() == QSGTextInput::PasswordEchoOnEdit && !hasFocus)
1096 d->control->updatePasswordEchoEditing(false);//QLineControl sets it on key events, but doesn't deal with focus events
1098 d->control->deselect();
1100 QSGItem::itemChange(change, value);
1103 bool QSGTextInput::isInputMethodComposing() const
1105 Q_D(const QSGTextInput);
1106 return d->control->preeditAreaText().length() > 0;
1109 void QSGTextInputPrivate::init()
1112 control->setParent(q);//Now mandatory due to accessibility changes
1113 control->setCursorWidth(1);
1114 control->setPasswordCharacter(QLatin1Char('*'));
1115 q->setSmooth(smooth);
1116 q->setAcceptedMouseButtons(Qt::LeftButton);
1117 q->setFlag(QSGItem::ItemAcceptsInputMethod);
1118 q->connect(control, SIGNAL(cursorPositionChanged(int,int)),
1119 q, SLOT(cursorPosChanged()));
1120 q->connect(control, SIGNAL(selectionChanged()),
1121 q, SLOT(selectionChanged()));
1122 q->connect(control, SIGNAL(textChanged(QString)),
1123 q, SLOT(q_textChanged()));
1124 q->connect(control, SIGNAL(accepted()),
1125 q, SIGNAL(accepted()));
1126 q->connect(control, SIGNAL(updateNeeded(QRect)),
1127 q, SLOT(updateRect(QRect)));
1128 #ifndef QT_NO_CLIPBOARD
1129 q->connect(q, SIGNAL(readOnlyChanged(bool)),
1130 q, SLOT(q_canPasteChanged()));
1131 q->connect(QApplication::clipboard(), SIGNAL(dataChanged()),
1132 q, SLOT(q_canPasteChanged()));
1133 canPaste = !control->isReadOnly() && QApplication::clipboard()->text().length() != 0;
1134 #endif // QT_NO_CLIPBOARD
1135 q->connect(control, SIGNAL(updateMicroFocus()),
1136 q, SLOT(updateCursorRectangle()));
1137 q->connect(control, SIGNAL(displayTextChanged(QString)),
1138 q, SLOT(updateRect()));
1140 oldValidity = control->hasAcceptableInput();
1141 lastSelectionStart = 0;
1142 lastSelectionEnd = 0;
1143 QPalette p = control->palette();
1144 selectedTextColor = p.color(QPalette::HighlightedText);
1145 selectionColor = p.color(QPalette::Highlight);
1146 determineHorizontalAlignment();
1149 void QSGTextInput::cursorPosChanged()
1152 updateCursorRectangle();
1153 emit cursorPositionChanged();
1154 // XXX todo - not in 4.8?
1156 d->control->resetCursorBlinkTimer();
1159 if(!d->control->hasSelectedText()){
1160 if(d->lastSelectionStart != d->control->cursor()){
1161 d->lastSelectionStart = d->control->cursor();
1162 emit selectionStartChanged();
1164 if(d->lastSelectionEnd != d->control->cursor()){
1165 d->lastSelectionEnd = d->control->cursor();
1166 emit selectionEndChanged();
1171 void QSGTextInput::updateCursorRectangle()
1174 d->updateHorizontalScroll();
1175 updateRect();//TODO: Only update rect between pos's
1177 emit cursorRectangleChanged();
1179 d->cursorItem->setX(d->control->cursorToX() - d->hscroll);
1182 void QSGTextInput::selectionChanged()
1185 updateRect();//TODO: Only update rect in selection
1186 emit selectedTextChanged();
1188 if(d->lastSelectionStart != d->control->selectionStart()){
1189 d->lastSelectionStart = d->control->selectionStart();
1190 if(d->lastSelectionStart == -1)
1191 d->lastSelectionStart = d->control->cursor();
1192 emit selectionStartChanged();
1194 if(d->lastSelectionEnd != d->control->selectionEnd()){
1195 d->lastSelectionEnd = d->control->selectionEnd();
1196 if(d->lastSelectionEnd == -1)
1197 d->lastSelectionEnd = d->control->cursor();
1198 emit selectionEndChanged();
1202 void QSGTextInput::q_textChanged()
1206 emit displayTextChanged();
1208 d->determineHorizontalAlignment();
1209 d->updateHorizontalScroll();
1211 if(hasAcceptableInput() != d->oldValidity){
1212 d->oldValidity = hasAcceptableInput();
1213 emit acceptableInputChanged();
1217 void QSGTextInput::updateRect(const QRect &r)
1223 update(QRect(r.x() - d->hscroll, r.y(), r.width(), r.height()));
1226 QRectF QSGTextInput::boundingRect() const
1228 Q_D(const QSGTextInput);
1229 QRectF r = QSGPaintedItem::boundingRect();
1231 int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth();
1233 // Could include font max left/right bearings to either side of rectangle.
1235 r.setRight(r.right() + cursorWidth);
1239 void QSGTextInput::updateSize(bool needsRedraw)
1244 setImplicitHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text.
1245 setImplicitWidth(d->calculateTextWidth());
1246 setContentsSize(boundingRect().size().toSize());
1247 if(w==width() && h==height() && needsRedraw)
1251 void QSGTextInput::q_canPasteChanged()
1254 bool old = d->canPaste;
1255 #ifndef QT_NO_CLIPBOARD
1256 d->canPaste = !d->control->isReadOnly() && QApplication::clipboard()->text().length() != 0;
1258 if(d->canPaste != old)
1259 emit canPasteChanged();