aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/items/qsgtextedit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/items/qsgtextedit.cpp')
-rw-r--r--src/declarative/items/qsgtextedit.cpp850
1 files changed, 802 insertions, 48 deletions
diff --git a/src/declarative/items/qsgtextedit.cpp b/src/declarative/items/qsgtextedit.cpp
index 4ba5bec5e4..1c0ed62f0d 100644
--- a/src/declarative/items/qsgtextedit.cpp
+++ b/src/declarative/items/qsgtextedit.cpp
@@ -43,6 +43,8 @@
#include "qsgtextedit_p_p.h"
#include "qsgevents_p_p.h"
#include "qsgcanvas.h"
+#include "qsgtextnode_p.h"
+#include "qsgsimplerectnode.h"
#include <QtDeclarative/qdeclarativeinfo.h>
#include <QtWidgets/qapplication.h>
@@ -55,13 +57,68 @@
#include <private/qtextcontrol_p.h>
#include <private/qtextengine_p.h>
#include <private/qwidget_p.h>
+#include <private/qsgdistancefieldglyphcache_p.h>
+#include <private/qsgtexture_p.h>
+#include <private/qsgadaptationlayer_p.h>
QT_BEGIN_NAMESPACE
+DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
+
QWidgetPrivate *qt_widget_private(QWidget *widget);
+/*!
+ \qmlclass TextEdit QSGTextEdit
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+ \brief The TextEdit item displays multiple lines of editable formatted text.
+ \inherits Item
+
+ The TextEdit item displays a block of editable, formatted text.
+
+ It can display both plain and rich text. For example:
+
+ \qml
+TextEdit {
+ width: 240
+ text: "<b>Hello</b> <i>World!</i>"
+ font.family: "Helvetica"
+ font.pointSize: 20
+ color: "blue"
+ focus: true
+}
+ \endqml
+
+ \image declarative-textedit.gif
+
+ Setting \l {Item::focus}{focus} to \c true enables the TextEdit item to receive keyboard focus.
+
+ Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific
+ to a look-and-feel. For example, to add flickable scrolling that follows the cursor:
+ \snippet snippets/declarative/texteditor.qml 0
+
+ A particular look-and-feel might use smooth scrolling (eg. using SmoothedFollow), might have a visible
+ scrollbar, or a scrollbar that fades in to show location, etc.
+
+ Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can
+ be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely
+ from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord().
+
+ You can translate between cursor positions (characters from the start of the document) and pixel
+ points using positionAt() and positionToRectangle().
+
+ \sa Text, TextInput, {declarative/text/textselection}{Text Selection example}
+*/
+
+/*!
+ \qmlsignal QtQuick2::TextEdit::onLinkActivated(string link)
+
+ This handler is called when the user clicks on a link embedded in the text.
+ The link must be in rich text or HTML format and the
+ \a link string provides access to the particular link.
+*/
QSGTextEdit::QSGTextEdit(QSGItem *parent)
-: QSGImplicitSizePaintedItem(*(new QSGTextEditPrivate), parent)
+: QSGImplicitSizeItem(*(new QSGTextEditPrivate), parent)
{
Q_D(QSGTextEdit);
d->init();
@@ -79,6 +136,119 @@ QString QSGTextEdit::text() const
return d->document->toPlainText();
}
+/*!
+ \qmlproperty string QtQuick2::TextEdit::font.family
+
+ Sets the family name of the font.
+
+ The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
+ If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
+ If the family isn't available a family will be set using the font matching algorithm.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::font.bold
+
+ Sets whether the font weight is bold.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick2::TextEdit::font.weight
+
+ Sets the font's weight.
+
+ The weight can be one of:
+ \list
+ \o Font.Light
+ \o Font.Normal - the default
+ \o Font.DemiBold
+ \o Font.Bold
+ \o Font.Black
+ \endlist
+
+ \qml
+ TextEdit { text: "Hello"; font.weight: Font.DemiBold }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::font.italic
+
+ Sets whether the font has an italic style.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::font.underline
+
+ Sets whether the text is underlined.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::font.strikeout
+
+ Sets whether the font has a strikeout style.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::TextEdit::font.pointSize
+
+ Sets the font size in points. The point size must be greater than zero.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::TextEdit::font.pixelSize
+
+ Sets the font size in pixels.
+
+ Using this function makes the font device dependent. Use
+ \l{TextEdit::font.pointSize} to set the size of the font in a
+ device independent manner.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::TextEdit::font.letterSpacing
+
+ Sets the letter spacing for the font.
+
+ Letter spacing changes the default spacing between individual letters in the font.
+ A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::TextEdit::font.wordSpacing
+
+ Sets the word spacing for the font.
+
+ Word spacing changes the default spacing between individual words.
+ A positive value increases the word spacing by a corresponding amount of pixels,
+ while a negative value decreases the inter-word spacing accordingly.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick2::TextEdit::font.capitalization
+
+ Sets the capitalization for the text.
+
+ \list
+ \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
+ \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
+ \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
+ \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
+ \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
+ \endlist
+
+ \qml
+ TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase }
+ \endqml
+*/
+
+/*!
+ \qmlproperty string QtQuick2::TextEdit::text
+
+ The text to display. If the text format is AutoText the text edit will
+ automatically determine whether the text should be treated as
+ rich text. This determination is made using Qt::mightBeRichText().
+*/
void QSGTextEdit::setText(const QString &text)
{
Q_D(QSGTextEdit);
@@ -92,12 +262,52 @@ void QSGTextEdit::setText(const QString &text)
#else
d->control->setPlainText(text);
#endif
+ d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
} else {
d->control->setPlainText(text);
}
q_textChanged();
}
+/*!
+ \qmlproperty enumeration QtQuick2::TextEdit::textFormat
+
+ The way the text property should be displayed.
+
+ \list
+ \o TextEdit.AutoText
+ \o TextEdit.PlainText
+ \o TextEdit.RichText
+ \endlist
+
+ The default is TextEdit.AutoText. If the text format is TextEdit.AutoText the text edit
+ will automatically determine whether the text should be treated as
+ rich text. This determination is made using Qt::mightBeRichText().
+
+ \table
+ \row
+ \o
+ \qml
+Column {
+ TextEdit {
+ font.pointSize: 24
+ text: "<b>Hello</b> <i>World!</i>"
+ }
+ TextEdit {
+ font.pointSize: 24
+ textFormat: TextEdit.RichText
+ text: "<b>Hello</b> <i>World!</i>"
+ }
+ TextEdit {
+ font.pointSize: 24
+ textFormat: TextEdit.PlainText
+ text: "<b>Hello</b> <i>World!</i>"
+ }
+}
+ \endqml
+ \o \image declarative-textformat.png
+ \endtable
+*/
QSGTextEdit::TextFormat QSGTextEdit::textFormat() const
{
Q_D(const QSGTextEdit);
@@ -122,6 +332,7 @@ void QSGTextEdit::setTextFormat(TextFormat format)
d->control->setPlainText(d->text);
#endif
updateSize();
+ d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
}
d->format = format;
d->control->setAcceptRichText(d->format != PlainText);
@@ -156,11 +367,26 @@ void QSGTextEdit::setFont(const QFont &font)
moveCursorDelegate();
}
updateSize();
- update();
+ updateDocument();
}
emit fontChanged(d->sourceFont);
}
+/*!
+ \qmlproperty color QtQuick2::TextEdit::color
+
+ The text color.
+
+ \qml
+ // green text using hexadecimal notation
+ TextEdit { color: "#00FF00" }
+ \endqml
+
+ \qml
+ // steelblue text using SVG color name
+ TextEdit { color: "steelblue" }
+ \endqml
+*/
QColor QSGTextEdit::color() const
{
Q_D(const QSGTextEdit);
@@ -177,10 +403,15 @@ void QSGTextEdit::setColor(const QColor &color)
QPalette pal = d->control->palette();
pal.setColor(QPalette::Text, color);
d->control->setPalette(pal);
- update();
+ updateDocument();
emit colorChanged(d->color);
}
+/*!
+ \qmlproperty color QtQuick2::TextEdit::selectionColor
+
+ The text highlight color, used behind selections.
+*/
QColor QSGTextEdit::selectionColor() const
{
Q_D(const QSGTextEdit);
@@ -197,10 +428,15 @@ void QSGTextEdit::setSelectionColor(const QColor &color)
QPalette pal = d->control->palette();
pal.setColor(QPalette::Highlight, color);
d->control->setPalette(pal);
- update();
+ updateDocument();
emit selectionColorChanged(d->selectionColor);
}
+/*!
+ \qmlproperty color QtQuick2::TextEdit::selectedTextColor
+
+ The selected text color, used in selections.
+*/
QColor QSGTextEdit::selectedTextColor() const
{
Q_D(const QSGTextEdit);
@@ -217,10 +453,40 @@ void QSGTextEdit::setSelectedTextColor(const QColor &color)
QPalette pal = d->control->palette();
pal.setColor(QPalette::HighlightedText, color);
d->control->setPalette(pal);
- update();
+ updateDocument();
emit selectedTextColorChanged(d->selectedTextColor);
}
+/*!
+ \qmlproperty enumeration QtQuick2::TextEdit::horizontalAlignment
+ \qmlproperty enumeration QtQuick2::TextEdit::verticalAlignment
+ \qmlproperty enumeration QtQuick2::TextEdit::effectiveHorizontalAlignment
+
+ Sets the horizontal and vertical alignment of the text within the TextEdit item's
+ width and height. By default, the text alignment follows the natural alignment
+ of the text, for example text that is read from left to right will be aligned to
+ the left.
+
+ Valid values for \c horizontalAlignment are:
+ \list
+ \o TextEdit.AlignLeft (default)
+ \o TextEdit.AlignRight
+ \o TextEdit.AlignHCenter
+ \o TextEdit.AlignJustify
+ \endlist
+
+ Valid values for \c verticalAlignment are:
+ \list
+ \o TextEdit.AlignTop (default)
+ \o TextEdit.AlignBottom
+ \o TextEdit.AlignVCenter
+ \endlist
+
+ When using the attached property LayoutMirroring::enabled to mirror application
+ layouts, the horizontal alignment of text will also be mirrored. However, the property
+ \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
+ of TextEdit, use the read-only property \c effectiveHorizontalAlignment.
+*/
QSGTextEdit::HAlignment QSGTextEdit::hAlign() const
{
Q_D(const QSGTextEdit);
@@ -320,7 +586,21 @@ void QSGTextEdit::setVAlign(QSGTextEdit::VAlignment alignment)
moveCursorDelegate();
emit verticalAlignmentChanged(d->vAlign);
}
+/*!
+ \qmlproperty enumeration QtQuick2::TextEdit::wrapMode
+
+ Set this property to wrap the text to the TextEdit item's width.
+ The text will only wrap if an explicit width has been set.
+
+ \list
+ \o TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
+ \o TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
+ \o TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \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.
+ \endlist
+ The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap.
+*/
QSGTextEdit::WrapMode QSGTextEdit::wrapMode() const
{
Q_D(const QSGTextEdit);
@@ -338,24 +618,48 @@ void QSGTextEdit::setWrapMode(WrapMode mode)
emit wrapModeChanged();
}
+/*!
+ \qmlproperty int QtQuick2::TextEdit::lineCount
+
+ Returns the total number of lines in the textEdit item.
+*/
int QSGTextEdit::lineCount() const
{
Q_D(const QSGTextEdit);
return d->lineCount;
}
+/*!
+ \qmlproperty real QtQuick2::TextEdit::paintedWidth
+
+ Returns the width of the text, including the width past the width
+ which is covered due to insufficient wrapping if \l wrapMode is set.
+*/
qreal QSGTextEdit::paintedWidth() const
{
Q_D(const QSGTextEdit);
return d->paintedSize.width();
}
+/*!
+ \qmlproperty real QtQuick2::TextEdit::paintedHeight
+
+ Returns the height of the text, including the height past the height
+ that is covered if the text does not fit within the set height.
+*/
qreal QSGTextEdit::paintedHeight() const
{
Q_D(const QSGTextEdit);
return d->paintedSize.height();
}
+/*!
+ \qmlmethod rectangle QtQuick2::TextEdit::positionToRectangle(position)
+
+ Returns the rectangle at the given \a position in the text. The x, y,
+ and height properties correspond to the cursor that would describe
+ that position.
+*/
QRectF QSGTextEdit::positionToRectangle(int pos) const
{
Q_D(const QSGTextEdit);
@@ -365,6 +669,14 @@ QRectF QSGTextEdit::positionToRectangle(int pos) const
}
+/*!
+ \qmlmethod int QtQuick2::TextEdit::positionAt(int x, int y)
+
+ Returns the text position closest to pixel position (\a x, \a y).
+
+ Position 0 is before the first character, position 1 is after the first character
+ but before the second, and so on until position \l {text}.length, which is after all characters.
+*/
int QSGTextEdit::positionAt(int x, int y) const
{
Q_D(const QSGTextEdit);
@@ -388,6 +700,43 @@ int QSGTextEdit::positionAt(int x, int y) const
return r;
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters)
+
+ Moves the cursor to \a position and updates the selection according to the optional \a mode
+ parameter. (To only move the cursor, set the \l cursorPosition property.)
+
+ When this method is called it additionally sets either the
+ selectionStart or the selectionEnd (whichever was at the previous cursor position)
+ to the specified position. This allows you to easily extend and contract the selected
+ text range.
+
+ The selection mode specifies whether the selection is updated on a per character or a per word
+ basis. If not specified the selection mode will default to TextEdit.SelectCharacters.
+
+ \list
+ \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
+ the previous cursor position) to the specified position.
+ \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
+ words between the specified postion and the previous cursor position. Words partially in the
+ range are included.
+ \endlist
+
+ For example, take this sequence of calls:
+
+ \code
+ cursorPosition = 5
+ moveCursorSelection(9, TextEdit.SelectCharacters)
+ moveCursorSelection(7, TextEdit.SelectCharacters)
+ \endcode
+
+ This moves the cursor to position 5, extend the selection end from 5 to 9
+ and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
+ selected (the 6th and 7th characters).
+
+ The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary
+ before or on position 5 and extend the selection end to a word boundary on or past position 9.
+*/
void QSGTextEdit::moveCursorSelection(int pos)
{
//Note that this is the same as setCursorPosition but with the KeepAnchor flag set
@@ -448,6 +797,13 @@ void QSGTextEdit::moveCursorSelection(int pos, SelectionMode mode)
d->control->setTextCursor(cursor);
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::cursorVisible
+ If true the text edit shows a cursor.
+
+ This property is set and unset when the text edit gets active focus, but it can also
+ be set directly (useful, for example, if a KeyProxy might forward keys to it).
+*/
bool QSGTextEdit::isCursorVisible() const
{
Q_D(const QSGTextEdit);
@@ -467,6 +823,10 @@ void QSGTextEdit::setCursorVisible(bool on)
emit cursorVisibleChanged(d->cursorVisible);
}
+/*!
+ \qmlproperty int QtQuick2::TextEdit::cursorPosition
+ The position of the cursor in the TextEdit.
+*/
int QSGTextEdit::cursorPosition() const
{
Q_D(const QSGTextEdit);
@@ -485,6 +845,19 @@ void QSGTextEdit::setCursorPosition(int pos)
d->control->setTextCursor(cursor);
}
+/*!
+ \qmlproperty Component QtQuick2::TextEdit::cursorDelegate
+ The delegate for the cursor in the TextEdit.
+
+ If you set a cursorDelegate for a TextEdit, this delegate will be used for
+ drawing the cursor instead of the standard cursor. An instance of the
+ delegate will be created and managed by the text edit when a cursor is
+ needed, and the x and y properties of delegate instance will be set so as
+ to be one pixel before the top left of the current character.
+
+ Note that the root item of the delegate component must be a QDeclarativeItem or
+ QDeclarativeItem derived item.
+*/
QDeclarativeComponent* QSGTextEdit::cursorDelegate() const
{
Q_D(const QSGTextEdit);
@@ -497,7 +870,7 @@ void QSGTextEdit::setCursorDelegate(QDeclarativeComponent* c)
if(d->cursorComponent){
if(d->cursor){
d->control->setCursorWidth(-1);
- update(cursorRectangle());
+ updateCursor();
delete d->cursor;
d->cursor = 0;
}
@@ -522,7 +895,7 @@ void QSGTextEdit::loadCursorDelegate()
d->cursor = qobject_cast<QSGItem*>(d->cursorComponent->create(qmlContext(this)));
if(d->cursor){
d->control->setCursorWidth(0);
- update(cursorRectangle());
+ updateCursor();
QDeclarative_setParent_noEvent(d->cursor, this);
d->cursor->setParentItem(this);
d->cursor->setHeight(QFontMetrics(d->font).height());
@@ -532,24 +905,64 @@ void QSGTextEdit::loadCursorDelegate()
}
}
+/*!
+ \qmlproperty int QtQuick2::TextEdit::selectionStart
+
+ The cursor position before the first character in the current selection.
+
+ This property is read-only. To change the selection, use select(start,end),
+ selectAll(), or selectWord().
+
+ \sa selectionEnd, cursorPosition, selectedText
+*/
int QSGTextEdit::selectionStart() const
{
Q_D(const QSGTextEdit);
return d->control->textCursor().selectionStart();
}
+/*!
+ \qmlproperty int QtQuick2::TextEdit::selectionEnd
+
+ The cursor position after the last character in the current selection.
+
+ This property is read-only. To change the selection, use select(start,end),
+ selectAll(), or selectWord().
+
+ \sa selectionStart, cursorPosition, selectedText
+*/
int QSGTextEdit::selectionEnd() const
{
Q_D(const QSGTextEdit);
return d->control->textCursor().selectionEnd();
}
+/*!
+ \qmlproperty string QtQuick2::TextEdit::selectedText
+
+ This read-only property provides the text currently selected in the
+ text edit.
+
+ It is equivalent to the following snippet, but is faster and easier
+ to use.
+ \code
+ //myTextEdit is the id of the TextEdit
+ myTextEdit.text.toString().substring(myTextEdit.selectionStart,
+ myTextEdit.selectionEnd);
+ \endcode
+*/
QString QSGTextEdit::selectedText() const
{
Q_D(const QSGTextEdit);
return d->control->textCursor().selectedText();
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::activeFocusOnPress
+
+ Whether the TextEdit should gain active focus on a mouse press. By default this is
+ set to true.
+*/
bool QSGTextEdit::focusOnPress() const
{
Q_D(const QSGTextEdit);
@@ -565,6 +978,12 @@ void QSGTextEdit::setFocusOnPress(bool on)
emit activeFocusOnPressChanged(d->focusOnPress);
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::persistentSelection
+
+ Whether the TextEdit should keep the selection visible when it loses active focus to another
+ item in the scene. By default this is set to true;
+*/
bool QSGTextEdit::persistentSelection() const
{
Q_D(const QSGTextEdit);
@@ -580,6 +999,11 @@ void QSGTextEdit::setPersistentSelection(bool on)
emit persistentSelectionChanged(d->persistentSelection);
}
+/*
+ \qmlproperty real QtQuick2::TextEdit::textMargin
+
+ The margin, in pixels, around the text in the TextEdit.
+*/
qreal QSGTextEdit::textMargin() const
{
Q_D(const QSGTextEdit);
@@ -601,21 +1025,40 @@ void QSGTextEdit::geometryChanged(const QRectF &newGeometry,
{
if (newGeometry.width() != oldGeometry.width())
updateSize();
- QSGPaintedItem::geometryChanged(newGeometry, oldGeometry);
+ QSGImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
}
+/*!
+ Ensures any delayed caching or data loading the class
+ needs to performed is complete.
+*/
void QSGTextEdit::componentComplete()
{
Q_D(QSGTextEdit);
- QSGPaintedItem::componentComplete();
+ QSGImplicitSizeItem::componentComplete();
+
+ if (d->richText) {
+ d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
+ }
+
if (d->dirty) {
d->determineHorizontalAlignment();
d->updateDefaultTextOption();
updateSize();
d->dirty = false;
}
+
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::selectByMouse
+ Defaults to false.
+
+ If true, the user can use the mouse to select text in some
+ platform-specific way. Note that for some platforms this may
+ not be an appropriate interaction (eg. may conflict with how
+ the text needs to behave inside a Flickable.
+*/
bool QSGTextEdit::selectByMouse() const
{
Q_D(const QSGTextEdit);
@@ -636,6 +1079,18 @@ void QSGTextEdit::setSelectByMouse(bool on)
}
}
+/*!
+ \qmlproperty enum QtQuick2::TextEdit::mouseSelectionMode
+
+ Specifies how text should be selected using a mouse.
+
+ \list
+ \o TextEdit.SelectCharacters - The selection is updated with individual characters. (Default)
+ \o TextEdit.SelectWords - The selection is updated with whole words.
+ \endlist
+
+ This property only applies when \l selectByMouse is true.
+*/
QSGTextEdit::SelectionMode QSGTextEdit::mouseSelectionMode() const
{
Q_D(const QSGTextEdit);
@@ -652,6 +1107,14 @@ void QSGTextEdit::setMouseSelectionMode(SelectionMode mode)
}
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::readOnly
+
+ Whether the user can interact with the TextEdit item. If this
+ property is set to true the text cannot be edited by user interaction.
+
+ By default this property is false.
+*/
void QSGTextEdit::setReadOnly(bool r)
{
Q_D(QSGTextEdit);
@@ -677,18 +1140,32 @@ bool QSGTextEdit::isReadOnly() const
return !(d->control->textInteractionFlags() & Qt::TextEditable);
}
+/*!
+ Sets how the text edit should interact with user input to the given
+ \a flags.
+*/
void QSGTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
{
Q_D(QSGTextEdit);
d->control->setTextInteractionFlags(flags);
}
+/*!
+ Returns the flags specifying how the text edit should interact
+ with user input.
+*/
Qt::TextInteractionFlags QSGTextEdit::textInteractionFlags() const
{
Q_D(const QSGTextEdit);
return d->control->textInteractionFlags();
}
+/*!
+ \qmlproperty rectangle QtQuick2::TextEdit::cursorRectangle
+
+ The rectangle where the text cursor is rendered
+ within the text edit. Read-only.
+*/
QRect QSGTextEdit::cursorRectangle() const
{
Q_D(const QSGTextEdit);
@@ -702,7 +1179,7 @@ bool QSGTextEdit::event(QEvent *event)
d->control->processEvent(event, QPointF(0, -d->yoff));
return event->isAccepted();
}
- return QSGPaintedItem::event(event);
+ return QSGImplicitSizeItem::event(event);
}
/*!
@@ -714,7 +1191,7 @@ void QSGTextEdit::keyPressEvent(QKeyEvent *event)
Q_D(QSGTextEdit);
d->control->processEvent(event, QPointF(0, -d->yoff));
if (!event->isAccepted())
- QSGPaintedItem::keyPressEvent(event);
+ QSGImplicitSizeItem::keyPressEvent(event);
}
/*!
@@ -726,9 +1203,14 @@ void QSGTextEdit::keyReleaseEvent(QKeyEvent *event)
Q_D(QSGTextEdit);
d->control->processEvent(event, QPointF(0, -d->yoff));
if (!event->isAccepted())
- QSGPaintedItem::keyReleaseEvent(event);
+ QSGImplicitSizeItem::keyReleaseEvent(event);
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::deselect()
+
+ Removes active text selection.
+*/
void QSGTextEdit::deselect()
{
Q_D(QSGTextEdit);
@@ -737,12 +1219,22 @@ void QSGTextEdit::deselect()
d->control->setTextCursor(c);
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::selectAll()
+
+ Causes all text to be selected.
+*/
void QSGTextEdit::selectAll()
{
Q_D(QSGTextEdit);
d->control->selectAll();
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::selectWord()
+
+ Causes the word closest to the current cursor position to be selected.
+*/
void QSGTextEdit::selectWord()
{
Q_D(QSGTextEdit);
@@ -751,6 +1243,19 @@ void QSGTextEdit::selectWord()
d->control->setTextCursor(c);
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::select(int start, int end)
+
+ Causes the text from \a start to \a end to be selected.
+
+ If either start or end is out of range, the selection is not changed.
+
+ After calling this, selectionStart will become the lesser
+ and selectionEnd will become the greater (regardless of the order passed
+ to this method).
+
+ \sa selectionStart, selectionEnd
+*/
void QSGTextEdit::select(int start, int end)
{
Q_D(QSGTextEdit);
@@ -767,6 +1272,12 @@ void QSGTextEdit::select(int start, int end)
updateSelectionMarkers();
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::isRightToLeft(int start, int end)
+
+ Returns true if the natural reading direction of the editor text
+ found between positions \a start and \a end is right to left.
+*/
bool QSGTextEdit::isRightToLeft(int start, int end)
{
Q_D(QSGTextEdit);
@@ -779,18 +1290,33 @@ bool QSGTextEdit::isRightToLeft(int start, int end)
}
#ifndef QT_NO_CLIPBOARD
+/*!
+ \qmlmethod QtQuick2::TextEdit::cut()
+
+ Moves the currently selected text to the system clipboard.
+*/
void QSGTextEdit::cut()
{
Q_D(QSGTextEdit);
d->control->cut();
}
+/*!
+ \qmlmethod QtQuick2::TextEdit::copy()
+
+ Copies the currently selected text to the system clipboard.
+*/
void QSGTextEdit::copy()
{
Q_D(QSGTextEdit);
d->control->copy();
}
+/*!
+ \qmlmethod QtQuick2::TextEdit::paste()
+
+ Replaces the currently selected text by the contents of the system clipboard.
+*/
void QSGTextEdit::paste()
{
Q_D(QSGTextEdit);
@@ -821,7 +1347,7 @@ void QSGTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event)
}
d->control->processEvent(event, QPointF(0, -d->yoff));
if (!event->isAccepted())
- QSGPaintedItem::mousePressEvent(event);
+ QSGImplicitSizeItem::mousePressEvent(event);
}
/*!
@@ -844,7 +1370,7 @@ void QSGTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
d->clickCausedFocus = false;
if (!event->isAccepted())
- QSGPaintedItem::mouseReleaseEvent(event);
+ QSGImplicitSizeItem::mouseReleaseEvent(event);
}
/*!
@@ -856,7 +1382,7 @@ void QSGTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
Q_D(QSGTextEdit);
d->control->processEvent(event, QPointF(0, -d->yoff));
if (!event->isAccepted())
- QSGPaintedItem::mouseDoubleClickEvent(event);
+ QSGImplicitSizeItem::mouseDoubleClickEvent(event);
}
/*!
@@ -868,7 +1394,7 @@ void QSGTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
Q_D(QSGTextEdit);
d->control->processEvent(event, QPointF(0, -d->yoff));
if (!event->isAccepted())
- QSGPaintedItem::mouseMoveEvent(event);
+ QSGImplicitSizeItem::mouseMoveEvent(event);
}
/*!
@@ -903,49 +1429,162 @@ QVariant QSGTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
return d->control->inputMethodQuery(property);
}
-/*!
-Draws the contents of the text edit using the given \a painter within
-the given \a bounds.
-*/
-void QSGTextEdit::paint(QPainter *painter)
+void QSGTextEdit::updateImageCache(const QRectF &)
{
- // XXX todo
- QRect bounds(0, 0, width(), height());
Q_D(QSGTextEdit);
- painter->setRenderHint(QPainter::TextAntialiasing, true);
- painter->translate(0,d->yoff);
+ // Do we really need the image cache?
+ if (!d->richText || !d->isComplexRichText) {
+ if (!d->pixmapCache.isNull())
+ d->pixmapCache = QPixmap();
+ return;
+ }
+
+ if (width() != d->pixmapCache.width() || height() != d->pixmapCache.height())
+ d->pixmapCache = QPixmap(width(), height());
+
+ if (d->pixmapCache.isNull())
+ return;
+
+ // ### Use supplied rect, clear area and update only this part (for cursor updates)
+ QRectF bounds = QRectF(0, 0, width(), height());
+ d->pixmapCache.fill(Qt::transparent);
+ {
+ QPainter painter(&d->pixmapCache);
- d->control->drawContents(painter, bounds.translated(0,-d->yoff));
+ painter.setRenderHint(QPainter::TextAntialiasing);
+ painter.translate(0, d->yoff);
+
+ d->control->drawContents(&painter, bounds);
+ }
- painter->translate(0,-d->yoff);
}
-void QSGTextEdit::updateImgCache(const QRectF &rf)
+QSGNode *QSGTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
{
- Q_D(const QSGTextEdit);
- QRect r;
- if (!rf.isValid()) {
- r = QRect(0,0,INT_MAX,INT_MAX);
- } else {
- r = rf.toRect();
- if (r.height() > INT_MAX/2) {
- // Take care of overflow when translating "everything"
- r.setTop(r.y() + d->yoff);
- r.setBottom(INT_MAX/2);
+ Q_UNUSED(updatePaintNodeData);
+ Q_D(QSGTextEdit);
+
+ QSGNode *currentNode = oldNode;
+ if (d->richText && d->isComplexRichText) {
+ QSGImageNode *node = 0;
+ if (oldNode == 0 || d->nodeType != QSGTextEditPrivate::NodeIsTexture) {
+ delete oldNode;
+ node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
+ d->texture = new QSGPlainTexture();
+ d->nodeType = QSGTextEditPrivate::NodeIsTexture;
+ currentNode = node;
+ } else {
+ node = static_cast<QSGImageNode *>(oldNode);
+ }
+
+ qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->pixmapCache.toImage());
+ node->setTexture(0);
+ node->setTexture(d->texture);
+
+ node->setTargetRect(QRectF(0, 0, d->pixmapCache.width(), d->pixmapCache.height()));
+ node->setSourceRect(QRectF(0, 0, 1, 1));
+ node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ node->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
+ node->update();
+
+ } else if (oldNode == 0 || d->documentDirty) {
+ d->documentDirty = false;
+
+#if defined(Q_WS_MAC)
+ // Make sure document is relayouted in the paint node on Mac
+ // to avoid crashes due to the font engines created in the
+ // shaping process
+ d->document->markContentsDirty(0, d->document->characterCount());
+#endif
+
+ QSGTextNode *node = 0;
+ if (oldNode == 0 || d->nodeType != QSGTextEditPrivate::NodeIsText) {
+ delete oldNode;
+ node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
+ d->nodeType = QSGTextEditPrivate::NodeIsText;
+ currentNode = node;
} else {
- r = r.translated(0,d->yoff);
+ node = static_cast<QSGTextNode *>(oldNode);
}
+
+ node->deleteContent();
+ node->setMatrix(QMatrix4x4());
+
+ QRectF bounds = boundingRect();
+
+ QColor selectionColor = d->control->palette().color(QPalette::Highlight);
+ QColor selectedTextColor = d->control->palette().color(QPalette::HighlightedText);
+ node->addTextDocument(bounds.topLeft(), d->document, d->color, QSGText::Normal, QColor(),
+ selectionColor, selectedTextColor, selectionStart(),
+ selectionEnd());
+
+#if defined(Q_WS_MAC)
+ // We also need to make sure the document layout is redone when
+ // control is returned to the main thread, as all the font engines
+ // are now owned by the rendering thread
+ d->document->markContentsDirty(0, d->document->characterCount());
+#endif
}
- update(r);
+
+ if (d->nodeType == QSGTextEditPrivate::NodeIsText && d->cursorComponent == 0 && !isReadOnly()) {
+ QSGTextNode *node = static_cast<QSGTextNode *>(currentNode);
+
+ QColor color = (!d->cursorVisible || !d->control->cursorOn())
+ ? QColor(0, 0, 0, 0)
+ : d->color;
+
+ if (node->cursorNode() == 0) {
+ node->setCursor(cursorRectangle(), color);
+ } else {
+ node->cursorNode()->setRect(cursorRectangle());
+ node->cursorNode()->setColor(color);
+ }
+
+ }
+
+ return currentNode;
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::smooth
+
+ This property holds whether the text is smoothly scaled or transformed.
+
+ Smooth filtering gives better visual quality, but is slower. If
+ the item is displayed at its natural size, this property has no visual or
+ performance effect.
+
+ \note Generally scaling artifacts are only visible if the item is stationary on
+ the screen. A common pattern when animating an item is to disable smooth
+ filtering at the beginning of the animation and reenable it at the conclusion.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::canPaste
+
+ Returns true if the TextEdit is writable and the content of the clipboard is
+ suitable for pasting into the TextEdit.
+*/
bool QSGTextEdit::canPaste() const
{
Q_D(const QSGTextEdit);
return d->canPaste;
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::inputMethodComposing
+
+
+ This property holds whether the TextEdit has partial text input from an
+ input method.
+
+ While it is composing an input method may rely on mouse or key events from
+ the TextEdit to edit or commit the partial text. This property can be used
+ to determine when to disable events handlers that may interfere with the
+ correct operation of an input method.
+*/
bool QSGTextEdit::isInputMethodComposing() const
{
Q_D(const QSGTextEdit);
@@ -961,12 +1600,22 @@ void QSGTextEditPrivate::init()
q->setSmooth(smooth);
q->setAcceptedMouseButtons(Qt::LeftButton);
q->setFlag(QSGItem::ItemAcceptsInputMethod);
+ q->setFlag(QSGItem::ItemHasContents);
control = new QTextControl(q);
control->setIgnoreUnusedNavigationEvents(true);
control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
control->setDragEnabled(false);
+ // By default, QTextControl will issue both a updateCursorRequest() and an updateRequest()
+ // when the cursor needs to be repainted. We need the signals to be separate to be able to
+ // distinguish the cursor updates so that we can avoid updating the whole subtree when the
+ // cursor blinks.
+ if (!QObject::disconnect(control, SIGNAL(updateCursorRequest(QRectF)),
+ control, SIGNAL(updateRequest(QRectF)))) {
+ qWarning("QSGTextEditPrivate::init: Failed to disconnect updateCursorRequest and updateRequest");
+ }
+
// QTextControl follows the default text color
// defined by the platform, declarative text
// should be black by default
@@ -976,8 +1625,8 @@ void QSGTextEditPrivate::init()
control->setPalette(pal);
}
- QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateImgCache(QRectF)));
-
+ QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateDocument()));
+ QObject::connect(control, SIGNAL(updateCursorRequest()), q, SLOT(updateCursor()));
QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged()));
QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers()));
@@ -1056,7 +1705,7 @@ void QSGTextEdit::updateSelectionMarkers()
QRectF QSGTextEdit::boundingRect() const
{
Q_D(const QSGTextEdit);
- QRectF r = QSGPaintedItem::boundingRect();
+ QRectF r = QSGImplicitSizeItem::boundingRect();
int cursorWidth = 1;
if(d->cursor)
cursorWidth = d->cursor->width();
@@ -1121,7 +1770,7 @@ void QSGTextEdit::updateSize()
} else {
nyoff = 0;
}
- if (nyoff != d->yoff)
+ if (nyoff != d->yoff)
d->yoff = nyoff;
setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
@@ -1138,12 +1787,31 @@ void QSGTextEdit::updateSize()
setImplicitHeight(newHeight);
d->paintedSize = QSize(newWidth, newHeight);
- setContentsSize(d->paintedSize);
emit paintedSizeChanged();
} else {
d->dirty = true;
}
- update();
+ updateDocument();
+}
+
+void QSGTextEdit::updateDocument()
+{
+ Q_D(QSGTextEdit);
+ d->documentDirty = true;
+
+ if (isComponentComplete()) {
+ updateImageCache();
+ update();
+ }
+}
+
+void QSGTextEdit::updateCursor()
+{
+ Q_D(QSGTextEdit);
+ if (isComponentComplete()) {
+ updateImageCache(d->control->cursorRect());
+ update();
+ }
}
void QSGTextEdit::updateTotalLines()
@@ -1184,12 +1852,59 @@ void QSGTextEditPrivate::updateDefaultTextOption()
QTextOption::WrapMode oldWrapMode = opt.wrapMode();
opt.setWrapMode(QTextOption::WrapMode(wrapMode));
- if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment())
+ bool oldUseDesignMetrics = opt.useDesignMetrics();
+ bool useDesignMetrics = !qmlDisableDistanceField();
+ opt.setUseDesignMetrics(useDesignMetrics);
+
+ if (oldWrapMode == opt.wrapMode()
+ && oldAlignment == opt.alignment()
+ && oldUseDesignMetrics == useDesignMetrics) {
return;
+ }
document->setDefaultTextOption(opt);
}
+
+/*!
+ \qmlmethod void QtQuick2::TextEdit::openSoftwareInputPanel()
+
+ Opens software input panels like virtual keyboards for typing, useful for
+ customizing when you want the input keyboard to be shown and hidden in
+ your application.
+
+ By default the opening of input panels follows the platform style. On Symbian^1 and
+ Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
+ the panels are automatically opened when TextEdit element gains active focus. Input panels are
+ always closed if no editor has active focus.
+
+ You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
+ and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
+ the behavior you want.
+
+ Only relevant on platforms, which provide virtual keyboards.
+
+ \code
+ import QtQuick 1.0
+ TextEdit {
+ id: textEdit
+ text: "Hello world!"
+ activeFocusOnPress: false
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (!textEdit.activeFocus) {
+ textEdit.forceActiveFocus();
+ textEdit.openSoftwareInputPanel();
+ } else {
+ textEdit.focus = false;
+ }
+ }
+ onPressAndHold: textEdit.closeSoftwareInputPanel();
+ }
+ }
+ \endcode
+*/
void QSGTextEdit::openSoftwareInputPanel()
{
if (qApp) {
@@ -1200,6 +1915,45 @@ void QSGTextEdit::openSoftwareInputPanel()
}
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::closeSoftwareInputPanel()
+
+ Closes a software input panel like a virtual keyboard shown on the screen, useful
+ for customizing when you want the input keyboard to be shown and hidden in
+ your application.
+
+ By default the opening of input panels follows the platform style. On Symbian^1 and
+ Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
+ the panels are automatically opened when TextEdit element gains active focus. Input panels are
+ always closed if no editor has active focus.
+
+ You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
+ and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
+ the behavior you want.
+
+ Only relevant on platforms, which provide virtual keyboards.
+
+ \code
+ import QtQuick 1.0
+ TextEdit {
+ id: textEdit
+ text: "Hello world!"
+ activeFocusOnPress: false
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (!textEdit.activeFocus) {
+ textEdit.forceActiveFocus();
+ textEdit.openSoftwareInputPanel();
+ } else {
+ textEdit.focus = false;
+ }
+ }
+ onPressAndHold: textEdit.closeSoftwareInputPanel();
+ }
+ }
+ \endcode
+*/
void QSGTextEdit::closeSoftwareInputPanel()
{
if (qApp) {
@@ -1218,7 +1972,7 @@ void QSGTextEdit::focusInEvent(QFocusEvent *event)
openSoftwareInputPanel();
}
}
- QSGPaintedItem::focusInEvent(event);
+ QSGImplicitSizeItem::focusInEvent(event);
}
void QSGTextEdit::q_canPasteChanged()