From ee7539cf6ec46a8afc69038c8542d13e0efbd929 Mon Sep 17 00:00:00 2001 From: Martchus Date: Tue, 9 Jun 2020 23:24:24 +0200 Subject: [PATCH] Allow adding custom actions to button overlay Of course it would is possible to add actions directly to the line edit. These new helper functions have the advantage that they provide a fallback to the old button overlay implementation in case line edit actions can not be used. --- widgets/buttonoverlay.cpp | 77 ++++++++++++++++++++++++++++++--------- widgets/buttonoverlay.h | 4 ++ widgets/iconbutton.cpp | 45 +++++++++++++++++++++++ widgets/iconbutton.h | 8 ++++ 4 files changed, 117 insertions(+), 17 deletions(-) diff --git a/widgets/buttonoverlay.cpp b/widgets/buttonoverlay.cpp index fecd571..ce2217d 100644 --- a/widgets/buttonoverlay.cpp +++ b/widgets/buttonoverlay.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -227,17 +228,52 @@ void ButtonOverlay::insertCustomButton(int index, QWidget *button) } /*! - * \brief Removes the specified custom \a button. + * \brief Removes the specified custom \a button; does nothing if \a button has not been added. * * The ownership of widget remains the same as when it was added. - * - * \remarks This function enforces the "custom approach" mentioned in the class documentation - * and should therefore be avoided. */ void ButtonOverlay::removeCustomButton(QWidget *button) { - fallbackToUsingCustomLayout(); - m_buttonLayout->removeWidget(button); + if (isUsingCustomLayout()) { + m_buttonLayout->removeWidget(button); + } +} + +/*! + * \brief Adds a custom \a action. + */ +void ButtonOverlay::addCustomAction(QAction *action) +{ + if (auto *const le = lineEditForWidget()) { + le->addAction(action, QLineEdit::TrailingPosition); + } else { + addCustomButton(IconButton::fromAction(action, reinterpret_cast(this))); + } +} + +/*! + * \brief Inserts a custom \a action at the specified \a index. + */ +void ButtonOverlay::insertCustomAction(int index, QAction *action) +{ + if (auto *const le = lineEditForWidget()) { + const auto actions = le->actions(); + le->insertAction(index < actions.size() ? actions[index] : nullptr, action); + } else { + insertCustomButton(index, IconButton::fromAction(action, reinterpret_cast(this))); + } +} + +/*! + * \brief Removes the specified custom \a action; does nothing if \a action has not been added. + */ +void ButtonOverlay::removeCustomAction(QAction *action) +{ + if (auto *const le = lineEditForWidget()) { + le->removeAction(action); + } else { + removeCustomButton(IconButton::fromAction(action, reinterpret_cast(this))); + } } /*! @@ -284,19 +320,23 @@ void ButtonOverlay::fallbackToUsingCustomLayout() } // disable QLineEdit's clear button and actions; save configuration - const auto clearButtonEnabled = isClearButtonEnabled(); - if (clearButtonEnabled) { - setClearButtonEnabled(false); - } - auto *const iconAction = static_cast(m_infoButtonOrAction); + auto clearButtonEnabled = false; + auto *iconAction = static_cast(m_infoButtonOrAction); QPixmap infoPixmap; QString infoText; - if (iconAction) { - const auto icon = iconAction->icon(); - const auto sizes = icon.availableSizes(); - infoPixmap = icon.pixmap(sizes.empty() ? QSize(16, 16) : sizes.front()); - infoText = iconAction->toolTip(); - disableInfoButton(); + QList actions; + if (auto const *le = lineEditForWidget()) { + if ((clearButtonEnabled = le->isClearButtonEnabled())) { + setClearButtonEnabled(false); + } + if ((iconAction = static_cast(m_infoButtonOrAction))) { + const auto icon = iconAction->icon(); + const auto sizes = icon.availableSizes(); + infoPixmap = icon.pixmap(sizes.empty() ? QSize(16, 16) : sizes.front()); + infoText = iconAction->toolTip(); + disableInfoButton(); + } + actions = le->actions(); } // initialize custom layout @@ -313,6 +353,9 @@ void ButtonOverlay::fallbackToUsingCustomLayout() if (iconAction) { enableInfoButton(infoPixmap, infoText); } + for (auto *const action : actions) { + addCustomAction(action); + } } /*! diff --git a/widgets/buttonoverlay.h b/widgets/buttonoverlay.h index 08cb54c..49f7aa6 100644 --- a/widgets/buttonoverlay.h +++ b/widgets/buttonoverlay.h @@ -5,6 +5,7 @@ #include +QT_FORWARD_DECLARE_CLASS(QAction) QT_FORWARD_DECLARE_CLASS(QWidget) QT_FORWARD_DECLARE_CLASS(QHBoxLayout) QT_FORWARD_DECLARE_CLASS(QString) @@ -43,6 +44,9 @@ public: void addCustomButton(QWidget *button); void insertCustomButton(int index, QWidget *button); void removeCustomButton(QWidget *button); + void addCustomAction(QAction *action); + void insertCustomAction(int index, QAction *action); + void removeCustomAction(QAction *action); virtual bool isCleared() const; protected: diff --git a/widgets/iconbutton.cpp b/widgets/iconbutton.cpp index 3a983dc..54cd043 100644 --- a/widgets/iconbutton.cpp +++ b/widgets/iconbutton.cpp @@ -1,10 +1,14 @@ #include "./iconbutton.h" +#include + #include #include #include #include +using namespace CppUtilities; + namespace QtUtilities { /*! @@ -29,6 +33,46 @@ IconButton::~IconButton() { } +/*! + * \brief Creates an IconButton for the specified \a action. + * \remarks Calling this function on the same action twice with the same \a id yields the + * same instance. + */ +IconButton *IconButton::fromAction(QAction *action, std::uintptr_t id) +{ + const auto propertyName = argsToString("iconButton-", id); + const auto existingIconButton = action->property(propertyName.data()); + if (!existingIconButton.isNull()) { + return existingIconButton.value(); + } + auto *const iconButton = new IconButton; + iconButton->assignDataFromAction(action); + action->setProperty(propertyName.data(), QVariant::fromValue(iconButton)); + connect(action, &QAction::changed, iconButton, &IconButton::assignDataFromActionChangedSignal); + connect(iconButton, &IconButton::clicked, action, &QAction::trigger); + return iconButton; +} + +/*! + * \brief Internally called to assign data from a QAction to the icon button. + */ +void IconButton::assignDataFromActionChangedSignal() +{ + assignDataFromAction(qobject_cast(QObject::sender())); +} + +/*! + * \brief Internally called to assign data from a QAction to the icon button. + */ +void IconButton::assignDataFromAction(const QAction *action) +{ + auto const icon = action->icon(); + const auto sizes = icon.availableSizes(); + const auto text = action->text(); + setPixmap(icon.pixmap(sizes.empty() ? QSize(16, 16) : sizes.front())); + setToolTip(text.isEmpty() ? action->toolTip() : text); +} + QSize IconButton::sizeHint() const { #if QT_VERSION >= 0x050100 @@ -77,4 +121,5 @@ void IconButton::keyReleaseEvent(QKeyEvent *event) QAbstractButton::keyReleaseEvent(event); event->accept(); } + } // namespace QtUtilities diff --git a/widgets/iconbutton.h b/widgets/iconbutton.h index fbb04c9..68ceb5a 100644 --- a/widgets/iconbutton.h +++ b/widgets/iconbutton.h @@ -4,8 +4,11 @@ #include "../global.h" #include +#include #include +#include + namespace QtUtilities { class QT_UTILITIES_EXPORT IconButton : public QAbstractButton { @@ -16,6 +19,7 @@ public: explicit IconButton(QWidget *parent = nullptr); ~IconButton() override; + static IconButton *fromAction(QAction *action, std::uintptr_t id = 0); const QPixmap &pixmap() const; void setPixmap(const QPixmap &pixmap); QSize sizeHint() const override; @@ -25,6 +29,10 @@ protected: void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; +private Q_SLOTS: + void assignDataFromActionChangedSignal(); + void assignDataFromAction(const QAction *action); + private: QPixmap m_pixmap; };