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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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