Qt Utilities  6.4.1
Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models
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 
17 namespace 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 
65 ButtonOverlay::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 
81 {
82 }
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 
157 void 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 
209 void ButtonOverlay::addCustomButton(QWidget *button)
210 {
211  fallbackToUsingCustomLayout();
212  m_buttonLayout->addWidget(button);
213 }
214 
223 void 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 
244 void ButtonOverlay::addCustomAction(QAction *action)
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 
256 void 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 
296 {
297 }
298 
306 {
307 }
308 
314 void 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())) {
329  setClearButtonEnabled(false);
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) {
353  setClearButtonEnabled(true);
354  }
355  if (iconAction) {
356  enableInfoButton(infoPixmap, infoText);
357  }
358  for (auto *const action : actions) {
359  addCustomAction(action);
360  }
361 }
362 
367 QLineEdit *ButtonOverlay::lineEditForWidget() const
368 {
369  return isUsingCustomLayout() ? nullptr : static_cast<QLineEdit *>(m_buttonWidget);
370 }
371 
378 {
379  return false;
380 }
381 
391 void 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 
413 void 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.
Definition: iconbutton.cpp:41