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 "qsgtextedit_p.h"
43 #include "qsgtextedit_p_p.h"
44 #include "qsgevents_p_p.h"
45 #include "qsgcanvas.h"
46 #include "qsgtextnode_p.h"
47 #include "qsgsimplerectnode.h"
49 #include <QtDeclarative/qdeclarativeinfo.h>
50 #include <QtWidgets/qapplication.h>
51 #include <QtWidgets/qgraphicssceneevent.h>
52 #include <QtGui/qpainter.h>
53 #include <QtGui/qtextobject.h>
54 #include <QtCore/qmath.h>
56 #include <private/qdeclarativeglobal_p.h>
57 #include <private/qtextcontrol_p.h>
58 #include <private/qtextengine_p.h>
59 #include <private/qwidget_p.h>
60 #include <private/qsgdistancefieldglyphcache_p.h>
61 #include <private/qsgtexture_p.h>
62 #include <private/qsgadaptationlayer_p.h>
66 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
68 QWidgetPrivate *qt_widget_private(QWidget *widget);
70 \qmlclass TextEdit QSGTextEdit
71 \inqmlmodule QtQuick 2
72 \ingroup qml-basic-visual-elements
73 \brief The TextEdit item displays multiple lines of editable formatted text.
76 The TextEdit item displays a block of editable, formatted text.
78 It can display both plain and rich text. For example:
83 text: "<b>Hello</b> <i>World!</i>"
84 font.family: "Helvetica"
91 \image declarative-textedit.gif
93 Setting \l {Item::focus}{focus} to \c true enables the TextEdit item to receive keyboard focus.
95 Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific
96 to a look-and-feel. For example, to add flickable scrolling that follows the cursor:
98 \snippet snippets/declarative/texteditor.qml 0
100 A particular look-and-feel might use smooth scrolling (eg. using SmoothedFollow), might have a visible
101 scrollbar, or a scrollbar that fades in to show location, etc.
103 Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can
104 be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely
105 from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord().
107 You can translate between cursor positions (characters from the start of the document) and pixel
108 points using positionAt() and positionToRectangle().
110 \sa Text, TextInput, {declarative/text/textselection}{Text Selection example}
114 \qmlsignal QtQuick2::TextEdit::onLinkActivated(string link)
116 This handler is called when the user clicks on a link embedded in the text.
117 The link must be in rich text or HTML format and the
118 \a link string provides access to the particular link.
120 QSGTextEdit::QSGTextEdit(QSGItem *parent)
121 : QSGImplicitSizeItem(*(new QSGTextEditPrivate), parent)
127 QString QSGTextEdit::text() const
129 Q_D(const QSGTextEdit);
131 #ifndef QT_NO_TEXTHTMLPARSER
133 return d->document->toHtml();
136 return d->document->toPlainText();
140 \qmlproperty string QtQuick2::TextEdit::font.family
142 Sets the family name of the font.
144 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
145 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
146 If the family isn't available a family will be set using the font matching algorithm.
150 \qmlproperty bool QtQuick2::TextEdit::font.bold
152 Sets whether the font weight is bold.
156 \qmlproperty enumeration QtQuick2::TextEdit::font.weight
158 Sets the font's weight.
160 The weight can be one of:
163 \o Font.Normal - the default
170 TextEdit { text: "Hello"; font.weight: Font.DemiBold }
175 \qmlproperty bool QtQuick2::TextEdit::font.italic
177 Sets whether the font has an italic style.
181 \qmlproperty bool QtQuick2::TextEdit::font.underline
183 Sets whether the text is underlined.
187 \qmlproperty bool QtQuick2::TextEdit::font.strikeout
189 Sets whether the font has a strikeout style.
193 \qmlproperty real QtQuick2::TextEdit::font.pointSize
195 Sets the font size in points. The point size must be greater than zero.
199 \qmlproperty int QtQuick2::TextEdit::font.pixelSize
201 Sets the font size in pixels.
203 Using this function makes the font device dependent. Use
204 \l{TextEdit::font.pointSize} to set the size of the font in a
205 device independent manner.
209 \qmlproperty real QtQuick2::TextEdit::font.letterSpacing
211 Sets the letter spacing for the font.
213 Letter spacing changes the default spacing between individual letters in the font.
214 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
218 \qmlproperty real QtQuick2::TextEdit::font.wordSpacing
220 Sets the word spacing for the font.
222 Word spacing changes the default spacing between individual words.
223 A positive value increases the word spacing by a corresponding amount of pixels,
224 while a negative value decreases the inter-word spacing accordingly.
228 \qmlproperty enumeration QtQuick2::TextEdit::font.capitalization
230 Sets the capitalization for the text.
233 \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
234 \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
235 \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
236 \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
237 \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
241 TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase }
246 \qmlproperty string QtQuick2::TextEdit::text
248 The text to display. If the text format is AutoText the text edit will
249 automatically determine whether the text should be treated as
250 rich text. This determination is made using Qt::mightBeRichText().
252 void QSGTextEdit::setText(const QString &text)
255 if (QSGTextEdit::text() == text)
258 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
260 #ifndef QT_NO_TEXTHTMLPARSER
261 d->control->setHtml(text);
263 d->control->setPlainText(text);
265 d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
267 d->control->setPlainText(text);
273 \qmlproperty enumeration QtQuick2::TextEdit::textFormat
275 The way the text property should be displayed.
279 \o TextEdit.PlainText
283 The default is TextEdit.AutoText. If the text format is TextEdit.AutoText the text edit
284 will automatically determine whether the text should be treated as
285 rich text. This determination is made using Qt::mightBeRichText().
294 text: "<b>Hello</b> <i>World!</i>"
298 textFormat: TextEdit.RichText
299 text: "<b>Hello</b> <i>World!</i>"
303 textFormat: TextEdit.PlainText
304 text: "<b>Hello</b> <i>World!</i>"
308 \o \image declarative-textformat.png
311 QSGTextEdit::TextFormat QSGTextEdit::textFormat() const
313 Q_D(const QSGTextEdit);
317 void QSGTextEdit::setTextFormat(TextFormat format)
320 if (format == d->format)
322 bool wasRich = d->richText;
323 d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
325 if (wasRich && !d->richText) {
326 d->control->setPlainText(d->text);
328 } else if (!wasRich && d->richText) {
329 #ifndef QT_NO_TEXTHTMLPARSER
330 d->control->setHtml(d->text);
332 d->control->setPlainText(d->text);
335 d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
338 d->control->setAcceptRichText(d->format != PlainText);
339 emit textFormatChanged(d->format);
342 QFont QSGTextEdit::font() const
344 Q_D(const QSGTextEdit);
345 return d->sourceFont;
348 void QSGTextEdit::setFont(const QFont &font)
351 if (d->sourceFont == font)
354 d->sourceFont = font;
355 QFont oldFont = d->font;
357 if (d->font.pointSizeF() != -1) {
359 qreal size = qRound(d->font.pointSizeF()*2.0);
360 d->font.setPointSizeF(size/2.0);
363 if (oldFont != d->font) {
364 d->document->setDefaultFont(d->font);
366 d->cursor->setHeight(QFontMetrics(d->font).height());
367 moveCursorDelegate();
372 emit fontChanged(d->sourceFont);
376 \qmlproperty color QtQuick2::TextEdit::color
381 // green text using hexadecimal notation
382 TextEdit { color: "#00FF00" }
386 // steelblue text using SVG color name
387 TextEdit { color: "steelblue" }
390 QColor QSGTextEdit::color() const
392 Q_D(const QSGTextEdit);
396 void QSGTextEdit::setColor(const QColor &color)
399 if (d->color == color)
403 QPalette pal = d->control->palette();
404 pal.setColor(QPalette::Text, color);
405 d->control->setPalette(pal);
407 emit colorChanged(d->color);
411 \qmlproperty color QtQuick2::TextEdit::selectionColor
413 The text highlight color, used behind selections.
415 QColor QSGTextEdit::selectionColor() const
417 Q_D(const QSGTextEdit);
418 return d->selectionColor;
421 void QSGTextEdit::setSelectionColor(const QColor &color)
424 if (d->selectionColor == color)
427 d->selectionColor = color;
428 QPalette pal = d->control->palette();
429 pal.setColor(QPalette::Highlight, color);
430 d->control->setPalette(pal);
432 emit selectionColorChanged(d->selectionColor);
436 \qmlproperty color QtQuick2::TextEdit::selectedTextColor
438 The selected text color, used in selections.
440 QColor QSGTextEdit::selectedTextColor() const
442 Q_D(const QSGTextEdit);
443 return d->selectedTextColor;
446 void QSGTextEdit::setSelectedTextColor(const QColor &color)
449 if (d->selectedTextColor == color)
452 d->selectedTextColor = color;
453 QPalette pal = d->control->palette();
454 pal.setColor(QPalette::HighlightedText, color);
455 d->control->setPalette(pal);
457 emit selectedTextColorChanged(d->selectedTextColor);
461 \qmlproperty enumeration QtQuick2::TextEdit::horizontalAlignment
462 \qmlproperty enumeration QtQuick2::TextEdit::verticalAlignment
463 \qmlproperty enumeration QtQuick2::TextEdit::effectiveHorizontalAlignment
465 Sets the horizontal and vertical alignment of the text within the TextEdit item's
466 width and height. By default, the text alignment follows the natural alignment
467 of the text, for example text that is read from left to right will be aligned to
470 Valid values for \c horizontalAlignment are:
472 \o TextEdit.AlignLeft (default)
473 \o TextEdit.AlignRight
474 \o TextEdit.AlignHCenter
475 \o TextEdit.AlignJustify
478 Valid values for \c verticalAlignment are:
480 \o TextEdit.AlignTop (default)
481 \o TextEdit.AlignBottom
482 \o TextEdit.AlignVCenter
485 When using the attached property LayoutMirroring::enabled to mirror application
486 layouts, the horizontal alignment of text will also be mirrored. However, the property
487 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
488 of TextEdit, use the read-only property \c effectiveHorizontalAlignment.
490 QSGTextEdit::HAlignment QSGTextEdit::hAlign() const
492 Q_D(const QSGTextEdit);
496 void QSGTextEdit::setHAlign(HAlignment align)
499 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
500 d->hAlignImplicit = false;
501 if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
502 d->updateDefaultTextOption();
507 void QSGTextEdit::resetHAlign()
510 d->hAlignImplicit = true;
511 if (d->determineHorizontalAlignment() && isComponentComplete()) {
512 d->updateDefaultTextOption();
517 QSGTextEdit::HAlignment QSGTextEdit::effectiveHAlign() const
519 Q_D(const QSGTextEdit);
520 QSGTextEdit::HAlignment effectiveAlignment = d->hAlign;
521 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
523 case QSGTextEdit::AlignLeft:
524 effectiveAlignment = QSGTextEdit::AlignRight;
526 case QSGTextEdit::AlignRight:
527 effectiveAlignment = QSGTextEdit::AlignLeft;
533 return effectiveAlignment;
536 bool QSGTextEditPrivate::setHAlign(QSGTextEdit::HAlignment alignment, bool forceAlign)
539 if (hAlign != alignment || forceAlign) {
540 QSGTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
542 emit q->horizontalAlignmentChanged(alignment);
543 if (oldEffectiveHAlign != q->effectiveHAlign())
544 emit q->effectiveHorizontalAlignmentChanged();
550 bool QSGTextEditPrivate::determineHorizontalAlignment()
553 if (hAlignImplicit && q->isComponentComplete()) {
554 bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
555 return setHAlign(alignToRight ? QSGTextEdit::AlignRight : QSGTextEdit::AlignLeft);
560 void QSGTextEditPrivate::mirrorChange()
563 if (q->isComponentComplete()) {
564 if (!hAlignImplicit && (hAlign == QSGTextEdit::AlignRight || hAlign == QSGTextEdit::AlignLeft)) {
565 updateDefaultTextOption();
567 emit q->effectiveHorizontalAlignmentChanged();
572 QSGTextEdit::VAlignment QSGTextEdit::vAlign() const
574 Q_D(const QSGTextEdit);
578 void QSGTextEdit::setVAlign(QSGTextEdit::VAlignment alignment)
581 if (alignment == d->vAlign)
583 d->vAlign = alignment;
584 d->updateDefaultTextOption();
586 moveCursorDelegate();
587 emit verticalAlignmentChanged(d->vAlign);
590 \qmlproperty enumeration QtQuick2::TextEdit::wrapMode
592 Set this property to wrap the text to the TextEdit item's width.
593 The text will only wrap if an explicit width has been set.
596 \o TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
597 \o TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
598 \o TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
599 \o TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
602 The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap.
604 QSGTextEdit::WrapMode QSGTextEdit::wrapMode() const
606 Q_D(const QSGTextEdit);
610 void QSGTextEdit::setWrapMode(WrapMode mode)
613 if (mode == d->wrapMode)
616 d->updateDefaultTextOption();
618 emit wrapModeChanged();
622 \qmlproperty int QtQuick2::TextEdit::lineCount
624 Returns the total number of lines in the textEdit item.
626 int QSGTextEdit::lineCount() const
628 Q_D(const QSGTextEdit);
633 \qmlproperty real QtQuick2::TextEdit::paintedWidth
635 Returns the width of the text, including the width past the width
636 which is covered due to insufficient wrapping if \l wrapMode is set.
638 qreal QSGTextEdit::paintedWidth() const
640 Q_D(const QSGTextEdit);
641 return d->paintedSize.width();
645 \qmlproperty real QtQuick2::TextEdit::paintedHeight
647 Returns the height of the text, including the height past the height
648 that is covered if the text does not fit within the set height.
650 qreal QSGTextEdit::paintedHeight() const
652 Q_D(const QSGTextEdit);
653 return d->paintedSize.height();
657 \qmlmethod rectangle QtQuick2::TextEdit::positionToRectangle(position)
659 Returns the rectangle at the given \a position in the text. The x, y,
660 and height properties correspond to the cursor that would describe
663 QRectF QSGTextEdit::positionToRectangle(int pos) const
665 Q_D(const QSGTextEdit);
666 QTextCursor c(d->document);
668 return d->control->cursorRect(c);
673 \qmlmethod int QtQuick2::TextEdit::positionAt(int x, int y)
675 Returns the text position closest to pixel position (\a x, \a y).
677 Position 0 is before the first character, position 1 is after the first character
678 but before the second, and so on until position \l {text}.length, which is after all characters.
680 int QSGTextEdit::positionAt(int x, int y) const
682 Q_D(const QSGTextEdit);
683 int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit);
684 QTextCursor cursor = d->control->textCursor();
685 if (r > cursor.position()) {
686 // The cursor position includes positions within the preedit text, but only positions in the
687 // same text block are offset so it is possible to get a position that is either part of the
688 // preedit or the next text block.
689 QTextLayout *layout = cursor.block().layout();
690 const int preeditLength = layout
691 ? layout->preeditAreaText().length()
693 if (preeditLength > 0
694 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) {
695 r = r > cursor.position() + preeditLength
704 \qmlmethod void QtQuick2::TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters)
706 Moves the cursor to \a position and updates the selection according to the optional \a mode
707 parameter. (To only move the cursor, set the \l cursorPosition property.)
709 When this method is called it additionally sets either the
710 selectionStart or the selectionEnd (whichever was at the previous cursor position)
711 to the specified position. This allows you to easily extend and contract the selected
714 The selection mode specifies whether the selection is updated on a per character or a per word
715 basis. If not specified the selection mode will default to TextEdit.SelectCharacters.
718 \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
719 the previous cursor position) to the specified position.
720 \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
721 words between the specified postion and the previous cursor position. Words partially in the
725 For example, take this sequence of calls:
729 moveCursorSelection(9, TextEdit.SelectCharacters)
730 moveCursorSelection(7, TextEdit.SelectCharacters)
733 This moves the cursor to position 5, extend the selection end from 5 to 9
734 and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
735 selected (the 6th and 7th characters).
737 The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary
738 before or on position 5 and extend the selection end to a word boundary on or past position 9.
740 void QSGTextEdit::moveCursorSelection(int pos)
742 //Note that this is the same as setCursorPosition but with the KeepAnchor flag set
744 QTextCursor cursor = d->control->textCursor();
745 if (cursor.position() == pos)
747 cursor.setPosition(pos, QTextCursor::KeepAnchor);
748 d->control->setTextCursor(cursor);
751 void QSGTextEdit::moveCursorSelection(int pos, SelectionMode mode)
754 QTextCursor cursor = d->control->textCursor();
755 if (cursor.position() == pos)
757 if (mode == SelectCharacters) {
758 cursor.setPosition(pos, QTextCursor::KeepAnchor);
759 } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
760 if (cursor.anchor() > cursor.position()) {
761 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
762 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
763 if (cursor.position() == cursor.anchor())
764 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor);
766 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
768 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
769 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
772 cursor.setPosition(pos, QTextCursor::KeepAnchor);
773 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
774 if (cursor.position() != pos)
775 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
776 } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
777 if (cursor.anchor() < cursor.position()) {
778 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
779 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
781 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
782 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
783 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
784 if (cursor.position() != cursor.anchor()) {
785 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
786 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
790 cursor.setPosition(pos, QTextCursor::KeepAnchor);
791 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
792 if (cursor.position() != pos) {
793 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
794 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
797 d->control->setTextCursor(cursor);
801 \qmlproperty bool QtQuick2::TextEdit::cursorVisible
802 If true the text edit shows a cursor.
804 This property is set and unset when the text edit gets active focus, but it can also
805 be set directly (useful, for example, if a KeyProxy might forward keys to it).
807 bool QSGTextEdit::isCursorVisible() const
809 Q_D(const QSGTextEdit);
810 return d->cursorVisible;
813 void QSGTextEdit::setCursorVisible(bool on)
816 if (d->cursorVisible == on)
818 d->cursorVisible = on;
819 QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut);
820 if (!on && !d->persistentSelection)
821 d->control->setCursorIsFocusIndicator(true);
822 d->control->processEvent(&focusEvent, QPointF(0, -d->yoff));
823 emit cursorVisibleChanged(d->cursorVisible);
827 \qmlproperty int QtQuick2::TextEdit::cursorPosition
828 The position of the cursor in the TextEdit.
830 int QSGTextEdit::cursorPosition() const
832 Q_D(const QSGTextEdit);
833 return d->control->textCursor().position();
836 void QSGTextEdit::setCursorPosition(int pos)
839 if (pos < 0 || pos > d->text.length())
841 QTextCursor cursor = d->control->textCursor();
842 if (cursor.position() == pos && cursor.anchor() == pos)
844 cursor.setPosition(pos);
845 d->control->setTextCursor(cursor);
849 \qmlproperty Component QtQuick2::TextEdit::cursorDelegate
850 The delegate for the cursor in the TextEdit.
852 If you set a cursorDelegate for a TextEdit, this delegate will be used for
853 drawing the cursor instead of the standard cursor. An instance of the
854 delegate will be created and managed by the text edit when a cursor is
855 needed, and the x and y properties of delegate instance will be set so as
856 to be one pixel before the top left of the current character.
858 Note that the root item of the delegate component must be a QDeclarativeItem or
859 QDeclarativeItem derived item.
861 QDeclarativeComponent* QSGTextEdit::cursorDelegate() const
863 Q_D(const QSGTextEdit);
864 return d->cursorComponent;
867 void QSGTextEdit::setCursorDelegate(QDeclarativeComponent* c)
870 if(d->cursorComponent){
872 d->control->setCursorWidth(-1);
878 d->cursorComponent = c;
879 if(c && c->isReady()){
880 loadCursorDelegate();
883 connect(c, SIGNAL(statusChanged()),
884 this, SLOT(loadCursorDelegate()));
887 emit cursorDelegateChanged();
890 void QSGTextEdit::loadCursorDelegate()
893 if(d->cursorComponent->isLoading())
895 d->cursor = qobject_cast<QSGItem*>(d->cursorComponent->create(qmlContext(this)));
897 d->control->setCursorWidth(0);
899 QDeclarative_setParent_noEvent(d->cursor, this);
900 d->cursor->setParentItem(this);
901 d->cursor->setHeight(QFontMetrics(d->font).height());
902 moveCursorDelegate();
904 qmlInfo(this) << "Error loading cursor delegate.";
909 \qmlproperty int QtQuick2::TextEdit::selectionStart
911 The cursor position before the first character in the current selection.
913 This property is read-only. To change the selection, use select(start,end),
914 selectAll(), or selectWord().
916 \sa selectionEnd, cursorPosition, selectedText
918 int QSGTextEdit::selectionStart() const
920 Q_D(const QSGTextEdit);
921 return d->control->textCursor().selectionStart();
925 \qmlproperty int QtQuick2::TextEdit::selectionEnd
927 The cursor position after the last character in the current selection.
929 This property is read-only. To change the selection, use select(start,end),
930 selectAll(), or selectWord().
932 \sa selectionStart, cursorPosition, selectedText
934 int QSGTextEdit::selectionEnd() const
936 Q_D(const QSGTextEdit);
937 return d->control->textCursor().selectionEnd();
941 \qmlproperty string QtQuick2::TextEdit::selectedText
943 This read-only property provides the text currently selected in the
946 It is equivalent to the following snippet, but is faster and easier
949 //myTextEdit is the id of the TextEdit
950 myTextEdit.text.toString().substring(myTextEdit.selectionStart,
951 myTextEdit.selectionEnd);
954 QString QSGTextEdit::selectedText() const
956 Q_D(const QSGTextEdit);
957 return d->control->textCursor().selectedText();
961 \qmlproperty bool QtQuick2::TextEdit::activeFocusOnPress
963 Whether the TextEdit should gain active focus on a mouse press. By default this is
966 bool QSGTextEdit::focusOnPress() const
968 Q_D(const QSGTextEdit);
969 return d->focusOnPress;
972 void QSGTextEdit::setFocusOnPress(bool on)
975 if (d->focusOnPress == on)
977 d->focusOnPress = on;
978 emit activeFocusOnPressChanged(d->focusOnPress);
982 \qmlproperty bool QtQuick2::TextEdit::persistentSelection
984 Whether the TextEdit should keep the selection visible when it loses active focus to another
985 item in the scene. By default this is set to true;
987 bool QSGTextEdit::persistentSelection() const
989 Q_D(const QSGTextEdit);
990 return d->persistentSelection;
993 void QSGTextEdit::setPersistentSelection(bool on)
996 if (d->persistentSelection == on)
998 d->persistentSelection = on;
999 emit persistentSelectionChanged(d->persistentSelection);
1003 \qmlproperty real QtQuick2::TextEdit::textMargin
1005 The margin, in pixels, around the text in the TextEdit.
1007 qreal QSGTextEdit::textMargin() const
1009 Q_D(const QSGTextEdit);
1010 return d->textMargin;
1013 void QSGTextEdit::setTextMargin(qreal margin)
1016 if (d->textMargin == margin)
1018 d->textMargin = margin;
1019 d->document->setDocumentMargin(d->textMargin);
1020 emit textMarginChanged(d->textMargin);
1023 void QSGTextEdit::geometryChanged(const QRectF &newGeometry,
1024 const QRectF &oldGeometry)
1026 if (newGeometry.width() != oldGeometry.width())
1028 QSGImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
1032 Ensures any delayed caching or data loading the class
1033 needs to performed is complete.
1035 void QSGTextEdit::componentComplete()
1038 QSGImplicitSizeItem::componentComplete();
1041 d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
1045 d->determineHorizontalAlignment();
1046 d->updateDefaultTextOption();
1053 \qmlproperty bool QtQuick2::TextEdit::selectByMouse
1057 If true, the user can use the mouse to select text in some
1058 platform-specific way. Note that for some platforms this may
1059 not be an appropriate interaction (eg. may conflict with how
1060 the text needs to behave inside a Flickable.
1062 bool QSGTextEdit::selectByMouse() const
1064 Q_D(const QSGTextEdit);
1065 return d->selectByMouse;
1068 void QSGTextEdit::setSelectByMouse(bool on)
1071 if (d->selectByMouse != on) {
1072 d->selectByMouse = on;
1073 setKeepMouseGrab(on);
1075 setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1077 setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1078 emit selectByMouseChanged(on);
1083 \qmlproperty enum QtQuick2::TextEdit::mouseSelectionMode
1085 Specifies how text should be selected using a mouse.
1088 \o TextEdit.SelectCharacters - The selection is updated with individual characters. (Default)
1089 \o TextEdit.SelectWords - The selection is updated with whole words.
1092 This property only applies when \l selectByMouse is true.
1094 QSGTextEdit::SelectionMode QSGTextEdit::mouseSelectionMode() const
1096 Q_D(const QSGTextEdit);
1097 return d->mouseSelectionMode;
1100 void QSGTextEdit::setMouseSelectionMode(SelectionMode mode)
1103 if (d->mouseSelectionMode != mode) {
1104 d->mouseSelectionMode = mode;
1105 d->control->setWordSelectionEnabled(mode == SelectWords);
1106 emit mouseSelectionModeChanged(mode);
1111 \qmlproperty bool QtQuick2::TextEdit::readOnly
1113 Whether the user can interact with the TextEdit item. If this
1114 property is set to true the text cannot be edited by user interaction.
1116 By default this property is false.
1118 void QSGTextEdit::setReadOnly(bool r)
1121 if (r == isReadOnly())
1124 setFlag(QSGItem::ItemAcceptsInputMethod, !r);
1125 Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1126 if (d->selectByMouse)
1127 flags = flags | Qt::TextSelectableByMouse;
1129 flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable;
1130 d->control->setTextInteractionFlags(flags);
1132 d->control->moveCursor(QTextCursor::End);
1134 emit readOnlyChanged(r);
1137 bool QSGTextEdit::isReadOnly() const
1139 Q_D(const QSGTextEdit);
1140 return !(d->control->textInteractionFlags() & Qt::TextEditable);
1144 Sets how the text edit should interact with user input to the given
1147 void QSGTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
1150 d->control->setTextInteractionFlags(flags);
1154 Returns the flags specifying how the text edit should interact
1157 Qt::TextInteractionFlags QSGTextEdit::textInteractionFlags() const
1159 Q_D(const QSGTextEdit);
1160 return d->control->textInteractionFlags();
1164 \qmlproperty rectangle QtQuick2::TextEdit::cursorRectangle
1166 The rectangle where the text cursor is rendered
1167 within the text edit. Read-only.
1169 QRect QSGTextEdit::cursorRectangle() const
1171 Q_D(const QSGTextEdit);
1172 return d->control->cursorRect().toRect().translated(0,d->yoff);
1175 bool QSGTextEdit::event(QEvent *event)
1178 if (event->type() == QEvent::ShortcutOverride) {
1179 d->control->processEvent(event, QPointF(0, -d->yoff));
1180 return event->isAccepted();
1182 return QSGImplicitSizeItem::event(event);
1187 Handles the given key \a event.
1189 void QSGTextEdit::keyPressEvent(QKeyEvent *event)
1192 d->control->processEvent(event, QPointF(0, -d->yoff));
1193 if (!event->isAccepted())
1194 QSGImplicitSizeItem::keyPressEvent(event);
1199 Handles the given key \a event.
1201 void QSGTextEdit::keyReleaseEvent(QKeyEvent *event)
1204 d->control->processEvent(event, QPointF(0, -d->yoff));
1205 if (!event->isAccepted())
1206 QSGImplicitSizeItem::keyReleaseEvent(event);
1210 \qmlmethod void QtQuick2::TextEdit::deselect()
1212 Removes active text selection.
1214 void QSGTextEdit::deselect()
1217 QTextCursor c = d->control->textCursor();
1219 d->control->setTextCursor(c);
1223 \qmlmethod void QtQuick2::TextEdit::selectAll()
1225 Causes all text to be selected.
1227 void QSGTextEdit::selectAll()
1230 d->control->selectAll();
1234 \qmlmethod void QtQuick2::TextEdit::selectWord()
1236 Causes the word closest to the current cursor position to be selected.
1238 void QSGTextEdit::selectWord()
1241 QTextCursor c = d->control->textCursor();
1242 c.select(QTextCursor::WordUnderCursor);
1243 d->control->setTextCursor(c);
1247 \qmlmethod void QtQuick2::TextEdit::select(int start, int end)
1249 Causes the text from \a start to \a end to be selected.
1251 If either start or end is out of range, the selection is not changed.
1253 After calling this, selectionStart will become the lesser
1254 and selectionEnd will become the greater (regardless of the order passed
1257 \sa selectionStart, selectionEnd
1259 void QSGTextEdit::select(int start, int end)
1262 if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length())
1264 QTextCursor cursor = d->control->textCursor();
1265 cursor.beginEditBlock();
1266 cursor.setPosition(start, QTextCursor::MoveAnchor);
1267 cursor.setPosition(end, QTextCursor::KeepAnchor);
1268 cursor.endEditBlock();
1269 d->control->setTextCursor(cursor);
1272 updateSelectionMarkers();
1276 \qmlmethod void QtQuick2::TextEdit::isRightToLeft(int start, int end)
1278 Returns true if the natural reading direction of the editor text
1279 found between positions \a start and \a end is right to left.
1281 bool QSGTextEdit::isRightToLeft(int start, int end)
1285 qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
1288 return d->text.mid(start, end - start).isRightToLeft();
1292 #ifndef QT_NO_CLIPBOARD
1294 \qmlmethod QtQuick2::TextEdit::cut()
1296 Moves the currently selected text to the system clipboard.
1298 void QSGTextEdit::cut()
1305 \qmlmethod QtQuick2::TextEdit::copy()
1307 Copies the currently selected text to the system clipboard.
1309 void QSGTextEdit::copy()
1316 \qmlmethod QtQuick2::TextEdit::paste()
1318 Replaces the currently selected text by the contents of the system clipboard.
1320 void QSGTextEdit::paste()
1323 d->control->paste();
1325 #endif // QT_NO_CLIPBOARD
1329 Handles the given mouse \a event.
1331 void QSGTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event)
1334 if (d->focusOnPress){
1335 bool hadActiveFocus = hasActiveFocus();
1337 if (d->showInputPanelOnFocus) {
1338 if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) {
1339 // re-open input panel on press if already focused
1340 openSoftwareInputPanel();
1342 } else { // show input panel on click
1343 if (hasActiveFocus() && !hadActiveFocus) {
1344 d->clickCausedFocus = true;
1348 d->control->processEvent(event, QPointF(0, -d->yoff));
1349 if (!event->isAccepted())
1350 QSGImplicitSizeItem::mousePressEvent(event);
1355 Handles the given mouse \a event.
1357 void QSGTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1360 d->control->processEvent(event, QPointF(0, -d->yoff));
1361 if (!d->showInputPanelOnFocus) { // input panel on click
1362 if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) {
1363 // ### refactor: port properly
1364 qDebug("QSGTextEdit: virtual keyboard handling not implemented");
1365 // if (canvas() && canvas() == qApp->focusWidget()) {
1366 // qt_widget_private(canvas())->handleSoftwareInputPanel(event->button(), d->clickCausedFocus);
1370 d->clickCausedFocus = false;
1372 if (!event->isAccepted())
1373 QSGImplicitSizeItem::mouseReleaseEvent(event);
1378 Handles the given mouse \a event.
1380 void QSGTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
1383 d->control->processEvent(event, QPointF(0, -d->yoff));
1384 if (!event->isAccepted())
1385 QSGImplicitSizeItem::mouseDoubleClickEvent(event);
1390 Handles the given mouse \a event.
1392 void QSGTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1395 d->control->processEvent(event, QPointF(0, -d->yoff));
1396 if (!event->isAccepted())
1397 QSGImplicitSizeItem::mouseMoveEvent(event);
1402 Handles the given input method \a event.
1404 void QSGTextEdit::inputMethodEvent(QInputMethodEvent *event)
1407 const bool wasComposing = isInputMethodComposing();
1408 d->control->processEvent(event, QPointF(0, -d->yoff));
1409 if (wasComposing != isInputMethodComposing())
1410 emit inputMethodComposingChanged();
1413 void QSGTextEdit::itemChange(ItemChange change, const ItemChangeData &value)
1416 if (change == ItemActiveFocusHasChanged) {
1417 setCursorVisible(value.boolValue); // ### refactor: focus handling && d->canvas && d->canvas->hasFocus());
1419 QSGItem::itemChange(change, value);
1424 Returns the value of the given \a property.
1426 QVariant QSGTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
1428 Q_D(const QSGTextEdit);
1429 return d->control->inputMethodQuery(property);
1432 void QSGTextEdit::updateImageCache(const QRectF &)
1436 // Do we really need the image cache?
1437 if (!d->richText || !d->isComplexRichText) {
1438 if (!d->pixmapCache.isNull())
1439 d->pixmapCache = QPixmap();
1443 if (width() != d->pixmapCache.width() || height() != d->pixmapCache.height())
1444 d->pixmapCache = QPixmap(width(), height());
1446 if (d->pixmapCache.isNull())
1449 // ### Use supplied rect, clear area and update only this part (for cursor updates)
1450 QRectF bounds = QRectF(0, 0, width(), height());
1451 d->pixmapCache.fill(Qt::transparent);
1453 QPainter painter(&d->pixmapCache);
1455 painter.setRenderHint(QPainter::TextAntialiasing);
1456 painter.translate(0, d->yoff);
1458 d->control->drawContents(&painter, bounds);
1463 QSGNode *QSGTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
1465 Q_UNUSED(updatePaintNodeData);
1468 QSGNode *currentNode = oldNode;
1469 if (d->richText && d->isComplexRichText) {
1470 QSGImageNode *node = 0;
1471 if (oldNode == 0 || d->nodeType != QSGTextEditPrivate::NodeIsTexture) {
1473 node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1474 d->texture = new QSGPlainTexture();
1475 d->nodeType = QSGTextEditPrivate::NodeIsTexture;
1478 node = static_cast<QSGImageNode *>(oldNode);
1481 qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->pixmapCache.toImage());
1482 node->setTexture(0);
1483 node->setTexture(d->texture);
1485 node->setTargetRect(QRectF(0, 0, d->pixmapCache.width(), d->pixmapCache.height()));
1486 node->setSourceRect(QRectF(0, 0, 1, 1));
1487 node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1488 node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1489 node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1492 } else if (oldNode == 0 || d->documentDirty) {
1493 d->documentDirty = false;
1495 #if defined(Q_WS_MAC)
1496 // Make sure document is relayouted in the paint node on Mac
1497 // to avoid crashes due to the font engines created in the
1499 d->document->markContentsDirty(0, d->document->characterCount());
1502 QSGTextNode *node = 0;
1503 if (oldNode == 0 || d->nodeType != QSGTextEditPrivate::NodeIsText) {
1505 node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
1506 d->nodeType = QSGTextEditPrivate::NodeIsText;
1509 node = static_cast<QSGTextNode *>(oldNode);
1512 node->deleteContent();
1513 node->setMatrix(QMatrix4x4());
1515 QRectF bounds = boundingRect();
1517 QColor selectionColor = d->control->palette().color(QPalette::Highlight);
1518 QColor selectedTextColor = d->control->palette().color(QPalette::HighlightedText);
1519 node->addTextDocument(bounds.topLeft(), d->document, d->color, QSGText::Normal, QColor(),
1520 selectionColor, selectedTextColor, selectionStart(),
1523 #if defined(Q_WS_MAC)
1524 // We also need to make sure the document layout is redone when
1525 // control is returned to the main thread, as all the font engines
1526 // are now owned by the rendering thread
1527 d->document->markContentsDirty(0, d->document->characterCount());
1531 if (d->nodeType == QSGTextEditPrivate::NodeIsText && d->cursorComponent == 0 && !isReadOnly()) {
1532 QSGTextNode *node = static_cast<QSGTextNode *>(currentNode);
1534 QColor color = (!d->cursorVisible || !d->control->cursorOn())
1535 ? QColor(0, 0, 0, 0)
1538 if (node->cursorNode() == 0) {
1539 node->setCursor(cursorRectangle(), color);
1541 node->cursorNode()->setRect(cursorRectangle());
1542 node->cursorNode()->setColor(color);
1551 \qmlproperty bool QtQuick2::TextEdit::smooth
1553 This property holds whether the text is smoothly scaled or transformed.
1555 Smooth filtering gives better visual quality, but is slower. If
1556 the item is displayed at its natural size, this property has no visual or
1559 \note Generally scaling artifacts are only visible if the item is stationary on
1560 the screen. A common pattern when animating an item is to disable smooth
1561 filtering at the beginning of the animation and reenable it at the conclusion.
1565 \qmlproperty bool QtQuick2::TextEdit::canPaste
1567 Returns true if the TextEdit is writable and the content of the clipboard is
1568 suitable for pasting into the TextEdit.
1570 bool QSGTextEdit::canPaste() const
1572 Q_D(const QSGTextEdit);
1577 \qmlproperty bool QtQuick2::TextEdit::inputMethodComposing
1580 This property holds whether the TextEdit has partial text input from an
1583 While it is composing an input method may rely on mouse or key events from
1584 the TextEdit to edit or commit the partial text. This property can be used
1585 to determine when to disable events handlers that may interfere with the
1586 correct operation of an input method.
1588 bool QSGTextEdit::isInputMethodComposing() const
1590 Q_D(const QSGTextEdit);
1591 if (QTextLayout *layout = d->control->textCursor().block().layout())
1592 return layout->preeditAreaText().length() > 0;
1596 void QSGTextEditPrivate::init()
1600 q->setSmooth(smooth);
1601 q->setAcceptedMouseButtons(Qt::LeftButton);
1602 q->setFlag(QSGItem::ItemAcceptsInputMethod);
1603 q->setFlag(QSGItem::ItemHasContents);
1605 control = new QTextControl(q);
1606 control->setIgnoreUnusedNavigationEvents(true);
1607 control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
1608 control->setDragEnabled(false);
1610 // By default, QTextControl will issue both a updateCursorRequest() and an updateRequest()
1611 // when the cursor needs to be repainted. We need the signals to be separate to be able to
1612 // distinguish the cursor updates so that we can avoid updating the whole subtree when the
1614 if (!QObject::disconnect(control, SIGNAL(updateCursorRequest(QRectF)),
1615 control, SIGNAL(updateRequest(QRectF)))) {
1616 qWarning("QSGTextEditPrivate::init: Failed to disconnect updateCursorRequest and updateRequest");
1619 // QTextControl follows the default text color
1620 // defined by the platform, declarative text
1621 // should be black by default
1622 QPalette pal = control->palette();
1623 if (pal.color(QPalette::Text) != color) {
1624 pal.setColor(QPalette::Text, color);
1625 control->setPalette(pal);
1628 QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateDocument()));
1629 QObject::connect(control, SIGNAL(updateCursorRequest()), q, SLOT(updateCursor()));
1630 QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged()));
1631 QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
1632 QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers()));
1633 QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers()));
1634 QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged()));
1635 QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(moveCursorDelegate()));
1636 QObject::connect(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString)));
1637 #ifndef QT_NO_CLIPBOARD
1638 QObject::connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged()));
1639 QObject::connect(QApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged()));
1640 canPaste = control->canPaste();
1643 document = control->document();
1644 document->setDefaultFont(font);
1645 document->setDocumentMargin(textMargin);
1646 document->setUndoRedoEnabled(false); // flush undo buffer.
1647 document->setUndoRedoEnabled(true);
1648 updateDefaultTextOption();
1651 void QSGTextEdit::q_textChanged()
1655 d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft();
1656 d->determineHorizontalAlignment();
1657 d->updateDefaultTextOption();
1660 emit textChanged(d->text);
1663 void QSGTextEdit::moveCursorDelegate()
1667 emit cursorRectangleChanged();
1670 QRectF cursorRect = cursorRectangle();
1671 d->cursor->setX(cursorRect.x());
1672 d->cursor->setY(cursorRect.y());
1675 void QSGTextEditPrivate::updateSelection()
1678 QTextCursor cursor = control->textCursor();
1679 bool startChange = (lastSelectionStart != cursor.selectionStart());
1680 bool endChange = (lastSelectionEnd != cursor.selectionEnd());
1681 cursor.beginEditBlock();
1682 cursor.setPosition(lastSelectionStart, QTextCursor::MoveAnchor);
1683 cursor.setPosition(lastSelectionEnd, QTextCursor::KeepAnchor);
1684 cursor.endEditBlock();
1685 control->setTextCursor(cursor);
1687 q->selectionStartChanged();
1689 q->selectionEndChanged();
1692 void QSGTextEdit::updateSelectionMarkers()
1695 if(d->lastSelectionStart != d->control->textCursor().selectionStart()){
1696 d->lastSelectionStart = d->control->textCursor().selectionStart();
1697 emit selectionStartChanged();
1699 if(d->lastSelectionEnd != d->control->textCursor().selectionEnd()){
1700 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
1701 emit selectionEndChanged();
1705 QRectF QSGTextEdit::boundingRect() const
1707 Q_D(const QSGTextEdit);
1708 QRectF r = QSGImplicitSizeItem::boundingRect();
1709 int cursorWidth = 1;
1711 cursorWidth = d->cursor->width();
1712 if(!d->document->isEmpty())
1713 cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
1715 // Could include font max left/right bearings to either side of rectangle.
1717 r.setRight(r.right() + cursorWidth);
1718 return r.translated(0,d->yoff);
1721 qreal QSGTextEditPrivate::getImplicitWidth() const
1723 Q_Q(const QSGTextEdit);
1724 if (!requireImplicitWidth) {
1725 // We don't calculate implicitWidth unless it is required.
1726 // We need to force a size update now to ensure implicitWidth is calculated
1727 const_cast<QSGTextEditPrivate*>(this)->requireImplicitWidth = true;
1728 const_cast<QSGTextEdit*>(q)->updateSize();
1730 return implicitWidth;
1733 //### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't
1734 // need to do all the calculations each time
1735 void QSGTextEdit::updateSize()
1738 if (isComponentComplete()) {
1739 qreal naturalWidth = d->implicitWidth;
1740 // ### assumes that if the width is set, the text will fill to edges
1741 // ### (unless wrap is false, then clipping will occur)
1743 if (!d->requireImplicitWidth) {
1744 emit implicitWidthChanged();
1745 // if the implicitWidth is used, then updateSize() has already been called (recursively)
1746 if (d->requireImplicitWidth)
1749 if (d->requireImplicitWidth) {
1750 d->document->setTextWidth(-1);
1751 naturalWidth = d->document->idealWidth();
1753 if (d->document->textWidth() != width())
1754 d->document->setTextWidth(width());
1756 d->document->setTextWidth(-1);
1758 QFontMetrics fm = QFontMetrics(d->font);
1760 dy -= (int)d->document->size().height();
1763 if (heightValid()) {
1764 if (d->vAlign == AlignBottom)
1766 else if (d->vAlign == AlignVCenter)
1773 if (nyoff != d->yoff)
1775 setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
1777 //### need to comfirm cost of always setting these
1778 int newWidth = qCeil(d->document->idealWidth());
1779 if (!widthValid() && d->document->textWidth() != newWidth)
1780 d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug)
1781 // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
1783 setImplicitWidth(newWidth);
1784 else if (d->requireImplicitWidth)
1785 setImplicitWidth(naturalWidth);
1786 qreal newHeight = d->document->isEmpty() ? fm.height() : (int)d->document->size().height();
1787 setImplicitHeight(newHeight);
1789 d->paintedSize = QSize(newWidth, newHeight);
1790 emit paintedSizeChanged();
1797 void QSGTextEdit::updateDocument()
1800 d->documentDirty = true;
1802 if (isComponentComplete()) {
1808 void QSGTextEdit::updateCursor()
1811 if (isComponentComplete()) {
1812 updateImageCache(d->control->cursorRect());
1817 void QSGTextEdit::updateTotalLines()
1823 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
1824 QTextLayout *layout = it.layout();
1827 subLines += layout->lineCount()-1;
1830 int newTotalLines = d->document->lineCount() + subLines;
1831 if (d->lineCount != newTotalLines) {
1832 d->lineCount = newTotalLines;
1833 emit lineCountChanged();
1837 void QSGTextEditPrivate::updateDefaultTextOption()
1840 QTextOption opt = document->defaultTextOption();
1841 int oldAlignment = opt.alignment();
1843 QSGTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
1844 if (rightToLeftText) {
1845 if (horizontalAlignment == QSGTextEdit::AlignLeft)
1846 horizontalAlignment = QSGTextEdit::AlignRight;
1847 else if (horizontalAlignment == QSGTextEdit::AlignRight)
1848 horizontalAlignment = QSGTextEdit::AlignLeft;
1850 opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign));
1852 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
1853 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
1855 bool oldUseDesignMetrics = opt.useDesignMetrics();
1856 bool useDesignMetrics = !qmlDisableDistanceField();
1857 opt.setUseDesignMetrics(useDesignMetrics);
1859 if (oldWrapMode == opt.wrapMode()
1860 && oldAlignment == opt.alignment()
1861 && oldUseDesignMetrics == useDesignMetrics) {
1864 document->setDefaultTextOption(opt);
1870 \qmlmethod void QtQuick2::TextEdit::openSoftwareInputPanel()
1872 Opens software input panels like virtual keyboards for typing, useful for
1873 customizing when you want the input keyboard to be shown and hidden in
1876 By default the opening of input panels follows the platform style. On Symbian^1 and
1877 Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
1878 the panels are automatically opened when TextEdit element gains active focus. Input panels are
1879 always closed if no editor has active focus.
1881 You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1882 and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1883 the behavior you want.
1885 Only relevant on platforms, which provide virtual keyboards.
1891 text: "Hello world!"
1892 activeFocusOnPress: false
1894 anchors.fill: parent
1896 if (!textEdit.activeFocus) {
1897 textEdit.forceActiveFocus();
1898 textEdit.openSoftwareInputPanel();
1900 textEdit.focus = false;
1903 onPressAndHold: textEdit.closeSoftwareInputPanel();
1908 void QSGTextEdit::openSoftwareInputPanel()
1912 QEvent event(QEvent::RequestSoftwareInputPanel);
1913 QApplication::sendEvent(canvas(), &event);
1919 \qmlmethod void QtQuick2::TextEdit::closeSoftwareInputPanel()
1921 Closes a software input panel like a virtual keyboard shown on the screen, useful
1922 for customizing when you want the input keyboard to be shown and hidden in
1925 By default the opening of input panels follows the platform style. On Symbian^1 and
1926 Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
1927 the panels are automatically opened when TextEdit element gains active focus. Input panels are
1928 always closed if no editor has active focus.
1930 You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1931 and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1932 the behavior you want.
1934 Only relevant on platforms, which provide virtual keyboards.
1940 text: "Hello world!"
1941 activeFocusOnPress: false
1943 anchors.fill: parent
1945 if (!textEdit.activeFocus) {
1946 textEdit.forceActiveFocus();
1947 textEdit.openSoftwareInputPanel();
1949 textEdit.focus = false;
1952 onPressAndHold: textEdit.closeSoftwareInputPanel();
1957 void QSGTextEdit::closeSoftwareInputPanel()
1961 QEvent event(QEvent::CloseSoftwareInputPanel);
1962 QApplication::sendEvent(canvas(), &event);
1967 void QSGTextEdit::focusInEvent(QFocusEvent *event)
1969 Q_D(const QSGTextEdit);
1970 if (d->showInputPanelOnFocus) {
1971 if (d->focusOnPress && !isReadOnly()) {
1972 openSoftwareInputPanel();
1975 QSGImplicitSizeItem::focusInEvent(event);
1978 void QSGTextEdit::q_canPasteChanged()
1981 bool old = d->canPaste;
1982 d->canPaste = d->control->canPaste();
1983 if(old!=d->canPaste)
1984 emit canPasteChanged();