1145 lines
33 KiB
C++
1145 lines
33 KiB
C++
// Copyright (C) 2017 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
|
|
#include "qquicktextarea_p.h"
|
|
#include "qquicktextarea_p_p.h"
|
|
#include "qquickcontrol_p.h"
|
|
#include "qquickcontrol_p_p.h"
|
|
#include "qquickscrollview_p.h"
|
|
#include "qquickdeferredexecute_p_p.h"
|
|
|
|
#include <QtQml/qqmlinfo.h>
|
|
#include <QtQuick/private/qquickitem_p.h>
|
|
#include <QtQuick/private/qquickclipnode_p.h>
|
|
#include <QtQuick/private/qquickflickable_p.h>
|
|
|
|
#if QT_CONFIG(accessibility)
|
|
#include <QtQuick/private/qquickaccessibleattached_p.h>
|
|
#endif
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
/*!
|
|
\qmltype TextArea
|
|
\inherits TextEdit
|
|
//! \instantiates QQuickTextArea
|
|
\inqmlmodule QtQuick.Controls
|
|
\since 5.7
|
|
\ingroup qtquickcontrols2-input
|
|
\brief Multi-line text input area.
|
|
|
|
TextArea is a multi-line text editor. TextArea extends TextEdit with
|
|
a \l {placeholderText}{placeholder text} functionality, and adds decoration.
|
|
|
|
\image qtquickcontrols2-textarea.png
|
|
|
|
\code
|
|
TextArea {
|
|
placeholderText: qsTr("Enter description")
|
|
}
|
|
\endcode
|
|
|
|
TextArea is not scrollable by itself. Especially on screen-size constrained
|
|
platforms, it is often preferable to make entire application pages scrollable.
|
|
On such a scrollable page, a non-scrollable TextArea might behave better than
|
|
nested scrollable controls. Notice, however, that in such a scenario, the background
|
|
decoration of the TextArea scrolls together with the rest of the scrollable
|
|
content.
|
|
|
|
\section2 Scrollable TextArea
|
|
|
|
If you want to make a TextArea scrollable, for example, when it covers
|
|
an entire application page, it can be placed inside a \l ScrollView.
|
|
|
|
\image qtquickcontrols2-textarea-scrollable.png
|
|
|
|
\snippet qtquickcontrols2-textarea-scrollable.qml 1
|
|
|
|
A TextArea that is placed inside a \l ScrollView does the following:
|
|
|
|
\list
|
|
\li Sets the content size automatically
|
|
\li Ensures that the background decoration stays in place
|
|
\li Clips the content
|
|
\endlist
|
|
|
|
\section2 Tab Focus
|
|
|
|
By default, pressing the tab key while TextArea has
|
|
\l {Item::activeFocus}{active focus} results in a tab character being input
|
|
into the control itself. To make tab pass active focus onto another item,
|
|
use the attached \l KeyNavigation properties:
|
|
|
|
\code
|
|
TextField {
|
|
id: textField
|
|
}
|
|
|
|
TextArea {
|
|
KeyNavigation.priority: KeyNavigation.BeforeItem
|
|
KeyNavigation.tab: textField
|
|
}
|
|
\endcode
|
|
|
|
\sa TextField, {Customizing TextArea}, {Input Controls}
|
|
*/
|
|
|
|
/*!
|
|
\qmlsignal QtQuick.Controls::TextArea::pressAndHold(MouseEvent event)
|
|
|
|
This signal is emitted when there is a long press (the delay depends on the platform plugin).
|
|
The \a event parameter provides information about the press, including the x and y
|
|
coordinates of the press, and which button is pressed.
|
|
|
|
\sa pressed, released
|
|
*/
|
|
|
|
/*!
|
|
\qmlsignal QtQuick.Controls::TextArea::pressed(MouseEvent event)
|
|
\since QtQuick.Controls 2.1 (Qt 5.8)
|
|
|
|
This signal is emitted when the text area is pressed by the user.
|
|
The \a event parameter provides information about the press,
|
|
including the x and y coordinates of the press, and which button is pressed.
|
|
|
|
\sa released, pressAndHold
|
|
*/
|
|
|
|
/*!
|
|
\qmlsignal QtQuick.Controls::TextArea::released(MouseEvent event)
|
|
\since QtQuick.Controls 2.1 (Qt 5.8)
|
|
|
|
This signal is emitted when the text area is released by the user.
|
|
The \a event parameter provides information about the release,
|
|
including the x and y coordinates of the press, and which button
|
|
is pressed.
|
|
|
|
\sa pressed, pressAndHold
|
|
*/
|
|
|
|
QQuickTextAreaPrivate::QQuickTextAreaPrivate()
|
|
{
|
|
#if QT_CONFIG(accessibility)
|
|
QAccessible::installActivationObserver(this);
|
|
#endif
|
|
}
|
|
|
|
QQuickTextAreaPrivate::~QQuickTextAreaPrivate()
|
|
{
|
|
#if QT_CONFIG(accessibility)
|
|
QAccessible::removeActivationObserver(this);
|
|
#endif
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::setTopInset(qreal value, bool reset)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
const QMarginsF oldInset = getInset();
|
|
extra.value().topInset = value;
|
|
extra.value().hasTopInset = !reset;
|
|
if (!qFuzzyCompare(oldInset.top(), value)) {
|
|
emit q->topInsetChanged();
|
|
q->insetChange(getInset(), oldInset);
|
|
}
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::setLeftInset(qreal value, bool reset)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
const QMarginsF oldInset = getInset();
|
|
extra.value().leftInset = value;
|
|
extra.value().hasLeftInset = !reset;
|
|
if (!qFuzzyCompare(oldInset.left(), value)) {
|
|
emit q->leftInsetChanged();
|
|
q->insetChange(getInset(), oldInset);
|
|
}
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::setRightInset(qreal value, bool reset)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
const QMarginsF oldInset = getInset();
|
|
extra.value().rightInset = value;
|
|
extra.value().hasRightInset = !reset;
|
|
if (!qFuzzyCompare(oldInset.right(), value)) {
|
|
emit q->rightInsetChanged();
|
|
q->insetChange(getInset(), oldInset);
|
|
}
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::setBottomInset(qreal value, bool reset)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
const QMarginsF oldInset = getInset();
|
|
extra.value().bottomInset = value;
|
|
extra.value().hasBottomInset = !reset;
|
|
if (!qFuzzyCompare(oldInset.bottom(), value)) {
|
|
emit q->bottomInsetChanged();
|
|
q->insetChange(getInset(), oldInset);
|
|
}
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::resizeBackground()
|
|
{
|
|
if (!background)
|
|
return;
|
|
|
|
resizingBackground = true;
|
|
|
|
// When using the attached property TextArea.flickable, we reparent the background out
|
|
// of TextArea and into the Flickable since we don't want the background to move while
|
|
// flicking. This means that the size of the background should also follow the size of
|
|
// the Flickable rather than the size of the TextArea.
|
|
const auto flickable = qobject_cast<QQuickFlickable *>(background->parentItem());
|
|
|
|
QQuickItemPrivate *p = QQuickItemPrivate::get(background);
|
|
if (((!p->widthValid() || !extra.isAllocated() || !extra->hasBackgroundWidth) && qFuzzyIsNull(background->x()))
|
|
|| (extra.isAllocated() && (extra->hasLeftInset || extra->hasRightInset))) {
|
|
const qreal bgWidth = flickable ? flickable->width() : width;
|
|
background->setX(getLeftInset());
|
|
background->setWidth(bgWidth - getLeftInset() - getRightInset());
|
|
}
|
|
|
|
if (((!p->heightValid() || !extra.isAllocated() || !extra->hasBackgroundHeight) && qFuzzyIsNull(background->y()))
|
|
|| (extra.isAllocated() && (extra->hasTopInset || extra->hasBottomInset))) {
|
|
const qreal bgHeight = flickable ? flickable->height() : height;
|
|
background->setY(getTopInset());
|
|
background->setHeight(bgHeight - getTopInset() - getBottomInset());
|
|
}
|
|
|
|
resizingBackground = false;
|
|
}
|
|
|
|
/*!
|
|
\internal
|
|
|
|
Determine which font is implicitly imposed on this control by its ancestors
|
|
and QGuiApplication::font, resolve this against its own font (attributes from
|
|
the implicit font are copied over). Then propagate this font to this
|
|
control's children.
|
|
*/
|
|
void QQuickTextAreaPrivate::resolveFont()
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
inheritFont(QQuickControlPrivate::parentFont(q));
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::inheritFont(const QFont &font)
|
|
{
|
|
QFont parentFont = extra.isAllocated() ? extra->requestedFont.resolve(font) : font;
|
|
parentFont.setResolveMask(extra.isAllocated() ? extra->requestedFont.resolveMask() | font.resolveMask() : font.resolveMask());
|
|
|
|
const QFont defaultFont = QQuickTheme::font(QQuickTheme::TextArea);
|
|
QFont resolvedFont = parentFont.resolve(defaultFont);
|
|
|
|
setFont_helper(resolvedFont);
|
|
}
|
|
|
|
/*!
|
|
\internal
|
|
|
|
Assign \a font to this control, and propagate it to all children.
|
|
*/
|
|
void QQuickTextAreaPrivate::updateFont(const QFont &font)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
QFont oldFont = sourceFont;
|
|
q->QQuickTextEdit::setFont(font);
|
|
|
|
QQuickControlPrivate::updateFontRecur(q, font);
|
|
|
|
if (oldFont != font)
|
|
emit q->fontChanged();
|
|
}
|
|
|
|
#if QT_CONFIG(quicktemplates2_hover)
|
|
void QQuickTextAreaPrivate::updateHoverEnabled(bool enabled, bool xplicit)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
if (!xplicit && explicitHoverEnabled)
|
|
return;
|
|
|
|
bool wasEnabled = q->isHoverEnabled();
|
|
explicitHoverEnabled = xplicit;
|
|
if (wasEnabled != enabled) {
|
|
q->setAcceptHoverEvents(enabled);
|
|
QQuickControlPrivate::updateHoverEnabledRecur(q, enabled);
|
|
emit q->hoverEnabledChanged();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void QQuickTextAreaPrivate::attachFlickable(QQuickFlickable *item)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
flickable = item;
|
|
q->setParentItem(flickable->contentItem());
|
|
|
|
if (background)
|
|
background->setParentItem(flickable);
|
|
|
|
QObjectPrivate::connect(q, &QQuickTextArea::contentSizeChanged, this, &QQuickTextAreaPrivate::resizeFlickableContent);
|
|
QObjectPrivate::connect(q, &QQuickTextEdit::cursorRectangleChanged, this, &QQuickTextAreaPrivate::ensureCursorVisible);
|
|
|
|
QObject::connect(flickable, &QQuickFlickable::contentXChanged, q, &QQuickItem::update);
|
|
QObject::connect(flickable, &QQuickFlickable::contentYChanged, q, &QQuickItem::update);
|
|
|
|
QQuickItemPrivate::get(flickable)->updateOrAddGeometryChangeListener(this, QQuickGeometryChange::Size);
|
|
QQuickItemPrivate::get(flickable)->addItemChangeListener(this, QQuickItemPrivate::Destroyed);
|
|
QObjectPrivate::connect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickTextAreaPrivate::resizeFlickableControl);
|
|
QObjectPrivate::connect(flickable, &QQuickFlickable::contentHeightChanged, this, &QQuickTextAreaPrivate::resizeFlickableControl);
|
|
|
|
resizeFlickableControl();
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::detachFlickable()
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
q->setParentItem(nullptr);
|
|
if (background && background->parentItem() == flickable)
|
|
background->setParentItem(q);
|
|
|
|
QObjectPrivate::disconnect(q, &QQuickTextArea::contentSizeChanged, this, &QQuickTextAreaPrivate::resizeFlickableContent);
|
|
QObjectPrivate::disconnect(q, &QQuickTextEdit::cursorRectangleChanged, this, &QQuickTextAreaPrivate::ensureCursorVisible);
|
|
|
|
QObject::disconnect(flickable, &QQuickFlickable::contentXChanged, q, &QQuickItem::update);
|
|
QObject::disconnect(flickable, &QQuickFlickable::contentYChanged, q, &QQuickItem::update);
|
|
|
|
QQuickItemPrivate::get(flickable)->updateOrRemoveGeometryChangeListener(this, QQuickGeometryChange::Nothing);
|
|
QQuickItemPrivate::get(flickable)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed);
|
|
QObjectPrivate::disconnect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickTextAreaPrivate::resizeFlickableControl);
|
|
QObjectPrivate::disconnect(flickable, &QQuickFlickable::contentHeightChanged, this, &QQuickTextAreaPrivate::resizeFlickableControl);
|
|
|
|
flickable = nullptr;
|
|
|
|
resizeBackground();
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::ensureCursorVisible()
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
if (!flickable)
|
|
return;
|
|
|
|
const qreal cx = flickable->contentX();
|
|
const qreal cy = flickable->contentY();
|
|
const qreal w = flickable->width();
|
|
const qreal h = flickable->height();
|
|
|
|
const qreal tp = q->topPadding();
|
|
const qreal lp = q->leftPadding();
|
|
const QRectF cr = q->cursorRectangle();
|
|
|
|
if (cr.left() <= cx + lp) {
|
|
flickable->setContentX(cr.left() - lp);
|
|
} else {
|
|
// calculate the rectangle of the next character and ensure that
|
|
// it's visible if it's on the same line with the cursor
|
|
const qreal rp = q->rightPadding();
|
|
const QRectF nr = q->cursorPosition() < q->length() ? q->positionToRectangle(q->cursorPosition() + 1) : QRectF();
|
|
if (qFuzzyCompare(nr.y(), cr.y()) && nr.right() >= cx + lp + w - rp)
|
|
flickable->setContentX(nr.right() - w + rp);
|
|
else if (cr.right() >= cx + lp + w - rp)
|
|
flickable->setContentX(cr.right() - w + rp);
|
|
}
|
|
|
|
if (cr.top() <= cy + tp) {
|
|
flickable->setContentY(cr.top() - tp);
|
|
} else {
|
|
const qreal bp = q->bottomPadding();
|
|
if (cr.bottom() >= cy + tp + h - bp && cr.bottom() <= flickable->contentHeight())
|
|
flickable->setContentY(cr.bottom() - h + bp);
|
|
}
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::resizeFlickableControl()
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
if (!flickable)
|
|
return;
|
|
|
|
const qreal w = wrapMode == QQuickTextArea::NoWrap ? qMax(flickable->width(), flickable->contentWidth()) : flickable->width();
|
|
const qreal h = qMax(flickable->height(), flickable->contentHeight());
|
|
q->setSize(QSizeF(w, h));
|
|
|
|
resizeBackground();
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::resizeFlickableContent()
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
if (!flickable)
|
|
return;
|
|
|
|
flickable->setContentWidth(q->contentWidth() + q->leftPadding() + q->rightPadding());
|
|
flickable->setContentHeight(q->contentHeight() + q->topPadding() + q->bottomPadding());
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
|
|
{
|
|
Q_UNUSED(diff);
|
|
if (!resizingBackground && item == background) {
|
|
QQuickItemPrivate *p = QQuickItemPrivate::get(item);
|
|
// Only set hasBackgroundWidth/Height if it was a width/height change,
|
|
// otherwise we're prevented from setting a width/height in the future.
|
|
if (change.widthChange())
|
|
extra.value().hasBackgroundWidth = p->widthValid();
|
|
if (change.heightChange())
|
|
extra.value().hasBackgroundHeight = p->heightValid();
|
|
}
|
|
|
|
if (flickable)
|
|
resizeFlickableControl();
|
|
else
|
|
resizeBackground();
|
|
}
|
|
|
|
qreal QQuickTextAreaPrivate::getImplicitWidth() const
|
|
{
|
|
return QQuickItemPrivate::getImplicitWidth();
|
|
}
|
|
|
|
qreal QQuickTextAreaPrivate::getImplicitHeight() const
|
|
{
|
|
return QQuickItemPrivate::getImplicitHeight();
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::implicitWidthChanged()
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
QQuickItemPrivate::implicitWidthChanged();
|
|
emit q->implicitWidthChanged3();
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::implicitHeightChanged()
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
QQuickItemPrivate::implicitHeightChanged();
|
|
emit q->implicitHeightChanged3();
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::readOnlyChanged(bool isReadOnly)
|
|
{
|
|
Q_UNUSED(isReadOnly);
|
|
#if QT_CONFIG(accessibility)
|
|
if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(q_func()))
|
|
accessibleAttached->set_readOnly(isReadOnly);
|
|
#endif
|
|
}
|
|
|
|
#if QT_CONFIG(accessibility)
|
|
void QQuickTextAreaPrivate::accessibilityActiveChanged(bool active)
|
|
{
|
|
if (!active)
|
|
return;
|
|
|
|
Q_Q(QQuickTextArea);
|
|
QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
|
|
Q_ASSERT(accessibleAttached);
|
|
accessibleAttached->setRole(accessibleRole());
|
|
accessibleAttached->set_readOnly(q->isReadOnly());
|
|
accessibleAttached->setDescription(placeholder);
|
|
}
|
|
|
|
QAccessible::Role QQuickTextAreaPrivate::accessibleRole() const
|
|
{
|
|
return QAccessible::EditableText;
|
|
}
|
|
#endif
|
|
|
|
static inline QString backgroundName() { return QStringLiteral("background"); }
|
|
|
|
void QQuickTextAreaPrivate::cancelBackground()
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
quickCancelDeferred(q, backgroundName());
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::executeBackground(bool complete)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
if (background.wasExecuted())
|
|
return;
|
|
|
|
if (!background || complete)
|
|
quickBeginDeferred(q, backgroundName(), background);
|
|
if (complete)
|
|
quickCompleteDeferred(q, backgroundName(), background);
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::itemImplicitWidthChanged(QQuickItem *item)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
if (item == background)
|
|
emit q->implicitBackgroundWidthChanged();
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::itemImplicitHeightChanged(QQuickItem *item)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
if (item == background)
|
|
emit q->implicitBackgroundHeightChanged();
|
|
}
|
|
|
|
void QQuickTextAreaPrivate::itemDestroyed(QQuickItem *item)
|
|
{
|
|
Q_Q(QQuickTextArea);
|
|
if (item == background) {
|
|
background = nullptr;
|
|
emit q->implicitBackgroundWidthChanged();
|
|
emit q->implicitBackgroundHeightChanged();
|
|
} else if (item == flickable) {
|
|
detachFlickable();
|
|
}
|
|
}
|
|
|
|
QPalette QQuickTextAreaPrivate::defaultPalette() const
|
|
{
|
|
return QQuickTheme::palette(QQuickTheme::TextArea);
|
|
}
|
|
|
|
QQuickTextArea::QQuickTextArea(QQuickItem *parent)
|
|
: QQuickTextEdit(*(new QQuickTextAreaPrivate), parent)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
setActiveFocusOnTab(true);
|
|
setAcceptedMouseButtons(Qt::AllButtons);
|
|
d->setImplicitResizeEnabled(false);
|
|
d->pressHandler.control = this;
|
|
|
|
QObjectPrivate::connect(this, &QQuickTextEdit::readOnlyChanged,
|
|
d, &QQuickTextAreaPrivate::readOnlyChanged);
|
|
}
|
|
|
|
QQuickTextArea::~QQuickTextArea()
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
if (d->flickable)
|
|
d->detachFlickable();
|
|
QQuickControlPrivate::removeImplicitSizeListener(d->background, d, QQuickControlPrivate::ImplicitSizeChanges | QQuickItemPrivate::Geometry);
|
|
}
|
|
|
|
QQuickTextAreaAttached *QQuickTextArea::qmlAttachedProperties(QObject *object)
|
|
{
|
|
return new QQuickTextAreaAttached(object);
|
|
}
|
|
|
|
QFont QQuickTextArea::font() const
|
|
{
|
|
return QQuickTextEdit::font();
|
|
}
|
|
|
|
void QQuickTextArea::setFont(const QFont &font)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
if (d->extra.value().requestedFont.resolveMask() == font.resolveMask() && d->extra.value().requestedFont == font)
|
|
return;
|
|
|
|
d->extra.value().requestedFont = font;
|
|
d->resolveFont();
|
|
}
|
|
|
|
/*!
|
|
\qmlproperty Item QtQuick.Controls::TextArea::background
|
|
|
|
This property holds the background item.
|
|
|
|
\input qquickcontrol-background.qdocinc notes
|
|
|
|
\sa {Customizing TextArea}
|
|
*/
|
|
QQuickItem *QQuickTextArea::background() const
|
|
{
|
|
QQuickTextAreaPrivate *d = const_cast<QQuickTextAreaPrivate *>(d_func());
|
|
if (!d->background)
|
|
d->executeBackground();
|
|
return d->background;
|
|
}
|
|
|
|
void QQuickTextArea::setBackground(QQuickItem *background)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
if (d->background == background)
|
|
return;
|
|
|
|
if (!d->background.isExecuting())
|
|
d->cancelBackground();
|
|
|
|
const qreal oldImplicitBackgroundWidth = implicitBackgroundWidth();
|
|
const qreal oldImplicitBackgroundHeight = implicitBackgroundHeight();
|
|
|
|
if (d->extra.isAllocated()) {
|
|
d->extra.value().hasBackgroundWidth = false;
|
|
d->extra.value().hasBackgroundHeight = false;
|
|
}
|
|
|
|
QQuickControlPrivate::removeImplicitSizeListener(d->background, d, QQuickControlPrivate::ImplicitSizeChanges | QQuickItemPrivate::Geometry);
|
|
QQuickControlPrivate::hideOldItem(d->background);
|
|
d->background = background;
|
|
|
|
if (background) {
|
|
QQuickItemPrivate *p = QQuickItemPrivate::get(background);
|
|
if (p->widthValid() || p->heightValid()) {
|
|
d->extra.value().hasBackgroundWidth = p->widthValid();
|
|
d->extra.value().hasBackgroundHeight = p->heightValid();
|
|
}
|
|
if (d->flickable)
|
|
background->setParentItem(d->flickable);
|
|
else
|
|
background->setParentItem(this);
|
|
if (qFuzzyIsNull(background->z()))
|
|
background->setZ(-1);
|
|
if (isComponentComplete())
|
|
d->resizeBackground();
|
|
QQuickControlPrivate::addImplicitSizeListener(background, d, QQuickControlPrivate::ImplicitSizeChanges | QQuickItemPrivate::Geometry);
|
|
}
|
|
|
|
if (!qFuzzyCompare(oldImplicitBackgroundWidth, implicitBackgroundWidth()))
|
|
emit implicitBackgroundWidthChanged();
|
|
if (!qFuzzyCompare(oldImplicitBackgroundHeight, implicitBackgroundHeight()))
|
|
emit implicitBackgroundHeightChanged();
|
|
if (!d->background.isExecuting())
|
|
emit backgroundChanged();
|
|
}
|
|
|
|
/*!
|
|
\qmlproperty string QtQuick.Controls::TextArea::placeholderText
|
|
|
|
This property holds the short hint that is displayed in the text area before
|
|
the user enters a value.
|
|
*/
|
|
QString QQuickTextArea::placeholderText() const
|
|
{
|
|
Q_D(const QQuickTextArea);
|
|
return d->placeholder;
|
|
}
|
|
|
|
void QQuickTextArea::setPlaceholderText(const QString &text)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
if (d->placeholder == text)
|
|
return;
|
|
|
|
d->placeholder = text;
|
|
#if QT_CONFIG(accessibility)
|
|
if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(this))
|
|
accessibleAttached->setDescription(text);
|
|
#endif
|
|
emit placeholderTextChanged();
|
|
}
|
|
|
|
/*!
|
|
\qmlproperty color QtQuick.Controls::TextArea::placeholderTextColor
|
|
\since QtQuick.Controls 2.5 (Qt 5.12)
|
|
|
|
This property holds the color of placeholderText.
|
|
|
|
\sa placeholderText
|
|
*/
|
|
QColor QQuickTextArea::placeholderTextColor() const
|
|
{
|
|
Q_D(const QQuickTextArea);
|
|
return d->placeholderColor;
|
|
}
|
|
|
|
void QQuickTextArea::setPlaceholderTextColor(const QColor &color)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
if (d->placeholderColor == color)
|
|
return;
|
|
|
|
d->placeholderColor = color;
|
|
emit placeholderTextColorChanged();
|
|
}
|
|
|
|
/*!
|
|
\qmlproperty enumeration QtQuick.Controls::TextArea::focusReason
|
|
|
|
\include qquickcontrol-focusreason.qdocinc
|
|
*/
|
|
Qt::FocusReason QQuickTextArea::focusReason() const
|
|
{
|
|
Q_D(const QQuickTextArea);
|
|
return d->focusReason;
|
|
}
|
|
|
|
void QQuickTextArea::setFocusReason(Qt::FocusReason reason)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
if (d->focusReason == reason)
|
|
return;
|
|
|
|
d->focusReason = reason;
|
|
emit focusReasonChanged();
|
|
}
|
|
|
|
bool QQuickTextArea::contains(const QPointF &point) const
|
|
{
|
|
Q_D(const QQuickTextArea);
|
|
if (d->flickable && !d->flickable->contains(d->flickable->mapFromItem(this, point)))
|
|
return false;
|
|
return QQuickTextEdit::contains(point);
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.1 (Qt 5.8)
|
|
\qmlproperty bool QtQuick.Controls::TextArea::hovered
|
|
\readonly
|
|
|
|
This property holds whether the text area is hovered.
|
|
|
|
\sa hoverEnabled
|
|
*/
|
|
bool QQuickTextArea::isHovered() const
|
|
{
|
|
#if QT_CONFIG(quicktemplates2_hover)
|
|
Q_D(const QQuickTextArea);
|
|
return d->hovered;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void QQuickTextArea::setHovered(bool hovered)
|
|
{
|
|
#if QT_CONFIG(quicktemplates2_hover)
|
|
Q_D(QQuickTextArea);
|
|
if (hovered == d->hovered)
|
|
return;
|
|
|
|
d->hovered = hovered;
|
|
emit hoveredChanged();
|
|
#else
|
|
Q_UNUSED(hovered);
|
|
#endif
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.1 (Qt 5.8)
|
|
\qmlproperty bool QtQuick.Controls::TextArea::hoverEnabled
|
|
|
|
This property determines whether the text area accepts hover events. The default value is \c true.
|
|
|
|
\sa hovered
|
|
*/
|
|
bool QQuickTextArea::isHoverEnabled() const
|
|
{
|
|
#if QT_CONFIG(quicktemplates2_hover)
|
|
Q_D(const QQuickTextArea);
|
|
return d->hoverEnabled;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void QQuickTextArea::setHoverEnabled(bool enabled)
|
|
{
|
|
#if QT_CONFIG(quicktemplates2_hover)
|
|
Q_D(QQuickTextArea);
|
|
if (d->explicitHoverEnabled && enabled == d->hoverEnabled)
|
|
return;
|
|
|
|
d->updateHoverEnabled(enabled, true); // explicit=true
|
|
#else
|
|
Q_UNUSED(enabled);
|
|
#endif
|
|
}
|
|
|
|
void QQuickTextArea::resetHoverEnabled()
|
|
{
|
|
#if QT_CONFIG(quicktemplates2_hover)
|
|
Q_D(QQuickTextArea);
|
|
if (!d->explicitHoverEnabled)
|
|
return;
|
|
|
|
d->explicitHoverEnabled = false;
|
|
d->updateHoverEnabled(QQuickControlPrivate::calcHoverEnabled(d->parentItem), false); // explicit=false
|
|
#endif
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.5 (Qt 5.12)
|
|
\qmlproperty real QtQuick.Controls::TextArea::implicitBackgroundWidth
|
|
\readonly
|
|
|
|
This property holds the implicit background width.
|
|
|
|
The value is equal to \c {background ? background.implicitWidth : 0}.
|
|
|
|
\sa implicitBackgroundHeight
|
|
*/
|
|
qreal QQuickTextArea::implicitBackgroundWidth() const
|
|
{
|
|
Q_D(const QQuickTextArea);
|
|
if (!d->background)
|
|
return 0;
|
|
return d->background->implicitWidth();
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.5 (Qt 5.12)
|
|
\qmlproperty real QtQuick.Controls::TextArea::implicitBackgroundHeight
|
|
\readonly
|
|
|
|
This property holds the implicit background height.
|
|
|
|
The value is equal to \c {background ? background.implicitHeight : 0}.
|
|
|
|
\sa implicitBackgroundWidth
|
|
*/
|
|
qreal QQuickTextArea::implicitBackgroundHeight() const
|
|
{
|
|
Q_D(const QQuickTextArea);
|
|
if (!d->background)
|
|
return 0;
|
|
return d->background->implicitHeight();
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.5 (Qt 5.12)
|
|
\qmlproperty real QtQuick.Controls::TextArea::topInset
|
|
|
|
This property holds the top inset for the background.
|
|
|
|
\sa {Control Layout}, bottomInset
|
|
*/
|
|
qreal QQuickTextArea::topInset() const
|
|
{
|
|
Q_D(const QQuickTextArea);
|
|
return d->getTopInset();
|
|
}
|
|
|
|
void QQuickTextArea::setTopInset(qreal inset)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->setTopInset(inset);
|
|
}
|
|
|
|
void QQuickTextArea::resetTopInset()
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->setTopInset(0, true);
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.5 (Qt 5.12)
|
|
\qmlproperty real QtQuick.Controls::TextArea::leftInset
|
|
|
|
This property holds the left inset for the background.
|
|
|
|
\sa {Control Layout}, rightInset
|
|
*/
|
|
qreal QQuickTextArea::leftInset() const
|
|
{
|
|
Q_D(const QQuickTextArea);
|
|
return d->getLeftInset();
|
|
}
|
|
|
|
void QQuickTextArea::setLeftInset(qreal inset)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->setLeftInset(inset);
|
|
}
|
|
|
|
void QQuickTextArea::resetLeftInset()
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->setLeftInset(0, true);
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.5 (Qt 5.12)
|
|
\qmlproperty real QtQuick.Controls::TextArea::rightInset
|
|
|
|
This property holds the right inset for the background.
|
|
|
|
\sa {Control Layout}, leftInset
|
|
*/
|
|
qreal QQuickTextArea::rightInset() const
|
|
{
|
|
Q_D(const QQuickTextArea);
|
|
return d->getRightInset();
|
|
}
|
|
|
|
void QQuickTextArea::setRightInset(qreal inset)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->setRightInset(inset);
|
|
}
|
|
|
|
void QQuickTextArea::resetRightInset()
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->setRightInset(0, true);
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.5 (Qt 5.12)
|
|
\qmlproperty real QtQuick.Controls::TextArea::bottomInset
|
|
|
|
This property holds the bottom inset for the background.
|
|
|
|
\sa {Control Layout}, topInset
|
|
*/
|
|
qreal QQuickTextArea::bottomInset() const
|
|
{
|
|
Q_D(const QQuickTextArea);
|
|
return d->getBottomInset();
|
|
}
|
|
|
|
void QQuickTextArea::setBottomInset(qreal inset)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->setBottomInset(inset);
|
|
}
|
|
|
|
void QQuickTextArea::resetBottomInset()
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->setBottomInset(0, true);
|
|
}
|
|
|
|
void QQuickTextArea::classBegin()
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
QQuickTextEdit::classBegin();
|
|
d->resolveFont();
|
|
}
|
|
|
|
void QQuickTextArea::componentComplete()
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->executeBackground(true);
|
|
QQuickTextEdit::componentComplete();
|
|
d->resizeBackground();
|
|
#if QT_CONFIG(quicktemplates2_hover)
|
|
if (!d->explicitHoverEnabled)
|
|
setAcceptHoverEvents(QQuickControlPrivate::calcHoverEnabled(d->parentItem));
|
|
#endif
|
|
#if QT_CONFIG(accessibility)
|
|
if (QAccessible::isActive())
|
|
d->accessibilityActiveChanged(true);
|
|
#endif
|
|
}
|
|
|
|
void QQuickTextArea::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
QQuickTextEdit::itemChange(change, value);
|
|
switch (change) {
|
|
case ItemEnabledHasChanged:
|
|
break;
|
|
case ItemSceneChange:
|
|
case ItemParentHasChanged:
|
|
if ((change == ItemParentHasChanged && value.item) || (change == ItemSceneChange && value.window)) {
|
|
d->resolveFont();
|
|
#if QT_CONFIG(quicktemplates2_hover)
|
|
if (!d->explicitHoverEnabled)
|
|
d->updateHoverEnabled(QQuickControlPrivate::calcHoverEnabled(d->parentItem), false); // explicit=false
|
|
#endif
|
|
if (change == ItemParentHasChanged) {
|
|
QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(value.item->parentItem());
|
|
if (flickable) {
|
|
QQuickScrollView *scrollView = qobject_cast<QQuickScrollView *>(flickable->parentItem());
|
|
if (scrollView)
|
|
d->attachFlickable(flickable);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QQuickTextArea::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
QQuickTextEdit::geometryChange(newGeometry, oldGeometry);
|
|
d->resizeBackground();
|
|
}
|
|
|
|
void QQuickTextArea::insetChange(const QMarginsF &newInset, const QMarginsF &oldInset)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
Q_UNUSED(newInset);
|
|
Q_UNUSED(oldInset);
|
|
d->resizeBackground();
|
|
}
|
|
|
|
QSGNode *QQuickTextArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
QQuickDefaultClipNode *clipNode = static_cast<QQuickDefaultClipNode *>(oldNode);
|
|
if (!clipNode)
|
|
clipNode = new QQuickDefaultClipNode(QRectF());
|
|
|
|
QQuickItem *clipper = this;
|
|
if (d->flickable)
|
|
clipper = d->flickable;
|
|
|
|
const QRectF cr = clipper->clipRect().adjusted(
|
|
leftPadding(), topPadding(),
|
|
(!d->cursorItem && effectiveHAlign() == HAlignment::AlignRight ? 1 : 0) - rightPadding(),
|
|
-bottomPadding());
|
|
|
|
clipNode->setRect(!d->flickable ? cr : cr.translated(d->flickable->contentX(), d->flickable->contentY()));
|
|
clipNode->update();
|
|
|
|
QSGNode *textNode = QQuickTextEdit::updatePaintNode(clipNode->firstChild(), data);
|
|
if (!textNode->parent())
|
|
clipNode->appendChildNode(textNode);
|
|
|
|
if (d->cursorItem) {
|
|
QQuickDefaultClipNode *cursorNode = QQuickItemPrivate::get(d->cursorItem)->clipNode();
|
|
if (cursorNode)
|
|
cursorNode->setClipRect(d->cursorItem->mapRectFromItem(clipper, cr));
|
|
}
|
|
|
|
return clipNode;
|
|
}
|
|
|
|
void QQuickTextArea::focusInEvent(QFocusEvent *event)
|
|
{
|
|
QQuickTextEdit::focusInEvent(event);
|
|
setFocusReason(event->reason());
|
|
}
|
|
|
|
void QQuickTextArea::focusOutEvent(QFocusEvent *event)
|
|
{
|
|
QQuickTextEdit::focusOutEvent(event);
|
|
setFocusReason(event->reason());
|
|
}
|
|
|
|
#if QT_CONFIG(quicktemplates2_hover)
|
|
void QQuickTextArea::hoverEnterEvent(QHoverEvent *event)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
QQuickTextEdit::hoverEnterEvent(event);
|
|
setHovered(d->hoverEnabled);
|
|
event->setAccepted(d->hoverEnabled);
|
|
}
|
|
|
|
void QQuickTextArea::hoverLeaveEvent(QHoverEvent *event)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
QQuickTextEdit::hoverLeaveEvent(event);
|
|
setHovered(false);
|
|
event->setAccepted(d->hoverEnabled);
|
|
}
|
|
#endif
|
|
|
|
void QQuickTextArea::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->pressHandler.mousePressEvent(event);
|
|
if (d->pressHandler.isActive()) {
|
|
if (d->pressHandler.delayedMousePressEvent) {
|
|
QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
|
|
d->pressHandler.clearDelayedMouseEvent();
|
|
}
|
|
// Calling the base class implementation will result in QQuickTextControl's
|
|
// press handler being called, which ignores events that aren't Qt::LeftButton.
|
|
const bool wasAccepted = event->isAccepted();
|
|
QQuickTextEdit::mousePressEvent(event);
|
|
if (wasAccepted)
|
|
event->accept();
|
|
}
|
|
}
|
|
|
|
void QQuickTextArea::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->pressHandler.mouseMoveEvent(event);
|
|
if (d->pressHandler.isActive()) {
|
|
if (d->pressHandler.delayedMousePressEvent) {
|
|
QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
|
|
d->pressHandler.clearDelayedMouseEvent();
|
|
}
|
|
QQuickTextEdit::mouseMoveEvent(event);
|
|
}
|
|
}
|
|
|
|
void QQuickTextArea::mouseReleaseEvent(QMouseEvent *event)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
d->pressHandler.mouseReleaseEvent(event);
|
|
if (d->pressHandler.isActive()) {
|
|
if (d->pressHandler.delayedMousePressEvent) {
|
|
QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
|
|
d->pressHandler.clearDelayedMouseEvent();
|
|
}
|
|
QQuickTextEdit::mouseReleaseEvent(event);
|
|
}
|
|
}
|
|
|
|
void QQuickTextArea::mouseDoubleClickEvent(QMouseEvent *event)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
if (d->pressHandler.delayedMousePressEvent) {
|
|
QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
|
|
d->pressHandler.clearDelayedMouseEvent();
|
|
}
|
|
QQuickTextEdit::mouseDoubleClickEvent(event);
|
|
}
|
|
|
|
void QQuickTextArea::timerEvent(QTimerEvent *event)
|
|
{
|
|
Q_D(QQuickTextArea);
|
|
if (event->timerId() == d->pressHandler.timer.timerId())
|
|
d->pressHandler.timerEvent(event);
|
|
else
|
|
QQuickTextEdit::timerEvent(event);
|
|
}
|
|
|
|
class QQuickTextAreaAttachedPrivate : public QObjectPrivate
|
|
{
|
|
public:
|
|
QQuickTextArea *control = nullptr;
|
|
};
|
|
|
|
QQuickTextAreaAttached::QQuickTextAreaAttached(QObject *parent)
|
|
: QObject(*(new QQuickTextAreaAttachedPrivate), parent)
|
|
{
|
|
}
|
|
|
|
/*!
|
|
\qmlattachedproperty TextArea QtQuick.Controls::TextArea::flickable
|
|
|
|
This property attaches a text area to a \l Flickable.
|
|
|
|
\sa ScrollBar, ScrollIndicator, {Scrollable TextArea}
|
|
*/
|
|
QQuickTextArea *QQuickTextAreaAttached::flickable() const
|
|
{
|
|
Q_D(const QQuickTextAreaAttached);
|
|
return d->control;
|
|
}
|
|
|
|
void QQuickTextAreaAttached::setFlickable(QQuickTextArea *control)
|
|
{
|
|
Q_D(QQuickTextAreaAttached);
|
|
QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(parent());
|
|
if (!flickable) {
|
|
qmlWarning(parent()) << "TextArea must be attached to a Flickable";
|
|
return;
|
|
}
|
|
|
|
if (d->control == control)
|
|
return;
|
|
|
|
if (d->control)
|
|
QQuickTextAreaPrivate::get(d->control)->detachFlickable();
|
|
|
|
d->control = control;
|
|
|
|
if (control)
|
|
QQuickTextAreaPrivate::get(control)->attachFlickable(flickable);
|
|
|
|
emit flickableChanged();
|
|
}
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#include "moc_qquicktextarea_p.cpp"
|