1 // Commit: b94176e69efc3948696c6774d5a228fc753b5b29
2 /****************************************************************************
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
41 ****************************************************************************/
43 #include "qsgtextinput_p.h"
44 #include "qsgtextinput_p_p.h"
45 #include "qsgcanvas.h"
47 #include <private/qdeclarativeglobal_p.h>
48 #include <private/qwidget_p.h>
50 #include <QtDeclarative/qdeclarativeinfo.h>
51 #include <QtGui/qgraphicssceneevent.h>
52 #include <QtGui/qinputcontext.h>
53 #include <QTextBoundaryFinder>
58 QWidgetPrivate *qt_widget_private(QWidget *widget);
60 QSGTextInput::QSGTextInput(QSGItem* parent)
61 : QSGImplicitSizePaintedItem(*(new QSGTextInputPrivate), parent)
67 QSGTextInput::~QSGTextInput()
71 QString QSGTextInput::text() const
73 Q_D(const QSGTextInput);
74 return d->control->text();
77 void QSGTextInput::setText(const QString &s)
82 d->control->setText(s);
85 QFont QSGTextInput::font() const
87 Q_D(const QSGTextInput);
91 void QSGTextInput::setFont(const QFont &font)
94 if (d->sourceFont == font)
98 QFont oldFont = d->font;
100 if (d->font.pointSizeF() != -1) {
102 qreal size = qRound(d->font.pointSizeF()*2.0);
103 d->font.setPointSizeF(size/2.0);
105 if (oldFont != d->font) {
106 d->control->setFont(d->font);
108 d->cursorItem->setHeight(QFontMetrics(d->font).height());
113 emit fontChanged(d->sourceFont);
116 QColor QSGTextInput::color() const
118 Q_D(const QSGTextInput);
122 void QSGTextInput::setColor(const QColor &c)
128 emit colorChanged(c);
132 QColor QSGTextInput::selectionColor() const
134 Q_D(const QSGTextInput);
135 return d->selectionColor;
138 void QSGTextInput::setSelectionColor(const QColor &color)
141 if (d->selectionColor == color)
144 d->selectionColor = color;
145 QPalette p = d->control->palette();
146 p.setColor(QPalette::Highlight, d->selectionColor);
147 d->control->setPalette(p);
148 if (d->control->hasSelectedText())
150 emit selectionColorChanged(color);
153 QColor QSGTextInput::selectedTextColor() const
155 Q_D(const QSGTextInput);
156 return d->selectedTextColor;
159 void QSGTextInput::setSelectedTextColor(const QColor &color)
162 if (d->selectedTextColor == color)
165 d->selectedTextColor = color;
166 QPalette p = d->control->palette();
167 p.setColor(QPalette::HighlightedText, d->selectedTextColor);
168 d->control->setPalette(p);
169 if (d->control->hasSelectedText())
171 emit selectedTextColorChanged(color);
174 QSGTextInput::HAlignment QSGTextInput::hAlign() const
176 Q_D(const QSGTextInput);
180 void QSGTextInput::setHAlign(HAlignment align)
183 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
184 d->hAlignImplicit = false;
185 if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
187 d->updateHorizontalScroll();
191 void QSGTextInput::resetHAlign()
194 d->hAlignImplicit = true;
195 if (d->determineHorizontalAlignment() && isComponentComplete()) {
197 d->updateHorizontalScroll();
201 QSGTextInput::HAlignment QSGTextInput::effectiveHAlign() const
203 Q_D(const QSGTextInput);
204 QSGTextInput::HAlignment effectiveAlignment = d->hAlign;
205 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
207 case QSGTextInput::AlignLeft:
208 effectiveAlignment = QSGTextInput::AlignRight;
210 case QSGTextInput::AlignRight:
211 effectiveAlignment = QSGTextInput::AlignLeft;
217 return effectiveAlignment;
220 bool QSGTextInputPrivate::setHAlign(QSGTextInput::HAlignment alignment, bool forceAlign)
223 if ((hAlign != alignment || forceAlign) && alignment <= QSGTextInput::AlignHCenter) { // justify not supported
224 QSGTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
226 emit q->horizontalAlignmentChanged(alignment);
227 if (oldEffectiveHAlign != q->effectiveHAlign())
228 emit q->effectiveHorizontalAlignmentChanged();
234 bool QSGTextInputPrivate::determineHorizontalAlignment()
236 if (hAlignImplicit) {
237 // if no explicit alignment has been set, follow the natural layout direction of the text
238 QString text = control->text();
239 bool isRightToLeft = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft();
240 return setHAlign(isRightToLeft ? QSGTextInput::AlignRight : QSGTextInput::AlignLeft);
245 void QSGTextInputPrivate::mirrorChange()
248 if (q->isComponentComplete()) {
249 if (!hAlignImplicit && (hAlign == QSGTextInput::AlignRight || hAlign == QSGTextInput::AlignLeft)) {
251 updateHorizontalScroll();
252 emit q->effectiveHorizontalAlignmentChanged();
257 bool QSGTextInput::isReadOnly() const
259 Q_D(const QSGTextInput);
260 return d->control->isReadOnly();
263 void QSGTextInput::setReadOnly(bool ro)
266 if (d->control->isReadOnly() == ro)
269 setFlag(QSGItem::ItemAcceptsInputMethod, !ro);
270 d->control->setReadOnly(ro);
272 emit readOnlyChanged(ro);
275 int QSGTextInput::maxLength() const
277 Q_D(const QSGTextInput);
278 return d->control->maxLength();
281 void QSGTextInput::setMaxLength(int ml)
284 if (d->control->maxLength() == ml)
287 d->control->setMaxLength(ml);
289 emit maximumLengthChanged(ml);
292 bool QSGTextInput::isCursorVisible() const
294 Q_D(const QSGTextInput);
295 return d->cursorVisible;
298 void QSGTextInput::setCursorVisible(bool on)
301 if (d->cursorVisible == on)
303 d->cursorVisible = on;
304 d->control->setCursorBlinkPeriod(on?QApplication::cursorFlashTime():0);
305 QRect r = d->control->cursorRect();
306 if (d->control->inputMask().isEmpty())
310 emit cursorVisibleChanged(d->cursorVisible);
313 int QSGTextInput::cursorPosition() const
315 Q_D(const QSGTextInput);
316 return d->control->cursor();
318 void QSGTextInput::setCursorPosition(int cp)
321 if (cp < 0 || cp > d->control->text().length())
323 d->control->moveCursor(cp);
326 QRect QSGTextInput::cursorRectangle() const
328 Q_D(const QSGTextInput);
329 QRect r = d->control->cursorRect();
330 // Scroll and make consistent with TextEdit
331 // QLineControl inexplicably adds 1 to the height and horizontal padding
332 // for unicode direction markers.
333 r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1);
337 int QSGTextInput::selectionStart() const
339 Q_D(const QSGTextInput);
340 return d->lastSelectionStart;
343 int QSGTextInput::selectionEnd() const
345 Q_D(const QSGTextInput);
346 return d->lastSelectionEnd;
349 void QSGTextInput::select(int start, int end)
352 if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length())
354 d->control->setSelection(start, end-start);
357 QString QSGTextInput::selectedText() const
359 Q_D(const QSGTextInput);
360 return d->control->selectedText();
363 bool QSGTextInput::focusOnPress() const
365 Q_D(const QSGTextInput);
366 return d->focusOnPress;
369 void QSGTextInput::setFocusOnPress(bool b)
372 if (d->focusOnPress == b)
377 emit activeFocusOnPressChanged(d->focusOnPress);
380 bool QSGTextInput::autoScroll() const
382 Q_D(const QSGTextInput);
383 return d->autoScroll;
386 void QSGTextInput::setAutoScroll(bool b)
389 if (d->autoScroll == b)
393 //We need to repaint so that the scrolling is taking into account.
395 d->updateHorizontalScroll();
396 emit autoScrollChanged(d->autoScroll);
399 #ifndef QT_NO_VALIDATOR
400 QValidator* QSGTextInput::validator() const
402 Q_D(const QSGTextInput);
403 //###const cast isn't good, but needed for property system?
404 return const_cast<QValidator*>(d->control->validator());
407 void QSGTextInput::setValidator(QValidator* v)
410 if (d->control->validator() == v)
413 d->control->setValidator(v);
414 if(!d->control->hasAcceptableInput()){
415 d->oldValidity = false;
416 emit acceptableInputChanged();
419 emit validatorChanged();
421 #endif // QT_NO_VALIDATOR
423 QString QSGTextInput::inputMask() const
425 Q_D(const QSGTextInput);
426 return d->control->inputMask();
429 void QSGTextInput::setInputMask(const QString &im)
432 if (d->control->inputMask() == im)
435 d->control->setInputMask(im);
436 emit inputMaskChanged(d->control->inputMask());
439 bool QSGTextInput::hasAcceptableInput() const
441 Q_D(const QSGTextInput);
442 return d->control->hasAcceptableInput();
445 QSGTextInput::EchoMode QSGTextInput::echoMode() const
447 Q_D(const QSGTextInput);
448 return (QSGTextInput::EchoMode)d->control->echoMode();
451 void QSGTextInput::setEchoMode(QSGTextInput::EchoMode echo)
454 if (echoMode() == echo)
456 Qt::InputMethodHints imHints = inputMethodHints();
457 if (echo == Password || echo == NoEcho)
458 imHints |= Qt::ImhHiddenText;
460 imHints &= ~Qt::ImhHiddenText;
462 imHints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
464 imHints &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
465 setInputMethodHints(imHints);
466 d->control->setEchoMode((uint)echo);
468 emit echoModeChanged(echoMode());
471 QDeclarativeComponent* QSGTextInput::cursorDelegate() const
473 Q_D(const QSGTextInput);
474 return d->cursorComponent;
477 void QSGTextInput::setCursorDelegate(QDeclarativeComponent* c)
480 if (d->cursorComponent == c)
483 d->cursorComponent = c;
485 //note that the components are owned by something else
486 disconnect(d->control, SIGNAL(cursorPositionChanged(int,int)),
487 this, SLOT(moveCursor()));
488 delete d->cursorItem;
490 d->startCreatingCursor();
493 emit cursorDelegateChanged();
496 void QSGTextInputPrivate::startCreatingCursor()
499 q->connect(control, SIGNAL(cursorPositionChanged(int,int)),
500 q, SLOT(moveCursor()));
501 if(cursorComponent->isReady()){
503 }else if(cursorComponent->isLoading()){
504 q->connect(cursorComponent, SIGNAL(statusChanged(int)),
505 q, SLOT(createCursor()));
507 qmlInfo(q, cursorComponent->errors()) << QSGTextInput::tr("Could not load cursor delegate");
511 void QSGTextInput::createCursor()
514 if(d->cursorComponent->isError()){
515 qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate");
519 if(!d->cursorComponent->isReady())
523 delete d->cursorItem;
524 d->cursorItem = qobject_cast<QSGItem*>(d->cursorComponent->create());
526 qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate");
530 QDeclarative_setParent_noEvent(d->cursorItem, this);
531 d->cursorItem->setParentItem(this);
532 d->cursorItem->setX(d->control->cursorToX());
533 d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text.
536 void QSGTextInput::moveCursor()
541 d->updateHorizontalScroll();
542 d->cursorItem->setX(d->control->cursorToX() - d->hscroll);
545 QRectF QSGTextInput::positionToRectangle(int pos) const
547 Q_D(const QSGTextInput);
548 if (pos > d->control->cursorPosition())
549 pos += d->control->preeditAreaText().length();
550 return QRectF(d->control->cursorToX(pos)-d->hscroll,
552 d->control->cursorWidth(),
553 cursorRectangle().height());
556 int QSGTextInput::positionAt(int x) const
558 return positionAt(x, CursorBetweenCharacters);
561 int QSGTextInput::positionAt(int x, CursorPosition position) const
563 Q_D(const QSGTextInput);
564 int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position));
565 const int cursor = d->control->cursor();
567 const int preeditLength = d->control->preeditAreaText().length();
568 pos = pos > cursor + preeditLength
569 ? pos - preeditLength
575 void QSGTextInput::keyPressEvent(QKeyEvent* ev)
578 // Don't allow MacOSX up/down support, and we don't allow a completer.
579 bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier;
580 if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) {
581 // Ignore when moving off the end unless there is a selection,
582 // because then moving will do something (deselect).
583 int cursorPosition = d->control->cursor();
584 if (cursorPosition == 0)
585 ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
586 if (cursorPosition == d->control->text().length())
587 ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
592 d->control->processKeyEvent(ev);
594 if (!ev->isAccepted())
595 QSGPaintedItem::keyPressEvent(ev);
598 void QSGTextInput::inputMethodEvent(QInputMethodEvent *ev)
601 const bool wasComposing = d->control->preeditAreaText().length() > 0;
602 if (d->control->isReadOnly()) {
605 d->control->processInputMethodEvent(ev);
607 d->updateHorizontalScroll();
609 if (!ev->isAccepted())
610 QSGPaintedItem::inputMethodEvent(ev);
612 if (wasComposing != (d->control->preeditAreaText().length() > 0))
613 emit inputMethodComposingChanged();
616 void QSGTextInput::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
619 if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonDblClick))
621 if (d->selectByMouse) {
622 int cursor = d->xToPos(event->pos().x());
623 d->control->selectWordAtPos(cursor);
624 event->setAccepted(true);
626 QSGPaintedItem::mouseDoubleClickEvent(event);
630 void QSGTextInput::mousePressEvent(QGraphicsSceneMouseEvent *event)
633 if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonPress))
636 bool hadActiveFocus = hasActiveFocus();
638 if (d->showInputPanelOnFocus) {
639 if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) {
640 // re-open input panel on press if already focused
641 openSoftwareInputPanel();
643 } else { // show input panel on click
644 if (hasActiveFocus() && !hadActiveFocus) {
645 d->clickCausedFocus = true;
649 if (d->selectByMouse) {
650 setKeepMouseGrab(false);
651 d->pressPos = event->pos();
653 bool mark = event->modifiers() & Qt::ShiftModifier;
654 int cursor = d->xToPos(event->pos().x());
655 d->control->moveCursor(cursor, mark);
656 event->setAccepted(true);
659 void QSGTextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
662 if (d->sendMouseEventToInputContext(event, QEvent::MouseMove))
664 if (d->selectByMouse) {
665 if (qAbs(int(event->pos().x() - d->pressPos.x())) > QApplication::startDragDistance())
666 setKeepMouseGrab(true);
667 moveCursorSelection(d->xToPos(event->pos().x()), d->mouseSelectionMode);
668 event->setAccepted(true);
670 QSGPaintedItem::mouseMoveEvent(event);
674 void QSGTextInput::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
677 if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonRelease))
679 if (d->selectByMouse)
680 setKeepMouseGrab(false);
681 if (!d->showInputPanelOnFocus) { // input panel on click
682 if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) {
683 if (canvas() && canvas() == qApp->focusWidget()) {
684 qt_widget_private(canvas())->handleSoftwareInputPanel(event->button(), d->clickCausedFocus);
688 d->clickCausedFocus = false;
689 d->control->processEvent(event);
690 if (!event->isAccepted())
691 QSGPaintedItem::mouseReleaseEvent(event);
694 bool QSGTextInputPrivate::sendMouseEventToInputContext(
695 QGraphicsSceneMouseEvent *event, QEvent::Type eventType)
697 #if !defined QT_NO_IM
698 if (event->widget() && control->composeMode()) {
699 int tmp_cursor = xToPos(event->pos().x());
700 int mousePos = tmp_cursor - control->cursor();
701 if (mousePos < 0 || mousePos > control->preeditAreaText().length()) {
703 // don't send move events outside the preedit area
704 if (eventType == QEvent::MouseMove)
708 QInputContext *qic = event->widget()->inputContext();
710 QMouseEvent mouseEvent(
712 event->widget()->mapFromGlobal(event->screenPos()),
717 // may be causing reset() in some input methods
718 qic->mouseHandler(mousePos, &mouseEvent);
719 event->setAccepted(mouseEvent.isAccepted());
721 if (!control->preeditAreaText().isEmpty())
732 void QSGTextInput::mouseUngrabEvent()
734 setKeepMouseGrab(false);
737 bool QSGTextInput::event(QEvent* ev)
740 //Anything we don't deal with ourselves, pass to the control
741 bool handled = false;
743 case QEvent::KeyPress:
744 case QEvent::KeyRelease://###Should the control be doing anything with release?
745 case QEvent::InputMethod:
746 case QEvent::GraphicsSceneMousePress:
747 case QEvent::GraphicsSceneMouseMove:
748 case QEvent::GraphicsSceneMouseRelease:
749 case QEvent::GraphicsSceneMouseDoubleClick:
752 handled = d->control->processEvent(ev);
755 handled = QSGPaintedItem::event(ev);
759 void QSGTextInput::geometryChanged(const QRectF &newGeometry,
760 const QRectF &oldGeometry)
763 if (newGeometry.width() != oldGeometry.width()) {
765 d->updateHorizontalScroll();
767 QSGPaintedItem::geometryChanged(newGeometry, oldGeometry);
770 int QSGTextInputPrivate::calculateTextWidth()
772 return qRound(control->naturalTextWidth());
775 void QSGTextInputPrivate::updateHorizontalScroll()
778 const int preeditLength = control->preeditAreaText().length();
779 int cix = qRound(control->cursorToX(control->cursor() + preeditLength));
780 QRect br(q->boundingRect().toRect());
781 int widthUsed = calculateTextWidth();
783 QSGTextInput::HAlignment effectiveHAlign = q->effectiveHAlign();
785 if (widthUsed <= br.width()) {
786 // text fits in br; use hscroll for alignment
787 switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) {
789 hscroll = widthUsed - br.width() - 1;
791 case Qt::AlignHCenter:
792 hscroll = (widthUsed - br.width()) / 2;
799 } else if (cix - hscroll >= br.width()) {
800 // text doesn't fit, cursor is to the right of br (scroll right)
801 hscroll = cix - br.width() + 1;
802 } else if (cix - hscroll < 0 && hscroll < widthUsed) {
803 // text doesn't fit, cursor is to the left of br (scroll left)
805 } else if (widthUsed - hscroll < br.width()) {
806 // text doesn't fit, text document is to the left of br; align
808 hscroll = widthUsed - br.width() + 1;
810 if (preeditLength > 0) {
811 // check to ensure long pre-edit text doesn't push the cursor
813 cix = qRound(control->cursorToX(
814 control->cursor() + qMax(0, control->preeditCursor() - 1)));
819 switch (effectiveHAlign) {
820 case QSGTextInput::AlignRight:
821 hscroll = q->width() - widthUsed;
823 case QSGTextInput::AlignHCenter:
824 hscroll = (q->width() - widthUsed) / 2;
834 void QSGTextInput::paint(QPainter *p)
837 QRect r(0, 0, width(), height());
840 p->setRenderHint(QPainter::TextAntialiasing, true);
842 p->setPen(QPen(d->color));
843 int flags = QLineControl::DrawText;
844 if(!isReadOnly() && d->cursorVisible && !d->cursorItem)
845 flags |= QLineControl::DrawCursor;
846 if (d->control->hasSelectedText())
847 flags |= QLineControl::DrawSelections;
848 QPoint offset = QPoint(0,0);
849 QFontMetrics fm = QFontMetrics(d->font);
850 QRect br(boundingRect().toRect());
852 // the y offset is there to keep the baseline constant in case we have script changes in the text.
853 offset = br.topLeft() - QPoint(d->hscroll, d->control->ascent() - fm.ascent());
855 offset = QPoint(d->hscroll, 0);
857 d->control->draw(p, offset, r, flags);
861 QVariant QSGTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
863 Q_D(const QSGTextInput);
865 case Qt::ImMicroFocus:
866 return cursorRectangle();
869 case Qt::ImCursorPosition:
870 return QVariant(d->control->cursor());
871 case Qt::ImSurroundingText:
872 if (d->control->echoMode() == PasswordEchoOnEdit && !d->control->passwordEchoEditing())
873 return QVariant(displayText());
875 return QVariant(text());
876 case Qt::ImCurrentSelection:
877 return QVariant(selectedText());
878 case Qt::ImMaximumTextLength:
879 return QVariant(maxLength());
880 case Qt::ImAnchorPosition:
881 if (d->control->selectionStart() == d->control->selectionEnd())
882 return QVariant(d->control->cursor());
883 else if (d->control->selectionStart() == d->control->cursor())
884 return QVariant(d->control->selectionEnd());
886 return QVariant(d->control->selectionStart());
892 void QSGTextInput::deselect()
895 d->control->deselect();
898 void QSGTextInput::selectAll()
901 d->control->setSelection(0, d->control->text().length());
904 bool QSGTextInput::isRightToLeft(int start, int end)
908 qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
911 return d->control->text().mid(start, end - start).isRightToLeft();
915 #ifndef QT_NO_CLIPBOARD
916 void QSGTextInput::cut()
923 void QSGTextInput::copy()
929 void QSGTextInput::paste()
932 if (!d->control->isReadOnly())
935 #endif // QT_NO_CLIPBOARD
937 void QSGTextInput::selectWord()
940 d->control->selectWordAtPos(d->control->cursor());
943 QString QSGTextInput::passwordCharacter() const
945 Q_D(const QSGTextInput);
946 return QString(d->control->passwordCharacter());
949 void QSGTextInput::setPasswordCharacter(const QString &str)
954 d->control->setPasswordCharacter(str.constData()[0]);
955 EchoMode echoMode_ = echoMode();
956 if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) {
959 emit passwordCharacterChanged();
962 QString QSGTextInput::displayText() const
964 Q_D(const QSGTextInput);
965 return d->control->displayText();
968 bool QSGTextInput::selectByMouse() const
970 Q_D(const QSGTextInput);
971 return d->selectByMouse;
974 void QSGTextInput::setSelectByMouse(bool on)
977 if (d->selectByMouse != on) {
978 d->selectByMouse = on;
979 emit selectByMouseChanged(on);
983 QSGTextInput::SelectionMode QSGTextInput::mouseSelectionMode() const
985 Q_D(const QSGTextInput);
986 return d->mouseSelectionMode;
989 void QSGTextInput::setMouseSelectionMode(SelectionMode mode)
992 if (d->mouseSelectionMode != mode) {
993 d->mouseSelectionMode = mode;
994 emit mouseSelectionModeChanged(mode);
998 bool QSGTextInput::canPaste() const
1000 Q_D(const QSGTextInput);
1004 void QSGTextInput::moveCursorSelection(int position)
1007 d->control->moveCursor(position, true);
1008 d->updateHorizontalScroll();
1011 void QSGTextInput::moveCursorSelection(int pos, SelectionMode mode)
1015 if (mode == SelectCharacters) {
1016 d->control->moveCursor(pos, true);
1017 } else if (pos != d->control->cursor()){
1018 const int cursor = d->control->cursor();
1020 if (!d->control->hasSelectedText())
1021 anchor = d->control->cursor();
1022 else if (d->control->selectionStart() == d->control->cursor())
1023 anchor = d->control->selectionEnd();
1025 anchor = d->control->selectionStart();
1027 if (anchor < pos || (anchor == pos && cursor < pos)) {
1028 const QString text = d->control->text();
1029 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
1030 finder.setPosition(anchor);
1032 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
1033 if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord)
1034 || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) {
1035 finder.toPreviousBoundary();
1037 anchor = finder.position() != -1 ? finder.position() : 0;
1039 finder.setPosition(pos);
1040 if (pos > 0 && !finder.boundaryReasons())
1041 finder.toNextBoundary();
1042 const int cursor = finder.position() != -1 ? finder.position() : text.length();
1044 d->control->setSelection(anchor, cursor - anchor);
1045 } else if (anchor > pos || (anchor == pos && cursor > pos)) {
1046 const QString text = d->control->text();
1047 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
1048 finder.setPosition(anchor);
1050 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
1051 if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord)
1052 || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) {
1053 finder.toNextBoundary();
1056 anchor = finder.position() != -1 ? finder.position() : text.length();
1058 finder.setPosition(pos);
1059 if (pos < text.length() && !finder.boundaryReasons())
1060 finder.toPreviousBoundary();
1061 const int cursor = finder.position() != -1 ? finder.position() : 0;
1063 d->control->setSelection(anchor, cursor - anchor);
1068 void QSGTextInput::openSoftwareInputPanel()
1070 QEvent event(QEvent::RequestSoftwareInputPanel);
1072 if (canvas() && canvas() == qApp->focusWidget()) {
1073 QEvent event(QEvent::RequestSoftwareInputPanel);
1074 QApplication::sendEvent(canvas(), &event);
1079 void QSGTextInput::closeSoftwareInputPanel()
1082 if (canvas() && canvas() == qApp->focusWidget()) {
1083 QEvent event(QEvent::CloseSoftwareInputPanel);
1084 QApplication::sendEvent(canvas(), &event);
1089 void QSGTextInput::focusInEvent(QFocusEvent *event)
1091 Q_D(const QSGTextInput);
1092 if (d->showInputPanelOnFocus) {
1093 if (d->focusOnPress && !isReadOnly()) {
1094 openSoftwareInputPanel();
1097 QSGPaintedItem::focusInEvent(event);
1100 void QSGTextInput::itemChange(ItemChange change, const ItemChangeData &value)
1103 if (change == ItemActiveFocusHasChanged) {
1104 bool hasFocus = value.boolValue;
1105 d->focused = hasFocus;
1106 setCursorVisible(hasFocus && d->canvas && d->canvas->hasFocus());
1107 if(echoMode() == QSGTextInput::PasswordEchoOnEdit && !hasFocus)
1108 d->control->updatePasswordEchoEditing(false);//QLineControl sets it on key events, but doesn't deal with focus events
1110 d->control->deselect();
1112 QSGItem::itemChange(change, value);
1115 bool QSGTextInput::isInputMethodComposing() const
1117 Q_D(const QSGTextInput);
1118 return d->control->preeditAreaText().length() > 0;
1121 void QSGTextInputPrivate::init()
1124 control->setCursorWidth(1);
1125 control->setPasswordCharacter(QLatin1Char('*'));
1126 q->setSmooth(smooth);
1127 q->setAcceptedMouseButtons(Qt::LeftButton);
1128 q->setFlag(QSGItem::ItemAcceptsInputMethod);
1129 q->connect(control, SIGNAL(cursorPositionChanged(int,int)),
1130 q, SLOT(cursorPosChanged()));
1131 q->connect(control, SIGNAL(selectionChanged()),
1132 q, SLOT(selectionChanged()));
1133 q->connect(control, SIGNAL(textChanged(QString)),
1134 q, SLOT(q_textChanged()));
1135 q->connect(control, SIGNAL(accepted()),
1136 q, SIGNAL(accepted()));
1137 q->connect(control, SIGNAL(updateNeeded(QRect)),
1138 q, SLOT(updateRect(QRect)));
1139 #ifndef QT_NO_CLIPBOARD
1140 q->connect(q, SIGNAL(readOnlyChanged(bool)),
1141 q, SLOT(q_canPasteChanged()));
1142 q->connect(QApplication::clipboard(), SIGNAL(dataChanged()),
1143 q, SLOT(q_canPasteChanged()));
1144 canPaste = !control->isReadOnly() && QApplication::clipboard()->text().length() != 0;
1145 #endif // QT_NO_CLIPBOARD
1146 q->connect(control, SIGNAL(updateMicroFocus()),
1147 q, SLOT(updateMicroFocus()));
1148 q->connect(control, SIGNAL(displayTextChanged(QString)),
1149 q, SLOT(updateRect()));
1151 oldValidity = control->hasAcceptableInput();
1152 lastSelectionStart = 0;
1153 lastSelectionEnd = 0;
1154 QPalette p = control->palette();
1155 selectedTextColor = p.color(QPalette::HighlightedText);
1156 selectionColor = p.color(QPalette::Highlight);
1157 determineHorizontalAlignment();
1160 void QSGTextInput::cursorPosChanged()
1163 d->updateHorizontalScroll();
1164 updateRect();//TODO: Only update rect between pos's
1166 emit cursorPositionChanged();
1167 // XXX todo - not in 4.8?
1169 d->control->resetCursorBlinkTimer();
1172 if(!d->control->hasSelectedText()){
1173 if(d->lastSelectionStart != d->control->cursor()){
1174 d->lastSelectionStart = d->control->cursor();
1175 emit selectionStartChanged();
1177 if(d->lastSelectionEnd != d->control->cursor()){
1178 d->lastSelectionEnd = d->control->cursor();
1179 emit selectionEndChanged();
1184 void QSGTextInput::selectionChanged()
1187 updateRect();//TODO: Only update rect in selection
1188 emit selectedTextChanged();
1190 if(d->lastSelectionStart != d->control->selectionStart()){
1191 d->lastSelectionStart = d->control->selectionStart();
1192 if(d->lastSelectionStart == -1)
1193 d->lastSelectionStart = d->control->cursor();
1194 emit selectionStartChanged();
1196 if(d->lastSelectionEnd != d->control->selectionEnd()){
1197 d->lastSelectionEnd = d->control->selectionEnd();
1198 if(d->lastSelectionEnd == -1)
1199 d->lastSelectionEnd = d->control->cursor();
1200 emit selectionEndChanged();
1204 void QSGTextInput::q_textChanged()
1208 d->determineHorizontalAlignment();
1209 d->updateHorizontalScroll();
1212 emit displayTextChanged();
1213 if(hasAcceptableInput() != d->oldValidity){
1214 d->oldValidity = hasAcceptableInput();
1215 emit acceptableInputChanged();
1219 void QSGTextInput::updateRect(const QRect &r)
1225 update(QRect(r.x() - d->hscroll, r.y(), r.width(), r.height()));
1228 QRectF QSGTextInput::boundingRect() const
1230 Q_D(const QSGTextInput);
1231 QRectF r = QSGPaintedItem::boundingRect();
1233 int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth();
1235 // Could include font max left/right bearings to either side of rectangle.
1237 r.setRight(r.right() + cursorWidth);
1241 void QSGTextInput::updateSize(bool needsRedraw)
1246 setImplicitHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text.
1247 setImplicitWidth(d->calculateTextWidth());
1248 setContentsSize(QSize(width(), height()));
1249 if(w==width() && h==height() && needsRedraw)
1253 void QSGTextInput::q_canPasteChanged()
1256 bool old = d->canPaste;
1257 #ifndef QT_NO_CLIPBOARD
1258 d->canPaste = !d->control->isReadOnly() && QApplication::clipboard()->text().length() != 0;
1260 if(d->canPaste != old)
1261 emit canPasteChanged();