1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qlineedit.h"
41#include "qlineedit_p.h"
42
43#include "qvariant.h"
44#if QT_CONFIG(itemviews)
45#include "qabstractitemview.h"
46#endif
47#if QT_CONFIG(draganddrop)
48#include "qdrag.h"
49#endif
50#include "qwidgetaction.h"
51#include "qclipboard.h"
52#ifndef QT_NO_ACCESSIBILITY
53#include "qaccessible.h"
54#endif
55#ifndef QT_NO_IM
56#include "qinputmethod.h"
57#include "qlist.h"
58#endif
59#include <qpainter.h>
60#if QT_CONFIG(animation)
61#include <qpropertyanimation.h>
62#endif
63#include <qstylehints.h>
64#include <qvalidator.h>
65
66QT_BEGIN_NAMESPACE
67
68const int QLineEditPrivate::verticalMargin(1);
69const int QLineEditPrivate::horizontalMargin(2);
70
71// Needs to be kept in sync with QLineEdit::paintEvent
72QRect QLineEditPrivate::adjustedControlRect(const QRect &rect) const
73{
74 QRect widgetRect = !rect.isEmpty() ? rect : q_func()->rect();
75 QRect cr = adjustedContentsRect();
76 int cix = cr.x() - hscroll + horizontalMargin;
77 return widgetRect.translated(p: QPoint(cix, vscroll - control->ascent() + q_func()->fontMetrics().ascent()));
78}
79
80int QLineEditPrivate::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
81{
82 QRect cr = adjustedContentsRect();
83 x-= cr.x() - hscroll + horizontalMargin;
84 return control->xToPos(x, betweenOrOn);
85}
86
87bool QLineEditPrivate::inSelection(int x) const
88{
89 x -= adjustedContentsRect().x() - hscroll + horizontalMargin;
90 return control->inSelection(x);
91}
92
93QRect QLineEditPrivate::cursorRect() const
94{
95 return adjustedControlRect(rect: control->cursorRect());
96}
97
98#if QT_CONFIG(completer)
99
100void QLineEditPrivate::_q_completionHighlighted(const QString &newText)
101{
102 Q_Q(QLineEdit);
103 if (control->completer()->completionMode() != QCompleter::InlineCompletion) {
104 q->setText(newText);
105 } else {
106 int c = control->cursor();
107 QString text = control->text();
108 q->setText(text.leftRef(n: c) + newText.midRef(position: c));
109 control->moveCursor(pos: control->end(), mark: false);
110#ifndef Q_OS_ANDROID
111 const bool mark = true;
112#else
113 const bool mark = (imHints & Qt::ImhNoPredictiveText);
114#endif
115 control->moveCursor(pos: c, mark);
116 }
117}
118
119#endif // QT_CONFIG(completer)
120
121void QLineEditPrivate::_q_handleWindowActivate()
122{
123 Q_Q(QLineEdit);
124 if (!q->hasFocus() && control->hasSelectedText())
125 control->deselect();
126}
127
128void QLineEditPrivate::_q_textEdited(const QString &text)
129{
130 Q_Q(QLineEdit);
131 edited = true;
132 emit q->textEdited(text);
133#if QT_CONFIG(completer)
134 if (control->completer()
135 && control->completer()->completionMode() != QCompleter::InlineCompletion)
136 control->complete(key: -1); // update the popup on cut/paste/del
137#endif
138}
139
140void QLineEditPrivate::_q_cursorPositionChanged(int from, int to)
141{
142 Q_Q(QLineEdit);
143 q->update();
144 emit q->cursorPositionChanged(from, to);
145}
146
147#ifdef QT_KEYPAD_NAVIGATION
148void QLineEditPrivate::_q_editFocusChange(bool e)
149{
150 Q_Q(QLineEdit);
151 q->setEditFocus(e);
152}
153#endif
154
155void QLineEditPrivate::_q_selectionChanged()
156{
157 Q_Q(QLineEdit);
158 if (control->preeditAreaText().isEmpty()) {
159 QStyleOptionFrame opt;
160 q->initStyleOption(option: &opt);
161 bool showCursor = control->hasSelectedText() ?
162 q->style()->styleHint(stylehint: QStyle::SH_BlinkCursorWhenTextSelected, opt: &opt, widget: q):
163 q->hasFocus();
164 setCursorVisible(showCursor);
165 }
166
167 emit q->selectionChanged();
168#ifndef QT_NO_ACCESSIBILITY
169 QAccessibleTextSelectionEvent ev(q, control->selectionStart(), control->selectionEnd());
170 ev.setCursorPosition(control->cursorPosition());
171 QAccessible::updateAccessibility(event: &ev);
172#endif
173}
174
175void QLineEditPrivate::_q_updateNeeded(const QRect &rect)
176{
177 q_func()->update(adjustedControlRect(rect));
178}
179
180void QLineEditPrivate::init(const QString& txt)
181{
182 Q_Q(QLineEdit);
183 control = new QWidgetLineControl(txt);
184 control->setParent(q);
185 control->setFont(q->font());
186 QObject::connect(sender: control, SIGNAL(textChanged(QString)),
187 receiver: q, SIGNAL(textChanged(QString)));
188 QObject::connect(sender: control, SIGNAL(textEdited(QString)),
189 receiver: q, SLOT(_q_textEdited(QString)));
190 QObject::connect(sender: control, SIGNAL(cursorPositionChanged(int,int)),
191 receiver: q, SLOT(_q_cursorPositionChanged(int,int)));
192 QObject::connect(sender: control, SIGNAL(selectionChanged()),
193 receiver: q, SLOT(_q_selectionChanged()));
194 QObject::connect(sender: control, SIGNAL(accepted()),
195 receiver: q, SIGNAL(returnPressed()));
196 QObject::connect(sender: control, SIGNAL(editingFinished()),
197 receiver: q, SIGNAL(editingFinished()));
198#ifdef QT_KEYPAD_NAVIGATION
199 QObject::connect(control, SIGNAL(editFocusChange(bool)),
200 q, SLOT(_q_editFocusChange(bool)));
201#endif
202 QObject::connect(sender: control, SIGNAL(cursorPositionChanged(int,int)),
203 receiver: q, SLOT(updateMicroFocus()));
204
205 QObject::connect(sender: control, SIGNAL(textChanged(QString)),
206 receiver: q, SLOT(updateMicroFocus()));
207
208 QObject::connect(sender: control, SIGNAL(updateMicroFocus()),
209 receiver: q, SLOT(updateMicroFocus()));
210
211 // for now, going completely overboard with updates.
212 QObject::connect(sender: control, SIGNAL(selectionChanged()),
213 receiver: q, SLOT(update()));
214
215 QObject::connect(sender: control, SIGNAL(selectionChanged()),
216 receiver: q, SLOT(updateMicroFocus()));
217
218 QObject::connect(sender: control, SIGNAL(displayTextChanged(QString)),
219 receiver: q, SLOT(update()));
220
221 QObject::connect(sender: control, SIGNAL(updateNeeded(QRect)),
222 receiver: q, SLOT(_q_updateNeeded(QRect)));
223 QObject::connect(sender: control, SIGNAL(inputRejected()), receiver: q, SIGNAL(inputRejected()));
224
225 QStyleOptionFrame opt;
226 q->initStyleOption(option: &opt);
227 control->setPasswordCharacter(q->style()->styleHint(stylehint: QStyle::SH_LineEdit_PasswordCharacter, opt: &opt, widget: q));
228 control->setPasswordMaskDelay(q->style()->styleHint(stylehint: QStyle::SH_LineEdit_PasswordMaskDelay, opt: &opt, widget: q));
229#ifndef QT_NO_CURSOR
230 q->setCursor(Qt::IBeamCursor);
231#endif
232 q->setFocusPolicy(Qt::StrongFocus);
233 q->setAttribute(Qt::WA_InputMethodEnabled);
234 // Specifies that this widget can use more, but is able to survive on
235 // less, horizontal space; and is fixed vertically.
236 q->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::LineEdit));
237 q->setBackgroundRole(QPalette::Base);
238 q->setAttribute(Qt::WA_KeyCompression);
239 q->setMouseTracking(true);
240 q->setAcceptDrops(true);
241
242 q->setAttribute(Qt::WA_MacShowFocusRect);
243
244 initMouseYThreshold();
245}
246
247void QLineEditPrivate::initMouseYThreshold()
248{
249 mouseYThreshold = QGuiApplication::styleHints()->mouseQuickSelectionThreshold();
250}
251
252QRect QLineEditPrivate::adjustedContentsRect() const
253{
254 Q_Q(const QLineEdit);
255 QStyleOptionFrame opt;
256 q->initStyleOption(option: &opt);
257 QRect r = q->style()->subElementRect(subElement: QStyle::SE_LineEditContents, option: &opt, widget: q);
258 r = r.marginsRemoved(margins: effectiveTextMargins());
259 return r;
260}
261
262void QLineEditPrivate::setCursorVisible(bool visible)
263{
264 Q_Q(QLineEdit);
265 if ((bool)cursorVisible == visible)
266 return;
267 cursorVisible = visible;
268 if (control->inputMask().isEmpty())
269 q->update(cursorRect());
270 else
271 q->update();
272}
273
274void QLineEditPrivate::setText(const QString& text)
275{
276 edited = true;
277 control->setText(text);
278}
279
280void QLineEditPrivate::updatePasswordEchoEditing(bool editing)
281{
282 Q_Q(QLineEdit);
283 control->updatePasswordEchoEditing(editing);
284 q->setAttribute(Qt::WA_InputMethodEnabled, on: shouldEnableInputMethod());
285}
286
287void QLineEditPrivate::resetInputMethod()
288{
289 Q_Q(QLineEdit);
290 if (q->hasFocus() && qApp) {
291 QGuiApplication::inputMethod()->reset();
292 }
293}
294
295/*!
296 This function is not intended as polymorphic usage. Just a shared code
297 fragment that calls QInputMethod::invokeAction for this
298 class.
299*/
300bool QLineEditPrivate::sendMouseEventToInputContext( QMouseEvent *e )
301{
302#if !defined QT_NO_IM
303 if ( control->composeMode() ) {
304 int tmp_cursor = xToPos(x: e->pos().x());
305 int mousePos = tmp_cursor - control->cursor();
306 if ( mousePos < 0 || mousePos > control->preeditAreaText().length() )
307 mousePos = -1;
308
309 if (mousePos >= 0) {
310 if (e->type() == QEvent::MouseButtonRelease)
311 QGuiApplication::inputMethod()->invokeAction(a: QInputMethod::Click, cursorPosition: mousePos);
312
313 return true;
314 }
315 }
316#else
317 Q_UNUSED(e);
318#endif
319
320 return false;
321}
322
323#if QT_CONFIG(draganddrop)
324void QLineEditPrivate::drag()
325{
326 Q_Q(QLineEdit);
327 dndTimer.stop();
328 QMimeData *data = new QMimeData;
329 data->setText(control->selectedText());
330 QDrag *drag = new QDrag(q);
331 drag->setMimeData(data);
332 Qt::DropAction action = drag->exec(supportedActions: Qt::CopyAction);
333 if (action == Qt::MoveAction && !control->isReadOnly() && drag->target() != q)
334 control->removeSelection();
335}
336#endif // QT_CONFIG(draganddrop)
337
338
339#if QT_CONFIG(toolbutton)
340QLineEditIconButton::QLineEditIconButton(QWidget *parent)
341 : QToolButton(parent)
342 , m_opacity(0)
343{
344 setFocusPolicy(Qt::NoFocus);
345}
346
347QLineEditPrivate *QLineEditIconButton::lineEditPrivate() const
348{
349 QLineEdit *le = qobject_cast<QLineEdit *>(object: parentWidget());
350 return le ? static_cast<QLineEditPrivate *>(qt_widget_private(widget: le)) : nullptr;
351}
352
353void QLineEditIconButton::paintEvent(QPaintEvent *)
354{
355 QPainter painter(this);
356 QWindow *window = qt_widget_private(widget: this)->windowHandle(mode: QWidgetPrivate::WindowHandleMode::Closest);
357 QIcon::Mode state = QIcon::Disabled;
358 if (isEnabled())
359 state = isDown() ? QIcon::Active : QIcon::Normal;
360 const QLineEditPrivate *lep = lineEditPrivate();
361 const int iconWidth = lep ? lep->sideWidgetParameters().iconSize : 16;
362 const QSize iconSize(iconWidth, iconWidth);
363 const QPixmap iconPixmap = icon().pixmap(window, size: iconSize, mode: state, state: QIcon::Off);
364 QRect pixmapRect = QRect(QPoint(0, 0), iconSize);
365 pixmapRect.moveCenter(p: rect().center());
366 painter.setOpacity(m_opacity);
367 painter.drawPixmap(r: pixmapRect, pm: iconPixmap);
368}
369
370void QLineEditIconButton::actionEvent(QActionEvent *e)
371{
372 switch (e->type()) {
373 case QEvent::ActionChanged: {
374 const QAction *action = e->action();
375 if (isVisibleTo(parentWidget()) != action->isVisible()) {
376 setVisible(action->isVisible());
377 if (QLineEditPrivate *lep = lineEditPrivate())
378 lep->positionSideWidgets();
379 }
380 }
381 break;
382 default:
383 break;
384 }
385 QToolButton::actionEvent(e);
386}
387
388void QLineEditIconButton::setOpacity(qreal value)
389{
390 if (!qFuzzyCompare(p1: m_opacity, p2: value)) {
391 m_opacity = value;
392 updateCursor();
393 update();
394 }
395}
396
397#if QT_CONFIG(animation)
398bool QLineEditIconButton::shouldHideWithText() const
399{
400 return m_hideWithText;
401}
402
403void QLineEditIconButton::setHideWithText(bool hide)
404{
405 m_hideWithText = hide;
406}
407
408void QLineEditIconButton::onAnimationFinished()
409{
410 if (shouldHideWithText() && isVisible() && m_fadingOut) {
411 hide();
412 m_fadingOut = false;
413
414 // Invalidate previous geometry to take into account new size of side widgets
415 if (auto le = lineEditPrivate())
416 le->updateGeometry_helper(forceUpdate: true);
417 }
418}
419
420void QLineEditIconButton::animateShow(bool visible)
421{
422 m_fadingOut = !visible;
423
424 if (shouldHideWithText() && !isVisible()) {
425 show();
426
427 // Invalidate previous geometry to take into account new size of side widgets
428 if (auto le = lineEditPrivate())
429 le->updateGeometry_helper(forceUpdate: true);
430 }
431
432 startOpacityAnimation(endValue: visible ? 1.0 : 0.0);
433}
434
435void QLineEditIconButton::startOpacityAnimation(qreal endValue)
436{
437 QPropertyAnimation *animation = new QPropertyAnimation(this, QByteArrayLiteral("opacity"));
438 connect(sender: animation, signal: &QPropertyAnimation::finished, receiver: this, slot: &QLineEditIconButton::onAnimationFinished);
439
440 animation->setDuration(160);
441 animation->setEndValue(endValue);
442 animation->start(policy: QAbstractAnimation::DeleteWhenStopped);
443}
444#endif
445
446void QLineEditIconButton::updateCursor()
447{
448#ifndef QT_NO_CURSOR
449 setCursor(qFuzzyCompare(p1: m_opacity, p2: qreal(1.0)) || !parentWidget() ? QCursor(Qt::ArrowCursor) : parentWidget()->cursor());
450#endif
451}
452#endif // QT_CONFIG(toolbutton)
453
454#if QT_CONFIG(animation) && QT_CONFIG(toolbutton)
455static void displayWidgets(const QLineEditPrivate::SideWidgetEntryList &widgets, bool display)
456{
457 for (const auto &e : widgets) {
458 if (e.flags & QLineEditPrivate::SideWidgetFadeInWithText)
459 static_cast<QLineEditIconButton *>(e.widget)->animateShow(visible: display);
460 }
461}
462#endif
463
464void QLineEditPrivate::_q_textChanged(const QString &text)
465{
466 if (hasSideWidgets()) {
467 const int newTextSize = text.size();
468 if (!newTextSize || !lastTextSize) {
469 lastTextSize = newTextSize;
470#if QT_CONFIG(animation) && QT_CONFIG(toolbutton)
471 const bool display = newTextSize > 0;
472 displayWidgets(widgets: leadingSideWidgets, display);
473 displayWidgets(widgets: trailingSideWidgets, display);
474#endif
475 }
476 }
477}
478
479void QLineEditPrivate::_q_clearButtonClicked()
480{
481 Q_Q(QLineEdit);
482 if (!q->text().isEmpty()) {
483 q->clear();
484 _q_textEdited(text: QString());
485 }
486}
487
488QLineEditPrivate::SideWidgetParameters QLineEditPrivate::sideWidgetParameters() const
489{
490 Q_Q(const QLineEdit);
491 SideWidgetParameters result;
492 result.iconSize = q->style()->pixelMetric(metric: QStyle::PM_SmallIconSize, option: nullptr, widget: q);
493 result.margin = result.iconSize / 4;
494 result.widgetWidth = result.iconSize + 6;
495 result.widgetHeight = result.iconSize + 2;
496 return result;
497}
498
499QIcon QLineEditPrivate::clearButtonIcon() const
500{
501 Q_Q(const QLineEdit);
502 QStyleOptionFrame styleOption;
503 q->initStyleOption(option: &styleOption);
504 return q->style()->standardIcon(standardIcon: QStyle::SP_LineEditClearButton, option: &styleOption, widget: q);
505}
506
507void QLineEditPrivate::setClearButtonEnabled(bool enabled)
508{
509#if QT_CONFIG(action)
510 for (const SideWidgetEntry &e : trailingSideWidgets) {
511 if (e.flags & SideWidgetClearButton) {
512 e.action->setEnabled(enabled);
513 break;
514 }
515 }
516#else
517 Q_UNUSED(enabled);
518#endif
519}
520
521void QLineEditPrivate::positionSideWidgets()
522{
523 Q_Q(QLineEdit);
524 if (hasSideWidgets()) {
525 const QRect contentRect = q->rect();
526 const SideWidgetParameters p = sideWidgetParameters();
527 const int delta = p.margin + p.widgetWidth;
528 QRect widgetGeometry(QPoint(p.margin, (contentRect.height() - p.widgetHeight) / 2),
529 QSize(p.widgetWidth, p.widgetHeight));
530 for (const SideWidgetEntry &e : leftSideWidgetList()) {
531 e.widget->setGeometry(widgetGeometry);
532#if QT_CONFIG(action)
533 if (e.action->isVisible())
534 widgetGeometry.moveLeft(pos: widgetGeometry.left() + delta);
535#else
536 Q_UNUSED(delta);
537#endif
538 }
539 widgetGeometry.moveLeft(pos: contentRect.width() - p.widgetWidth - p.margin);
540 for (const SideWidgetEntry &e : rightSideWidgetList()) {
541 e.widget->setGeometry(widgetGeometry);
542#if QT_CONFIG(action)
543 if (e.action->isVisible())
544 widgetGeometry.moveLeft(pos: widgetGeometry.left() - delta);
545#endif
546 }
547 }
548}
549
550QLineEditPrivate::SideWidgetLocation QLineEditPrivate::findSideWidget(const QAction *a) const
551{
552 int i = 0;
553 for (const auto &e : leadingSideWidgets) {
554 if (a == e.action)
555 return {.position: QLineEdit::LeadingPosition, .index: i};
556 ++i;
557 }
558 i = 0;
559 for (const auto &e : trailingSideWidgets) {
560 if (a == e.action)
561 return {.position: QLineEdit::TrailingPosition, .index: i};
562 ++i;
563 }
564 return {.position: QLineEdit::LeadingPosition, .index: -1};
565}
566
567QWidget *QLineEditPrivate::addAction(QAction *newAction, QAction *before, QLineEdit::ActionPosition position, int flags)
568{
569 Q_Q(QLineEdit);
570 if (!newAction)
571 return nullptr;
572 if (!hasSideWidgets()) { // initial setup.
573 QObject::connect(sender: q, SIGNAL(textChanged(QString)), receiver: q, SLOT(_q_textChanged(QString)));
574 lastTextSize = q->text().size();
575 }
576 QWidget *w = nullptr;
577 // Store flags about QWidgetAction here since removeAction() may be called from ~QAction,
578 // in which a qobject_cast<> no longer works.
579#if QT_CONFIG(action)
580 if (QWidgetAction *widgetAction = qobject_cast<QWidgetAction *>(object: newAction)) {
581 if ((w = widgetAction->requestWidget(parent: q)))
582 flags |= SideWidgetCreatedByWidgetAction;
583 }
584#endif
585 if (!w) {
586#if QT_CONFIG(toolbutton)
587 QLineEditIconButton *toolButton = new QLineEditIconButton(q);
588 toolButton->setIcon(newAction->icon());
589 toolButton->setOpacity(lastTextSize > 0 || !(flags & SideWidgetFadeInWithText) ? 1 : 0);
590 if (flags & SideWidgetClearButton) {
591 QObject::connect(sender: toolButton, SIGNAL(clicked()), receiver: q, SLOT(_q_clearButtonClicked()));
592
593#if QT_CONFIG(animation)
594 // The clear button is handled only by this widget. The button should be really
595 // shown/hidden in order to calculate size hints correctly.
596 toolButton->setHideWithText(true);
597#endif
598 }
599 toolButton->setDefaultAction(newAction);
600 w = toolButton;
601#else
602 return nullptr;
603#endif
604 }
605
606 // QTBUG-59957: clear button should be the leftmost action.
607 if (!before && !(flags & SideWidgetClearButton) && position == QLineEdit::TrailingPosition) {
608 for (const SideWidgetEntry &e : trailingSideWidgets) {
609 if (e.flags & SideWidgetClearButton) {
610 before = e.action;
611 break;
612 }
613 }
614 }
615
616 // If there is a 'before' action, it takes preference
617
618 // There's a bug in GHS compiler that causes internal error on the following code.
619 // The affected GHS compiler versions are 2016.5.4 and 2017.1. GHS internal reference
620 // to track the progress of this issue is TOOLS-26637.
621 // This temporary workaround allows to compile with GHS toolchain and should be
622 // removed when GHS provides a patch to fix the compiler issue.
623
624#if defined(Q_CC_GHS)
625 const SideWidgetLocation loc = {position, -1};
626 const auto location = before ? findSideWidget(before) : loc;
627#else
628 const auto location = before ? findSideWidget(a: before) : SideWidgetLocation{.position: position, .index: -1};
629#endif
630
631 SideWidgetEntryList &list = location.position == QLineEdit::TrailingPosition ? trailingSideWidgets : leadingSideWidgets;
632 list.insert(position: location.isValid() ? list.begin() + location.index : list.end(),
633 x: SideWidgetEntry(w, newAction, flags));
634 positionSideWidgets();
635 w->show();
636 return w;
637}
638
639void QLineEditPrivate::removeAction(QAction *action)
640{
641#if QT_CONFIG(action)
642 Q_Q(QLineEdit);
643 const auto location = findSideWidget(a: action);
644 if (!location.isValid())
645 return;
646 SideWidgetEntryList &list = location.position == QLineEdit::TrailingPosition ? trailingSideWidgets : leadingSideWidgets;
647 SideWidgetEntry entry = list[location.index];
648 list.erase(position: list.begin() + location.index);
649 if (entry.flags & SideWidgetCreatedByWidgetAction)
650 static_cast<QWidgetAction *>(entry.action)->releaseWidget(widget: entry.widget);
651 else
652 delete entry.widget;
653 positionSideWidgets();
654 if (!hasSideWidgets()) // Last widget, remove connection
655 QObject::disconnect(sender: q, SIGNAL(textChanged(QString)), receiver: q, SLOT(_q_textChanged(QString)));
656 q->update();
657#else
658 Q_UNUSED(action);
659#endif // QT_CONFIG(action)
660}
661
662static int effectiveTextMargin(int defaultMargin, const QLineEditPrivate::SideWidgetEntryList &widgets,
663 const QLineEditPrivate::SideWidgetParameters &parameters)
664{
665 if (widgets.empty())
666 return defaultMargin;
667
668 const auto visibleSideWidgetCount = std::count_if(first: widgets.begin(), last: widgets.end(),
669 pred: [](const QLineEditPrivate::SideWidgetEntry &e) {
670#if QT_CONFIG(animation)
671 // a button that's fading out doesn't get any space
672 if (auto* iconButton = qobject_cast<QLineEditIconButton*>(object: e.widget))
673 return iconButton->needsSpace();
674
675#endif
676 return e.widget->isVisibleTo(e.widget->parentWidget());
677 });
678
679 return defaultMargin + (parameters.margin + parameters.widgetWidth) * visibleSideWidgetCount;
680}
681
682QMargins QLineEditPrivate::effectiveTextMargins() const
683{
684 return {effectiveTextMargin(defaultMargin: textMargins.left(), widgets: leftSideWidgetList(), parameters: sideWidgetParameters()),
685 textMargins.top(),
686 effectiveTextMargin(defaultMargin: textMargins.right(), widgets: rightSideWidgetList(), parameters: sideWidgetParameters()),
687 textMargins.bottom()};
688}
689
690
691QT_END_NAMESPACE
692
693#include "moc_qlineedit_p.cpp"
694

source code of qtbase/src/widgets/widgets/qlineedit_p.cpp