Qt Utilities 6.14.0
Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models
Loading...
Searching...
No Matches
buttonoverlay.cpp
Go to the documentation of this file.
1#include "./buttonoverlay.h"
2#include "./iconbutton.h"
3
4#include <QAction>
5#include <QComboBox>
6#include <QCursor>
7#include <QHBoxLayout>
8#include <QIcon>
9#include <QLineEdit>
10#include <QStyle>
11#include <QStyleOption>
12#include <QToolTip>
13#include <QWidget>
14
15#include <functional>
16
17namespace QtUtilities {
18
50 : m_widget(widget)
51 , m_buttonWidget(nullptr)
52 , m_buttonLayout(nullptr)
53 , m_clearButton(nullptr)
54 , m_infoButtonOrAction(nullptr)
55{
56 fallbackToUsingCustomLayout();
57}
58
65ButtonOverlay::ButtonOverlay(QWidget *widget, QLineEdit *lineEdit)
66 : m_widget(widget)
67 , m_buttonWidget(lineEdit)
68 , m_buttonLayout(nullptr)
69 , m_clearButton(nullptr)
70 , m_infoButtonOrAction(nullptr)
71{
72 if (!m_buttonWidget) {
73 fallbackToUsingCustomLayout();
74 }
75}
76
83
88{
89 return m_buttonLayout != nullptr;
90}
91
98{
99 fallbackToUsingCustomLayout();
100 return m_buttonLayout;
101}
102
107{
108 if (isUsingCustomLayout()) {
109 return m_clearButton != nullptr;
110 }
111 return lineEditForWidget()->isClearButtonEnabled();
112}
113
118{
119 return m_infoButtonOrAction != nullptr;
120}
121
126{
127 if (auto *const le = lineEditForWidget()) {
128 le->setClearButtonEnabled(enabled);
129 return;
130 }
131 const auto clearButtonEnabled = isClearButtonEnabled();
132 if (clearButtonEnabled && !enabled) {
133 // disable clear button
134 m_buttonLayout->removeWidget(m_clearButton);
135 delete m_clearButton;
136 m_clearButton = nullptr;
137 } else if (!clearButtonEnabled && enabled) {
138 // enable clear button
139 m_clearButton = new IconButton;
140 m_clearButton->setHidden(isCleared());
141 m_clearButton->setPixmap(QIcon::fromTheme(QStringLiteral("edit-clear")).pixmap(IconButton::defaultPixmapSize));
142 m_clearButton->setGeometry(QRect(QPoint(), IconButton::defaultPixmapSize));
143 m_clearButton->setToolTip(QObject::tr("Clear"));
144 QObject::connect(m_clearButton, &IconButton::clicked, std::bind(&ButtonOverlay::handleClearButtonClicked, this));
145 m_buttonLayout->addWidget(m_clearButton);
146 }
147}
148
157void ButtonOverlay::enableInfoButton(const QPixmap &pixmap, const QString &infoText)
158{
159 if (auto *const le = lineEditForWidget()) {
161 auto *const action = le->addAction(QIcon(pixmap), QLineEdit::TrailingPosition);
162 action->setToolTip(infoText);
163 QObject::connect(action, &QAction::triggered, std::bind(&ButtonOverlay::showInfo, this));
164 m_infoButtonOrAction = action;
165 return;
166 }
167 auto *infoButton = static_cast<IconButton *>(m_infoButtonOrAction);
168 if (!infoButton) {
169 m_infoButtonOrAction = infoButton = new IconButton;
170 infoButton->setGeometry(QRect(QPoint(), IconButton::defaultPixmapSize));
171 if (m_clearButton) {
172 m_buttonLayout->insertWidget(m_buttonLayout->count() - 2, infoButton);
173 } else {
174 m_buttonLayout->addWidget(infoButton);
175 }
176 }
177 infoButton->setPixmap(pixmap);
178 infoButton->setToolTip(infoText);
179}
180
186{
187 if (auto *const le = lineEditForWidget()) {
188 if (auto *const infoAction = static_cast<QAction *>(m_infoButtonOrAction)) {
189 le->removeAction(infoAction);
190 m_infoButtonOrAction = nullptr;
191 }
192 return;
193 }
194 if (auto *infoButton = static_cast<IconButton *>(m_infoButtonOrAction)) {
195 m_buttonLayout->removeWidget(infoButton);
196 delete infoButton;
197 m_infoButtonOrAction = nullptr;
198 }
199}
200
210{
211 fallbackToUsingCustomLayout();
212 m_buttonLayout->addWidget(button);
213}
214
223void ButtonOverlay::insertCustomButton(int index, QWidget *button)
224{
225 fallbackToUsingCustomLayout();
226 m_buttonLayout->insertWidget(index, button);
227}
228
235{
236 if (isUsingCustomLayout()) {
237 m_buttonLayout->removeWidget(button);
238 }
239}
240
245{
246 if (auto *const le = lineEditForWidget()) {
247 le->addAction(action, QLineEdit::TrailingPosition);
248 } else {
249 addCustomButton(IconButton::fromAction(action, reinterpret_cast<std::uintptr_t>(this)));
250 }
251}
252
256void ButtonOverlay::insertCustomAction(int index, QAction *action)
257{
258 if (auto *const le = lineEditForWidget()) {
259 const auto actions = le->actions();
260 le->insertAction(index < actions.size() ? actions[index] : nullptr, action);
261 } else {
262 insertCustomButton(index, IconButton::fromAction(action, reinterpret_cast<std::uintptr_t>(this)));
263 }
264}
265
270{
271 if (auto *const le = lineEditForWidget()) {
272 le->removeAction(action);
273 } else {
274 removeCustomButton(IconButton::fromAction(action, reinterpret_cast<std::uintptr_t>(this)));
275 }
276}
277
284{
285 if (m_clearButton) {
286 m_clearButton->setVisible(visible);
287 }
288}
289
298
308
314void ButtonOverlay::fallbackToUsingCustomLayout()
315{
316 // skip if custom layout is already used
317 if (isUsingCustomLayout()) {
318 return;
319 }
320
321 // disable QLineEdit's clear button and actions; save configuration
322 auto clearButtonEnabled = false;
323 auto *iconAction = static_cast<QAction *>(m_infoButtonOrAction);
324 QPixmap infoPixmap;
325 QString infoText;
326 QList<QAction *> actions;
327 if (auto *const le = lineEditForWidget()) {
328 if ((clearButtonEnabled = le->isClearButtonEnabled())) {
330 }
331 if ((iconAction = static_cast<QAction *>(m_infoButtonOrAction))) {
332 const auto icon = iconAction->icon();
333 const auto sizes = icon.availableSizes();
334 infoPixmap = icon.pixmap(sizes.empty() ? IconButton::defaultPixmapSize : sizes.front());
335 infoText = iconAction->toolTip();
337 }
338 actions = le->actions();
339 for (auto *const action : actions) {
340 le->removeAction(action);
341 }
342 }
343
344 // initialize custom layout
345 m_buttonLayout = new QHBoxLayout(m_buttonWidget);
346 m_buttonWidget = new QWidget(m_widget);
347 m_buttonLayout->setAlignment(Qt::AlignCenter | Qt::AlignRight);
348 m_widget->setLayout(m_buttonLayout);
350
351 // restore old configuration
352 if (clearButtonEnabled) {
354 }
355 if (iconAction) {
356 enableInfoButton(infoPixmap, infoText);
357 }
358 for (auto *const action : actions) {
359 addCustomAction(action);
360 }
361}
362
367QLineEdit *ButtonOverlay::lineEditForWidget() const
368{
369 return isUsingCustomLayout() ? nullptr : static_cast<QLineEdit *>(m_buttonWidget);
370}
371
378{
379 return false;
380}
381
391void ButtonOverlay::showInfo()
392{
393 if (auto const *const le = lineEditForWidget()) {
394 if (auto *const infoAction = static_cast<QAction *>(m_infoButtonOrAction)) {
395 const auto pos = QCursor::pos();
396 if (!pos.isNull()) {
397 QToolTip::showText(pos, infoAction->toolTip(), m_widget);
398 }
399 }
400 return;
401 }
402 if (auto *const infoButton = static_cast<IconButton *>(m_infoButtonOrAction)) {
403 QToolTip::showText(infoButton->mapToGlobal(infoButton->rect().center()), infoButton->toolTip(), infoButton);
404 }
405}
406
413void ButtonOverlay::setContentsMarginsFromEditFieldRectAndFrameWidth(const QRect &editFieldRect, int frameWidth, int padding)
414{
415 const auto margins = m_widget->contentsMargins();
416 const auto buttonWidth = m_widget->width() - editFieldRect.width();
417 buttonLayout()->setContentsMargins(margins.left() + frameWidth + padding, margins.top() + frameWidth,
418 margins.right() + frameWidth + padding + buttonWidth, margins.bottom() + frameWidth);
419}
420
421} // namespace QtUtilities
virtual bool isCleared() const
Returns whether the related widget is cleared.
void updateClearButtonVisibility(bool visible)
Updates the visibility of the clear button.
QHBoxLayout * buttonLayout()
Returns the layout manager holding the buttons.
virtual void handleCustomLayoutCreated()
Applies additional handling when the button layout has been created.
ButtonOverlay(QWidget *widget)
Constructs a button overlay for the specified widget.
void insertCustomAction(int index, QAction *action)
Inserts a custom action at the specified index.
bool isInfoButtonEnabled() const
Returns whether the info button is enabled.
void enableInfoButton(const QPixmap &pixmap, const QString &infoText)
Shows an info button with the specified pixmap and infoText.
void removeCustomButton(QWidget *button)
Removes the specified custom button; does nothing if button has not been added.
bool isClearButtonEnabled() const
Returns whether the clear button is enabled.
virtual void handleClearButtonClicked()
Clears the related widget.
void insertCustomButton(int index, QWidget *button)
Inserts a custom button at the specified index.
bool isUsingCustomLayout() const
Returns whether the "custom approach" mentioned in the class documentation is used.
void addCustomButton(QWidget *button)
Adds a custom button.
virtual ~ButtonOverlay()
Destroys the button overlay.
void removeCustomAction(QAction *action)
Removes the specified custom action; does nothing if action has not been added.
void disableInfoButton()
Hides an info button if one is shown.
void addCustomAction(QAction *action)
Adds a custom action.
void setClearButtonEnabled(bool enabled)
Sets whether the clear button is enabled.
A simple QAbstractButton implementation displaying a QPixmap.
Definition iconbutton.h:15
void setPixmap(const QPixmap &pixmap)
Sets the pixmap.
Definition iconbutton.h:54
static constexpr auto defaultPixmapSize
Definition iconbutton.h:28
static IconButton * fromAction(QAction *action, std::uintptr_t id=0)
Creates an IconButton for the specified action.