1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquicktextarea_p.h"
5#include "qquicktextarea_p_p.h"
6#include "qquickcontrol_p.h"
7#include "qquickcontrol_p_p.h"
8#include "qquickscrollview_p.h"
9#include "qquickdeferredexecute_p_p.h"
10
11#include <QtQml/qqmlinfo.h>
12#include <QtQuick/private/qquickitem_p.h>
13#include <QtQuick/private/qquickclipnode_p.h>
14#include <QtQuick/private/qquickflickable_p.h>
15
16#if QT_CONFIG(accessibility)
17#include <QtQuick/private/qquickaccessibleattached_p.h>
18#endif
19
20QT_BEGIN_NAMESPACE
21
22using namespace Qt::StringLiterals;
23
24/*!
25 \qmltype TextArea
26 \inherits TextEdit
27//! \instantiates QQuickTextArea
28 \inqmlmodule QtQuick.Controls
29 \since 5.7
30 \ingroup qtquickcontrols-input
31 \brief Multi-line text input area.
32
33 TextArea is a multi-line text editor. TextArea extends TextEdit with
34 a \l {placeholderText}{placeholder text} functionality, and adds decoration.
35
36 \image qtquickcontrols-textarea.png
37
38 \code
39 TextArea {
40 placeholderText: qsTr("Enter description")
41 }
42 \endcode
43
44 TextArea is not scrollable by itself. Especially on screen-size constrained
45 platforms, it is often preferable to make entire application pages scrollable.
46 On such a scrollable page, a non-scrollable TextArea might behave better than
47 nested scrollable controls. Notice, however, that in such a scenario, the background
48 decoration of the TextArea scrolls together with the rest of the scrollable
49 content.
50
51 \section2 Scrollable TextArea
52
53 If you want to make a TextArea scrollable, for example, when it covers
54 an entire application page, it can be placed inside a \l ScrollView.
55
56 \image qtquickcontrols-textarea-scrollable.png
57
58 \snippet qtquickcontrols-textarea-scrollable.qml 1
59
60 A TextArea that is placed inside a \l ScrollView does the following:
61
62 \list
63 \li Sets the content size automatically
64 \li Ensures that the background decoration stays in place
65 \li Clips the content
66 \endlist
67
68 \section2 Tab Focus
69
70 By default, pressing the tab key while TextArea has
71 \l {Item::activeFocus}{active focus} results in a tab character being input
72 into the control itself. To make tab pass active focus onto another item,
73 use the attached \l KeyNavigation properties:
74
75 \code
76 TextField {
77 id: textField
78 }
79
80 TextArea {
81 KeyNavigation.priority: KeyNavigation.BeforeItem
82 KeyNavigation.tab: textField
83 }
84 \endcode
85
86 \sa TextField, {Customizing TextArea}, {Input Controls}
87*/
88
89/*!
90 \qmlsignal QtQuick.Controls::TextArea::pressAndHold(MouseEvent event)
91
92 This signal is emitted when there is a long press (the delay depends on the platform plugin).
93 The \a event parameter provides information about the press, including the x and y
94 coordinates of the press, and which button is pressed.
95
96 \sa pressed, released
97*/
98
99/*!
100 \qmlsignal QtQuick.Controls::TextArea::pressed(MouseEvent event)
101 \since QtQuick.Controls 2.1 (Qt 5.8)
102
103 This signal is emitted when the text area is pressed by the user.
104 The \a event parameter provides information about the press,
105 including the x and y coordinates of the press, and which button is pressed.
106
107 \sa released, pressAndHold
108*/
109
110/*!
111 \qmlsignal QtQuick.Controls::TextArea::released(MouseEvent event)
112 \since QtQuick.Controls 2.1 (Qt 5.8)
113
114 This signal is emitted when the text area is released by the user.
115 The \a event parameter provides information about the release,
116 including the x and y coordinates of the press, and which button
117 is pressed.
118
119 \sa pressed, pressAndHold
120*/
121
122QQuickTextAreaPrivate::QQuickTextAreaPrivate()
123{
124#if QT_CONFIG(accessibility)
125 QAccessible::installActivationObserver(this);
126#endif
127}
128
129QQuickTextAreaPrivate::~QQuickTextAreaPrivate()
130{
131#if QT_CONFIG(accessibility)
132 QAccessible::removeActivationObserver(this);
133#endif
134}
135
136void QQuickTextAreaPrivate::setTopInset(qreal value, bool reset)
137{
138 Q_Q(QQuickTextArea);
139 const QMarginsF oldInset = getInset();
140 extra.value().topInset = value;
141 extra.value().hasTopInset = !reset;
142 if (!qFuzzyCompare(p1: oldInset.top(), p2: value)) {
143 emit q->topInsetChanged();
144 q->insetChange(newInset: getInset(), oldInset);
145 }
146}
147
148void QQuickTextAreaPrivate::setLeftInset(qreal value, bool reset)
149{
150 Q_Q(QQuickTextArea);
151 const QMarginsF oldInset = getInset();
152 extra.value().leftInset = value;
153 extra.value().hasLeftInset = !reset;
154 if (!qFuzzyCompare(p1: oldInset.left(), p2: value)) {
155 emit q->leftInsetChanged();
156 q->insetChange(newInset: getInset(), oldInset);
157 }
158}
159
160void QQuickTextAreaPrivate::setRightInset(qreal value, bool reset)
161{
162 Q_Q(QQuickTextArea);
163 const QMarginsF oldInset = getInset();
164 extra.value().rightInset = value;
165 extra.value().hasRightInset = !reset;
166 if (!qFuzzyCompare(p1: oldInset.right(), p2: value)) {
167 emit q->rightInsetChanged();
168 q->insetChange(newInset: getInset(), oldInset);
169 }
170}
171
172void QQuickTextAreaPrivate::setBottomInset(qreal value, bool reset)
173{
174 Q_Q(QQuickTextArea);
175 const QMarginsF oldInset = getInset();
176 extra.value().bottomInset = value;
177 extra.value().hasBottomInset = !reset;
178 if (!qFuzzyCompare(p1: oldInset.bottom(), p2: value)) {
179 emit q->bottomInsetChanged();
180 q->insetChange(newInset: getInset(), oldInset);
181 }
182}
183
184void QQuickTextAreaPrivate::resizeBackground()
185{
186 if (!background)
187 return;
188
189 resizingBackground = true;
190
191 // When using the attached property TextArea.flickable, we reparent the background out
192 // of TextArea and into the Flickable since we don't want the background to move while
193 // flicking. This means that the size of the background should also follow the size of
194 // the Flickable rather than the size of the TextArea.
195 const auto flickable = qobject_cast<QQuickFlickable *>(object: background->parentItem());
196
197 QQuickItemPrivate *p = QQuickItemPrivate::get(item: background);
198 if (((!p->widthValid() || !extra.isAllocated() || !extra->hasBackgroundWidth) && qFuzzyIsNull(d: background->x()))
199 || (extra.isAllocated() && (extra->hasLeftInset || extra->hasRightInset))) {
200 const qreal bgWidth = flickable ? flickable->width() : width;
201 background->setX(getLeftInset());
202 background->setWidth(bgWidth - getLeftInset() - getRightInset());
203 }
204
205 if (((!p->heightValid() || !extra.isAllocated() || !extra->hasBackgroundHeight) && qFuzzyIsNull(d: background->y()))
206 || (extra.isAllocated() && (extra->hasTopInset || extra->hasBottomInset))) {
207 const qreal bgHeight = flickable ? flickable->height() : height;
208 background->setY(getTopInset());
209 background->setHeight(bgHeight - getTopInset() - getBottomInset());
210 }
211
212 resizingBackground = false;
213}
214
215/*!
216 \internal
217
218 Determine which font is implicitly imposed on this control by its ancestors
219 and QGuiApplication::font, resolve this against its own font (attributes from
220 the implicit font are copied over). Then propagate this font to this
221 control's children.
222*/
223void QQuickTextAreaPrivate::resolveFont()
224{
225 Q_Q(QQuickTextArea);
226 inheritFont(font: QQuickControlPrivate::parentFont(item: q));
227}
228
229void QQuickTextAreaPrivate::inheritFont(const QFont &font)
230{
231 QFont parentFont = extra.isAllocated() ? extra->requestedFont.resolve(font) : font;
232 parentFont.setResolveMask(extra.isAllocated() ? extra->requestedFont.resolveMask() | font.resolveMask() : font.resolveMask());
233
234 const QFont defaultFont = QQuickTheme::font(scope: QQuickTheme::TextArea);
235 QFont resolvedFont = parentFont.resolve(defaultFont);
236
237 setFont_helper(resolvedFont);
238}
239
240/*!
241 \internal
242
243 Assign \a font to this control, and propagate it to all children.
244*/
245void QQuickTextAreaPrivate::updateFont(const QFont &font)
246{
247 Q_Q(QQuickTextArea);
248 QFont oldFont = sourceFont;
249 q->QQuickTextEdit::setFont(font);
250
251 QQuickControlPrivate::updateFontRecur(item: q, font);
252
253 if (oldFont != font)
254 emit q->fontChanged();
255}
256
257#if QT_CONFIG(quicktemplates2_hover)
258void QQuickTextAreaPrivate::updateHoverEnabled(bool enabled, bool xplicit)
259{
260 Q_Q(QQuickTextArea);
261 if (!xplicit && explicitHoverEnabled)
262 return;
263
264 bool wasEnabled = q->isHoverEnabled();
265 explicitHoverEnabled = xplicit;
266 if (wasEnabled != enabled) {
267 q->setAcceptHoverEvents(enabled);
268 QQuickControlPrivate::updateHoverEnabledRecur(item: q, enabled);
269 emit q->hoverEnabledChanged();
270 }
271}
272#endif
273
274void QQuickTextAreaPrivate::attachFlickable(QQuickFlickable *item)
275{
276 Q_Q(QQuickTextArea);
277 flickable = item;
278 q->setParentItem(flickable->contentItem());
279
280 if (background)
281 background->setParentItem(flickable);
282
283 QObjectPrivate::connect(sender: q, signal: &QQuickTextArea::contentSizeChanged, receiverPrivate: this, slot: &QQuickTextAreaPrivate::resizeFlickableContent);
284 QObjectPrivate::connect(sender: q, signal: &QQuickTextEdit::cursorRectangleChanged, receiverPrivate: this, slot: &QQuickTextAreaPrivate::ensureCursorVisible);
285
286 QObject::connect(sender: flickable, signal: &QQuickFlickable::contentXChanged, context: q, slot: &QQuickItem::update);
287 QObject::connect(sender: flickable, signal: &QQuickFlickable::contentYChanged, context: q, slot: &QQuickItem::update);
288
289 QQuickItemPrivate::get(item: flickable)->updateOrAddGeometryChangeListener(listener: this, types: QQuickGeometryChange::Size);
290 QQuickItemPrivate::get(item: flickable)->addItemChangeListener(listener: this, types: QQuickItemPrivate::Destroyed);
291 QObjectPrivate::connect(sender: flickable, signal: &QQuickFlickable::contentWidthChanged, receiverPrivate: this, slot: &QQuickTextAreaPrivate::resizeFlickableControl);
292 QObjectPrivate::connect(sender: flickable, signal: &QQuickFlickable::contentHeightChanged, receiverPrivate: this, slot: &QQuickTextAreaPrivate::resizeFlickableControl);
293
294 resizeFlickableControl();
295}
296
297void QQuickTextAreaPrivate::detachFlickable()
298{
299 Q_Q(QQuickTextArea);
300 q->setParentItem(nullptr);
301 if (background && background->parentItem() == flickable)
302 background->setParentItem(q);
303
304 QObjectPrivate::disconnect(sender: q, signal: &QQuickTextArea::contentSizeChanged, receiverPrivate: this, slot: &QQuickTextAreaPrivate::resizeFlickableContent);
305 QObjectPrivate::disconnect(sender: q, signal: &QQuickTextEdit::cursorRectangleChanged, receiverPrivate: this, slot: &QQuickTextAreaPrivate::ensureCursorVisible);
306
307 QObject::disconnect(sender: flickable, signal: &QQuickFlickable::contentXChanged, receiver: q, slot: &QQuickItem::update);
308 QObject::disconnect(sender: flickable, signal: &QQuickFlickable::contentYChanged, receiver: q, slot: &QQuickItem::update);
309
310 QQuickItemPrivate::get(item: flickable)->updateOrRemoveGeometryChangeListener(listener: this, types: QQuickGeometryChange::Nothing);
311 QQuickItemPrivate::get(item: flickable)->removeItemChangeListener(this, types: QQuickItemPrivate::Destroyed);
312 QObjectPrivate::disconnect(sender: flickable, signal: &QQuickFlickable::contentWidthChanged, receiverPrivate: this, slot: &QQuickTextAreaPrivate::resizeFlickableControl);
313 QObjectPrivate::disconnect(sender: flickable, signal: &QQuickFlickable::contentHeightChanged, receiverPrivate: this, slot: &QQuickTextAreaPrivate::resizeFlickableControl);
314
315 flickable = nullptr;
316
317 resizeBackground();
318}
319
320void QQuickTextAreaPrivate::ensureCursorVisible()
321{
322 Q_Q(QQuickTextArea);
323 if (!flickable)
324 return;
325
326 const qreal cx = flickable->contentX();
327 const qreal cy = flickable->contentY();
328 const qreal w = flickable->width();
329 const qreal h = flickable->height();
330
331 const qreal tp = q->topPadding();
332 const qreal lp = q->leftPadding();
333 const QRectF cr = q->cursorRectangle();
334
335 if (cr.left() <= cx + lp) {
336 flickable->setContentX(cr.left() - lp);
337 } else {
338 // calculate the rectangle of the next character and ensure that
339 // it's visible if it's on the same line with the cursor
340 const qreal rp = q->rightPadding();
341 const QRectF nr = q->cursorPosition() < q->length() ? q->positionToRectangle(q->cursorPosition() + 1) : QRectF();
342 if (qFuzzyCompare(p1: nr.y(), p2: cr.y()) && nr.right() >= cx + lp + w - rp)
343 flickable->setContentX(nr.right() - w + rp);
344 else if (cr.right() >= cx + lp + w - rp)
345 flickable->setContentX(cr.right() - w + rp);
346 }
347
348 if (cr.top() <= cy + tp) {
349 flickable->setContentY(cr.top() - tp);
350 } else {
351 const qreal bp = q->bottomPadding();
352 if (cr.bottom() >= cy + tp + h - bp && cr.bottom() <= flickable->contentHeight())
353 flickable->setContentY(cr.bottom() - h + bp);
354 }
355}
356
357void QQuickTextAreaPrivate::resizeFlickableControl()
358{
359 Q_Q(QQuickTextArea);
360 if (!flickable)
361 return;
362
363 const qreal w = wrapMode == QQuickTextArea::NoWrap ? qMax(a: flickable->width(), b: flickable->contentWidth()) : flickable->width();
364 const qreal h = qMax(a: flickable->height(), b: flickable->contentHeight());
365 q->setSize(QSizeF(w, h));
366
367 resizeBackground();
368}
369
370void QQuickTextAreaPrivate::resizeFlickableContent()
371{
372 Q_Q(QQuickTextArea);
373 if (!flickable)
374 return;
375
376 flickable->setContentWidth(q->implicitWidth());
377 flickable->setContentHeight(q->implicitHeight());
378}
379
380void QQuickTextAreaPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
381{
382 Q_UNUSED(diff);
383 if (!resizingBackground && item == background) {
384 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
385 // Only set hasBackgroundWidth/Height if it was a width/height change,
386 // otherwise we're prevented from setting a width/height in the future.
387 if (change.widthChange())
388 extra.value().hasBackgroundWidth = p->widthValid();
389 if (change.heightChange())
390 extra.value().hasBackgroundHeight = p->heightValid();
391 }
392
393 if (flickable)
394 resizeFlickableControl();
395 else
396 resizeBackground();
397}
398
399qreal QQuickTextAreaPrivate::getImplicitWidth() const
400{
401 return QQuickItemPrivate::getImplicitWidth();
402}
403
404qreal QQuickTextAreaPrivate::getImplicitHeight() const
405{
406 return QQuickItemPrivate::getImplicitHeight();
407}
408
409void QQuickTextAreaPrivate::implicitWidthChanged()
410{
411 Q_Q(QQuickTextArea);
412 QQuickItemPrivate::implicitWidthChanged();
413 emit q->implicitWidthChanged3();
414}
415
416void QQuickTextAreaPrivate::implicitHeightChanged()
417{
418 Q_Q(QQuickTextArea);
419 QQuickItemPrivate::implicitHeightChanged();
420 emit q->implicitHeightChanged3();
421}
422
423void QQuickTextAreaPrivate::readOnlyChanged(bool isReadOnly)
424{
425 Q_UNUSED(isReadOnly);
426#if QT_CONFIG(accessibility)
427 if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(object: q_func()))
428 accessibleAttached->set_readOnly(isReadOnly);
429#endif
430}
431
432#if QT_CONFIG(accessibility)
433void QQuickTextAreaPrivate::accessibilityActiveChanged(bool active)
434{
435 if (!active)
436 return;
437
438 Q_Q(QQuickTextArea);
439 QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(object: qmlAttachedPropertiesObject<QQuickAccessibleAttached>(obj: q, create: true));
440 Q_ASSERT(accessibleAttached);
441 accessibleAttached->setRole(effectiveAccessibleRole());
442 accessibleAttached->set_readOnly(q->isReadOnly());
443 accessibleAttached->setDescription(placeholder);
444}
445
446QAccessible::Role QQuickTextAreaPrivate::accessibleRole() const
447{
448 return QAccessible::EditableText;
449}
450#endif
451
452void QQuickTextAreaPrivate::cancelBackground()
453{
454 Q_Q(QQuickTextArea);
455 quickCancelDeferred(object: q, property: backgroundName());
456}
457
458void QQuickTextAreaPrivate::executeBackground(bool complete)
459{
460 Q_Q(QQuickTextArea);
461 if (background.wasExecuted())
462 return;
463
464 if (!background || complete)
465 quickBeginDeferred(object: q, property: backgroundName(), delegate&: background);
466 if (complete)
467 quickCompleteDeferred(object: q, property: backgroundName(), delegate&: background);
468}
469
470void QQuickTextAreaPrivate::itemImplicitWidthChanged(QQuickItem *item)
471{
472 Q_Q(QQuickTextArea);
473 if (item == background)
474 emit q->implicitBackgroundWidthChanged();
475}
476
477void QQuickTextAreaPrivate::itemImplicitHeightChanged(QQuickItem *item)
478{
479 Q_Q(QQuickTextArea);
480 if (item == background)
481 emit q->implicitBackgroundHeightChanged();
482}
483
484void QQuickTextAreaPrivate::itemDestroyed(QQuickItem *item)
485{
486 Q_Q(QQuickTextArea);
487 if (item == background) {
488 background = nullptr;
489 emit q->implicitBackgroundWidthChanged();
490 emit q->implicitBackgroundHeightChanged();
491 } else if (item == flickable) {
492 detachFlickable();
493 }
494}
495
496QPalette QQuickTextAreaPrivate::defaultPalette() const
497{
498 return QQuickTheme::palette(scope: QQuickTheme::TextArea);
499}
500
501QQuickTextArea::QQuickTextArea(QQuickItem *parent)
502 : QQuickTextEdit(*(new QQuickTextAreaPrivate), parent)
503{
504 Q_D(QQuickTextArea);
505 setActiveFocusOnTab(true);
506 setAcceptedMouseButtons(Qt::AllButtons);
507 d->setImplicitResizeEnabled(false);
508 d->pressHandler.control = this;
509
510 QObjectPrivate::connect(sender: this, signal: &QQuickTextEdit::readOnlyChanged,
511 receiverPrivate: d, slot: &QQuickTextAreaPrivate::readOnlyChanged);
512#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
513 if (qEnvironmentVariable(varName: "QT_QUICK_CONTROLS_TEXT_SELECTION_BEHAVIOR") == u"old"_s)
514 QQuickTextEdit::setOldSelectionDefault();
515#endif
516}
517
518QQuickTextArea::~QQuickTextArea()
519{
520 Q_D(QQuickTextArea);
521 if (d->flickable)
522 d->detachFlickable();
523 QQuickControlPrivate::removeImplicitSizeListener(item: d->background, listener: d, changes: QQuickControlPrivate::ImplicitSizeChanges | QQuickItemPrivate::Geometry);
524}
525
526QQuickTextAreaAttached *QQuickTextArea::qmlAttachedProperties(QObject *object)
527{
528 return new QQuickTextAreaAttached(object);
529}
530
531QFont QQuickTextArea::font() const
532{
533 Q_D(const QQuickTextArea);
534 QFont font = QQuickTextEdit::font();
535 // The resolve mask should inherit from the requestedFont
536 font.setResolveMask(d->extra.value().requestedFont.resolveMask());
537 return font;
538}
539
540void QQuickTextArea::setFont(const QFont &font)
541{
542 Q_D(QQuickTextArea);
543 if (d->extra.value().requestedFont.resolveMask() == font.resolveMask() && d->extra.value().requestedFont == font)
544 return;
545
546 d->extra.value().requestedFont = font;
547 d->resolveFont();
548}
549
550/*!
551 \qmlproperty Item QtQuick.Controls::TextArea::background
552
553 This property holds the background item.
554
555 \input qquickcontrol-background.qdocinc notes
556
557 \sa {Customizing TextArea}
558*/
559QQuickItem *QQuickTextArea::background() const
560{
561 QQuickTextAreaPrivate *d = const_cast<QQuickTextAreaPrivate *>(d_func());
562 if (!d->background)
563 d->executeBackground();
564 return d->background;
565}
566
567void QQuickTextArea::setBackground(QQuickItem *background)
568{
569 Q_D(QQuickTextArea);
570 if (d->background == background)
571 return;
572
573 QQuickControlPrivate::warnIfCustomizationNotSupported(control: this, item: background, QStringLiteral("background"));
574
575 if (!d->background.isExecuting())
576 d->cancelBackground();
577
578 const qreal oldImplicitBackgroundWidth = implicitBackgroundWidth();
579 const qreal oldImplicitBackgroundHeight = implicitBackgroundHeight();
580
581 if (d->extra.isAllocated()) {
582 d->extra.value().hasBackgroundWidth = false;
583 d->extra.value().hasBackgroundHeight = false;
584 }
585
586 QQuickControlPrivate::removeImplicitSizeListener(item: d->background, listener: d, changes: QQuickControlPrivate::ImplicitSizeChanges | QQuickItemPrivate::Geometry);
587 QQuickControlPrivate::hideOldItem(item: d->background);
588 d->background = background;
589
590 if (background) {
591 QQuickItemPrivate *p = QQuickItemPrivate::get(item: background);
592 if (p->widthValid() || p->heightValid()) {
593 d->extra.value().hasBackgroundWidth = p->widthValid();
594 d->extra.value().hasBackgroundHeight = p->heightValid();
595 }
596 if (d->flickable)
597 background->setParentItem(d->flickable);
598 else
599 background->setParentItem(this);
600 if (qFuzzyIsNull(d: background->z()))
601 background->setZ(-1);
602 if (isComponentComplete())
603 d->resizeBackground();
604 QQuickControlPrivate::addImplicitSizeListener(item: background, listener: d, changes: QQuickControlPrivate::ImplicitSizeChanges | QQuickItemPrivate::Geometry);
605 }
606
607 if (!qFuzzyCompare(p1: oldImplicitBackgroundWidth, p2: implicitBackgroundWidth()))
608 emit implicitBackgroundWidthChanged();
609 if (!qFuzzyCompare(p1: oldImplicitBackgroundHeight, p2: implicitBackgroundHeight()))
610 emit implicitBackgroundHeightChanged();
611 if (!d->background.isExecuting())
612 emit backgroundChanged();
613}
614
615/*!
616 \qmlproperty string QtQuick.Controls::TextArea::placeholderText
617
618 This property holds the short hint that is displayed in the text area before
619 the user enters a value.
620*/
621QString QQuickTextArea::placeholderText() const
622{
623 Q_D(const QQuickTextArea);
624 return d->placeholder;
625}
626
627void QQuickTextArea::setPlaceholderText(const QString &text)
628{
629 Q_D(QQuickTextArea);
630 if (d->placeholder == text)
631 return;
632
633 d->placeholder = text;
634#if QT_CONFIG(accessibility)
635 if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(object: this))
636 accessibleAttached->setDescription(text);
637#endif
638 emit placeholderTextChanged();
639}
640
641/*!
642 \qmlproperty color QtQuick.Controls::TextArea::placeholderTextColor
643 \since QtQuick.Controls 2.5 (Qt 5.12)
644
645 This property holds the color of placeholderText.
646
647 \sa placeholderText
648*/
649QColor QQuickTextArea::placeholderTextColor() const
650{
651 Q_D(const QQuickTextArea);
652 return d->placeholderColor;
653}
654
655void QQuickTextArea::setPlaceholderTextColor(const QColor &color)
656{
657 Q_D(QQuickTextArea);
658 if (d->placeholderColor == color)
659 return;
660
661 d->placeholderColor = color;
662 emit placeholderTextColorChanged();
663}
664
665/*!
666 \qmlproperty enumeration QtQuick.Controls::TextArea::focusReason
667
668 \include qquickcontrol-focusreason.qdocinc
669*/
670Qt::FocusReason QQuickTextArea::focusReason() const
671{
672 Q_D(const QQuickTextArea);
673 return d->focusReason;
674}
675
676void QQuickTextArea::setFocusReason(Qt::FocusReason reason)
677{
678 Q_D(QQuickTextArea);
679 if (d->focusReason == reason)
680 return;
681
682 d->focusReason = reason;
683 emit focusReasonChanged();
684}
685
686bool QQuickTextArea::contains(const QPointF &point) const
687{
688 Q_D(const QQuickTextArea);
689 if (d->flickable && !d->flickable->contains(point: d->flickable->mapFromItem(item: this, point)))
690 return false;
691 return QQuickTextEdit::contains(point);
692}
693
694/*!
695 \since QtQuick.Controls 2.1 (Qt 5.8)
696 \qmlproperty bool QtQuick.Controls::TextArea::hovered
697 \readonly
698
699 This property holds whether the text area is hovered.
700
701 \sa hoverEnabled
702*/
703bool QQuickTextArea::isHovered() const
704{
705#if QT_CONFIG(quicktemplates2_hover)
706 Q_D(const QQuickTextArea);
707 return d->hovered;
708#else
709 return false;
710#endif
711}
712
713void QQuickTextArea::setHovered(bool hovered)
714{
715#if QT_CONFIG(quicktemplates2_hover)
716 Q_D(QQuickTextArea);
717 if (hovered == d->hovered)
718 return;
719
720 d->hovered = hovered;
721 emit hoveredChanged();
722#else
723 Q_UNUSED(hovered);
724#endif
725}
726
727/*!
728 \since QtQuick.Controls 2.1 (Qt 5.8)
729 \qmlproperty bool QtQuick.Controls::TextArea::hoverEnabled
730
731 This property determines whether the text area accepts hover events. The default value is \c true.
732
733 \sa hovered
734*/
735bool QQuickTextArea::isHoverEnabled() const
736{
737#if QT_CONFIG(quicktemplates2_hover)
738 Q_D(const QQuickTextArea);
739 return d->hoverEnabled;
740#else
741 return false;
742#endif
743}
744
745void QQuickTextArea::setHoverEnabled(bool enabled)
746{
747#if QT_CONFIG(quicktemplates2_hover)
748 Q_D(QQuickTextArea);
749 if (d->explicitHoverEnabled && enabled == d->hoverEnabled)
750 return;
751
752 d->updateHoverEnabled(enabled, xplicit: true); // explicit=true
753#else
754 Q_UNUSED(enabled);
755#endif
756}
757
758void QQuickTextArea::resetHoverEnabled()
759{
760#if QT_CONFIG(quicktemplates2_hover)
761 Q_D(QQuickTextArea);
762 if (!d->explicitHoverEnabled)
763 return;
764
765 d->explicitHoverEnabled = false;
766 d->updateHoverEnabled(enabled: QQuickControlPrivate::calcHoverEnabled(item: d->parentItem), xplicit: false); // explicit=false
767#endif
768}
769
770/*!
771 \since QtQuick.Controls 2.5 (Qt 5.12)
772 \qmlproperty real QtQuick.Controls::TextArea::implicitBackgroundWidth
773 \readonly
774
775 This property holds the implicit background width.
776
777 The value is equal to \c {background ? background.implicitWidth : 0}.
778
779 \sa implicitBackgroundHeight
780*/
781qreal QQuickTextArea::implicitBackgroundWidth() const
782{
783 Q_D(const QQuickTextArea);
784 if (!d->background)
785 return 0;
786 return d->background->implicitWidth();
787}
788
789/*!
790 \since QtQuick.Controls 2.5 (Qt 5.12)
791 \qmlproperty real QtQuick.Controls::TextArea::implicitBackgroundHeight
792 \readonly
793
794 This property holds the implicit background height.
795
796 The value is equal to \c {background ? background.implicitHeight : 0}.
797
798 \sa implicitBackgroundWidth
799*/
800qreal QQuickTextArea::implicitBackgroundHeight() const
801{
802 Q_D(const QQuickTextArea);
803 if (!d->background)
804 return 0;
805 return d->background->implicitHeight();
806}
807
808/*!
809 \since QtQuick.Controls 2.5 (Qt 5.12)
810 \qmlproperty real QtQuick.Controls::TextArea::topInset
811
812 This property holds the top inset for the background.
813
814 \sa {Control Layout}, bottomInset
815*/
816qreal QQuickTextArea::topInset() const
817{
818 Q_D(const QQuickTextArea);
819 return d->getTopInset();
820}
821
822void QQuickTextArea::setTopInset(qreal inset)
823{
824 Q_D(QQuickTextArea);
825 d->setTopInset(value: inset);
826}
827
828void QQuickTextArea::resetTopInset()
829{
830 Q_D(QQuickTextArea);
831 d->setTopInset(value: 0, reset: true);
832}
833
834/*!
835 \since QtQuick.Controls 2.5 (Qt 5.12)
836 \qmlproperty real QtQuick.Controls::TextArea::leftInset
837
838 This property holds the left inset for the background.
839
840 \sa {Control Layout}, rightInset
841*/
842qreal QQuickTextArea::leftInset() const
843{
844 Q_D(const QQuickTextArea);
845 return d->getLeftInset();
846}
847
848void QQuickTextArea::setLeftInset(qreal inset)
849{
850 Q_D(QQuickTextArea);
851 d->setLeftInset(value: inset);
852}
853
854void QQuickTextArea::resetLeftInset()
855{
856 Q_D(QQuickTextArea);
857 d->setLeftInset(value: 0, reset: true);
858}
859
860/*!
861 \since QtQuick.Controls 2.5 (Qt 5.12)
862 \qmlproperty real QtQuick.Controls::TextArea::rightInset
863
864 This property holds the right inset for the background.
865
866 \sa {Control Layout}, leftInset
867*/
868qreal QQuickTextArea::rightInset() const
869{
870 Q_D(const QQuickTextArea);
871 return d->getRightInset();
872}
873
874void QQuickTextArea::setRightInset(qreal inset)
875{
876 Q_D(QQuickTextArea);
877 d->setRightInset(value: inset);
878}
879
880void QQuickTextArea::resetRightInset()
881{
882 Q_D(QQuickTextArea);
883 d->setRightInset(value: 0, reset: true);
884}
885
886/*!
887 \since QtQuick.Controls 2.5 (Qt 5.12)
888 \qmlproperty real QtQuick.Controls::TextArea::bottomInset
889
890 This property holds the bottom inset for the background.
891
892 \sa {Control Layout}, topInset
893*/
894qreal QQuickTextArea::bottomInset() const
895{
896 Q_D(const QQuickTextArea);
897 return d->getBottomInset();
898}
899
900void QQuickTextArea::setBottomInset(qreal inset)
901{
902 Q_D(QQuickTextArea);
903 d->setBottomInset(value: inset);
904}
905
906void QQuickTextArea::resetBottomInset()
907{
908 Q_D(QQuickTextArea);
909 d->setBottomInset(value: 0, reset: true);
910}
911
912void QQuickTextArea::classBegin()
913{
914 Q_D(QQuickTextArea);
915 QQuickTextEdit::classBegin();
916 d->resolveFont();
917}
918
919void QQuickTextArea::componentComplete()
920{
921 Q_D(QQuickTextArea);
922 d->executeBackground(complete: true);
923 QQuickTextEdit::componentComplete();
924 d->resizeBackground();
925#if QT_CONFIG(quicktemplates2_hover)
926 if (!d->explicitHoverEnabled)
927 setAcceptHoverEvents(QQuickControlPrivate::calcHoverEnabled(item: d->parentItem));
928#endif
929#if QT_CONFIG(accessibility)
930 if (QAccessible::isActive())
931 d->accessibilityActiveChanged(active: true);
932#endif
933}
934
935void QQuickTextArea::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
936{
937 Q_D(QQuickTextArea);
938 QQuickTextEdit::itemChange(change, value);
939 switch (change) {
940 case ItemEnabledHasChanged:
941 break;
942 case ItemSceneChange:
943 case ItemParentHasChanged:
944 if ((change == ItemParentHasChanged && value.item) || (change == ItemSceneChange && value.window)) {
945 d->resolveFont();
946#if QT_CONFIG(quicktemplates2_hover)
947 if (!d->explicitHoverEnabled)
948 d->updateHoverEnabled(enabled: QQuickControlPrivate::calcHoverEnabled(item: d->parentItem), xplicit: false); // explicit=false
949#endif
950 if (change == ItemParentHasChanged) {
951 QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(object: value.item->parentItem());
952 if (flickable) {
953 QQuickScrollView *scrollView = qobject_cast<QQuickScrollView *>(object: flickable->parentItem());
954 if (scrollView)
955 d->attachFlickable(item: flickable);
956 }
957 }
958 }
959 break;
960 default:
961 break;
962 }
963}
964
965void QQuickTextArea::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
966{
967 Q_D(QQuickTextArea);
968 QQuickTextEdit::geometryChange(newGeometry, oldGeometry);
969 d->resizeBackground();
970}
971
972void QQuickTextArea::insetChange(const QMarginsF &newInset, const QMarginsF &oldInset)
973{
974 Q_D(QQuickTextArea);
975 Q_UNUSED(newInset);
976 Q_UNUSED(oldInset);
977 d->resizeBackground();
978}
979
980QSGNode *QQuickTextArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
981{
982 Q_D(QQuickTextArea);
983 QQuickDefaultClipNode *clipNode = static_cast<QQuickDefaultClipNode *>(oldNode);
984 if (!clipNode)
985 clipNode = new QQuickDefaultClipNode(QRectF());
986
987 QQuickItem *clipper = this;
988 if (d->flickable)
989 clipper = d->flickable;
990
991 const QRectF cr = clipper->clipRect().adjusted(
992 xp1: leftPadding(), yp1: topPadding(),
993 xp2: (!d->cursorItem && effectiveHAlign() == HAlignment::AlignRight ? 1 : 0) - rightPadding(),
994 yp2: -bottomPadding());
995
996 clipNode->setRect(!d->flickable ? cr : cr.translated(dx: d->flickable->contentX(), dy: d->flickable->contentY()));
997 clipNode->update();
998
999 QSGNode *textNode = QQuickTextEdit::updatePaintNode(oldNode: clipNode->firstChild(), updatePaintNodeData: data);
1000 if (!textNode->parent())
1001 clipNode->appendChildNode(node: textNode);
1002
1003 if (d->cursorItem) {
1004 QQuickDefaultClipNode *cursorNode = QQuickItemPrivate::get(item: d->cursorItem)->clipNode();
1005 if (cursorNode)
1006 cursorNode->setClipRect(d->cursorItem->mapRectFromItem(item: clipper, rect: cr));
1007 }
1008
1009 return clipNode;
1010}
1011
1012void QQuickTextArea::focusInEvent(QFocusEvent *event)
1013{
1014 QQuickTextEdit::focusInEvent(event);
1015 setFocusReason(event->reason());
1016}
1017
1018void QQuickTextArea::focusOutEvent(QFocusEvent *event)
1019{
1020 QQuickTextEdit::focusOutEvent(event);
1021 setFocusReason(event->reason());
1022}
1023
1024#if QT_CONFIG(quicktemplates2_hover)
1025void QQuickTextArea::hoverEnterEvent(QHoverEvent *event)
1026{
1027 Q_D(QQuickTextArea);
1028 QQuickTextEdit::hoverEnterEvent(event);
1029 setHovered(d->hoverEnabled);
1030 event->ignore();
1031}
1032
1033void QQuickTextArea::hoverLeaveEvent(QHoverEvent *event)
1034{
1035 QQuickTextEdit::hoverLeaveEvent(event);
1036 setHovered(false);
1037 event->ignore();
1038}
1039#endif
1040
1041void QQuickTextArea::mousePressEvent(QMouseEvent *event)
1042{
1043 Q_D(QQuickTextArea);
1044 d->pressHandler.mousePressEvent(event);
1045 if (d->pressHandler.isActive()) {
1046 if (d->pressHandler.delayedMousePressEvent) {
1047 QQuickTextEdit::mousePressEvent(event: d->pressHandler.delayedMousePressEvent.get());
1048 d->pressHandler.clearDelayedMouseEvent();
1049 }
1050 // Calling the base class implementation will result in QQuickTextControl's
1051 // press handler being called, which ignores events that aren't Qt::LeftButton.
1052 const bool wasAccepted = event->isAccepted();
1053 QQuickTextEdit::mousePressEvent(event);
1054 if (wasAccepted)
1055 event->accept();
1056 }
1057}
1058
1059void QQuickTextArea::mouseMoveEvent(QMouseEvent *event)
1060{
1061 Q_D(QQuickTextArea);
1062 d->pressHandler.mouseMoveEvent(event);
1063 if (d->pressHandler.isActive()) {
1064 if (d->pressHandler.delayedMousePressEvent) {
1065 QQuickTextEdit::mousePressEvent(event: d->pressHandler.delayedMousePressEvent.get());
1066 d->pressHandler.clearDelayedMouseEvent();
1067 }
1068 QQuickTextEdit::mouseMoveEvent(event);
1069 }
1070}
1071
1072void QQuickTextArea::mouseReleaseEvent(QMouseEvent *event)
1073{
1074 Q_D(QQuickTextArea);
1075 d->pressHandler.mouseReleaseEvent(event);
1076 if (d->pressHandler.isActive()) {
1077 if (d->pressHandler.delayedMousePressEvent) {
1078 QQuickTextEdit::mousePressEvent(event: d->pressHandler.delayedMousePressEvent.get());
1079 d->pressHandler.clearDelayedMouseEvent();
1080 }
1081 QQuickTextEdit::mouseReleaseEvent(event);
1082 }
1083}
1084
1085void QQuickTextArea::mouseDoubleClickEvent(QMouseEvent *event)
1086{
1087 Q_D(QQuickTextArea);
1088 if (d->pressHandler.delayedMousePressEvent) {
1089 QQuickTextEdit::mousePressEvent(event: d->pressHandler.delayedMousePressEvent.get());
1090 d->pressHandler.clearDelayedMouseEvent();
1091 }
1092 QQuickTextEdit::mouseDoubleClickEvent(event);
1093}
1094
1095void QQuickTextArea::timerEvent(QTimerEvent *event)
1096{
1097 Q_D(QQuickTextArea);
1098 if (event->timerId() == d->pressHandler.timer.timerId())
1099 d->pressHandler.timerEvent(event);
1100 else
1101 QQuickTextEdit::timerEvent(event);
1102}
1103
1104class QQuickTextAreaAttachedPrivate : public QObjectPrivate
1105{
1106public:
1107 QQuickTextArea *control = nullptr;
1108};
1109
1110QQuickTextAreaAttached::QQuickTextAreaAttached(QObject *parent)
1111 : QObject(*(new QQuickTextAreaAttachedPrivate), parent)
1112{
1113}
1114
1115/*!
1116 \qmlattachedproperty TextArea QtQuick.Controls::TextArea::flickable
1117
1118 This property attaches a text area to a \l Flickable.
1119
1120 \sa ScrollBar, ScrollIndicator, {Scrollable TextArea}
1121*/
1122QQuickTextArea *QQuickTextAreaAttached::flickable() const
1123{
1124 Q_D(const QQuickTextAreaAttached);
1125 return d->control;
1126}
1127
1128void QQuickTextAreaAttached::setFlickable(QQuickTextArea *control)
1129{
1130 Q_D(QQuickTextAreaAttached);
1131 QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(object: parent());
1132 if (!flickable) {
1133 qmlWarning(me: parent()) << "TextArea must be attached to a Flickable";
1134 return;
1135 }
1136
1137 if (d->control == control)
1138 return;
1139
1140 if (d->control)
1141 QQuickTextAreaPrivate::get(item: d->control)->detachFlickable();
1142
1143 d->control = control;
1144
1145 if (control)
1146 QQuickTextAreaPrivate::get(item: control)->attachFlickable(item: flickable);
1147
1148 emit flickableChanged();
1149}
1150
1151QT_END_NAMESPACE
1152
1153#include "moc_qquicktextarea_p.cpp"
1154

source code of qtdeclarative/src/quicktemplates/qquicktextarea.cpp