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

source code of qtquickcontrols2/src/quicktemplates2/qquicktextarea.cpp