Improve button overlay so buttons will not be shown over e.g. spin box buttons

* Unify/streamline code
* Use Qt::SC_ComboBoxEditField/Qt::SC_SpinBoxEditField to determine the edit
  area for better compatibility accorss various styles
This commit is contained in:
Martchus 2020-06-04 19:13:14 +02:00
parent 9b7ad3375c
commit 7db2fd02fc
7 changed files with 50 additions and 23 deletions

View File

@ -10,7 +10,7 @@ set(META_APP_DESCRIPTION
"Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models") "Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models")
set(META_VERSION_MAJOR 6) set(META_VERSION_MAJOR 6)
set(META_VERSION_MINOR 0) set(META_VERSION_MINOR 0)
set(META_VERSION_PATCH 6) set(META_VERSION_PATCH 7)
set(META_APP_VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}) set(META_APP_VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH})
project(${META_PROJECT_NAME}) project(${META_PROJECT_NAME})

View File

@ -37,15 +37,7 @@ ButtonOverlay::ButtonOverlay(QWidget *widget)
, m_clearButton(nullptr) , m_clearButton(nullptr)
, m_infoButton(nullptr) , m_infoButton(nullptr)
{ {
// setup button widget and layout buttonLayout()->setAlignment(Qt::AlignCenter | Qt::AlignRight);
const QMargins margins = widget->contentsMargins();
QStyleOption opt;
opt.initFrom(m_widget);
const int frameWidth = widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, m_widget);
const int pad = 2;
m_buttonLayout->setContentsMargins(
margins.left() + frameWidth + pad, margins.top() + frameWidth, margins.right() + frameWidth + pad, margins.bottom() + frameWidth);
m_buttonLayout->setAlignment(Qt::AlignCenter | Qt::AlignRight);
widget->setLayout(m_buttonLayout); widget->setLayout(m_buttonLayout);
} }
@ -187,4 +179,17 @@ void ButtonOverlay::showInfo()
QToolTip::showText(QCursor::pos(), m_infoButton->toolTip(), m_infoButton); QToolTip::showText(QCursor::pos(), m_infoButton->toolTip(), m_infoButton);
} }
} }
/*!
* \brief Sets the contents margins of the button layout so the overlay buttons will only be shown over the \a editFieldRect and
* not interfere with e.g. spin box buttons.
*/
void ButtonOverlay::setContentsMarginsFromEditFieldRectAndFrameWidth(const QRect &editFieldRect, int frameWidth, int padding)
{
const auto margins = m_widget->contentsMargins();
const auto buttonWidth = m_widget->width() - editFieldRect.width();
buttonLayout()->setContentsMargins(margins.left() + frameWidth + padding, margins.top() + frameWidth,
margins.right() + frameWidth + padding + buttonWidth, margins.bottom() + frameWidth);
}
} // namespace QtUtilities } // namespace QtUtilities

View File

@ -9,12 +9,24 @@ QT_FORWARD_DECLARE_CLASS(QWidget)
QT_FORWARD_DECLARE_CLASS(QHBoxLayout) QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
QT_FORWARD_DECLARE_CLASS(QString) QT_FORWARD_DECLARE_CLASS(QString)
QT_FORWARD_DECLARE_CLASS(QPixmap) QT_FORWARD_DECLARE_CLASS(QPixmap)
QT_FORWARD_DECLARE_CLASS(QMargins)
QT_FORWARD_DECLARE_CLASS(QRect)
namespace QtUtilities { namespace QtUtilities {
class IconButton; class IconButton;
class ClearComboBox;
class ClearSpinBox;
class ClearPlainTextEdit;
class ClearLineEdit;
class QT_UTILITIES_EXPORT ButtonOverlay { class QT_UTILITIES_EXPORT ButtonOverlay {
// allow these derived classes to use private helpers provided by ButtonOverlay
friend class ClearComboBox;
friend class ClearSpinBox;
friend class ClearPlainTextEdit;
friend class ClearLineEdit;
public: public:
explicit ButtonOverlay(QWidget *widget); explicit ButtonOverlay(QWidget *widget);
virtual ~ButtonOverlay(); virtual ~ButtonOverlay();
@ -36,6 +48,7 @@ protected:
private: private:
void showInfo(); void showInfo();
void setContentsMarginsFromEditFieldRectAndFrameWidth(const QRect &editFieldRect, int frameWidth, int padding = 0);
QWidget *m_widget; QWidget *m_widget;
QWidget *m_buttonWidget; QWidget *m_buttonWidget;

View File

@ -18,14 +18,11 @@ ClearComboBox::ClearComboBox(QWidget *parent)
: QComboBox(parent) : QComboBox(parent)
, ButtonOverlay(this) , ButtonOverlay(this)
{ {
const QMargins margins = contentsMargins(); const QStyle *const s = style();
QStyleOptionComboBox opt; QStyleOptionComboBox opt;
opt.initFrom(this); opt.initFrom(this);
const int frameWidth = style()->pixelMetric(QStyle::PM_ComboBoxFrameWidth, &opt, this); setContentsMarginsFromEditFieldRectAndFrameWidth(
const int pad = 2; s->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this), s->pixelMetric(QStyle::PM_ComboBoxFrameWidth, &opt, this));
const int buttonWidth = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow, this).width();
buttonLayout()->setContentsMargins(margins.left() + frameWidth + pad, margins.top() + frameWidth,
margins.right() + frameWidth + pad + buttonWidth, margins.bottom() + frameWidth);
setClearButtonEnabled(isEditable()); setClearButtonEnabled(isEditable());
connect(this, &ClearComboBox::currentTextChanged, this, &ClearComboBox::handleTextChanged); connect(this, &ClearComboBox::currentTextChanged, this, &ClearComboBox::handleTextChanged);
} }

View File

@ -1,5 +1,8 @@
#include "./clearlineedit.h" #include "./clearlineedit.h"
#include <QStyle>
#include <QStyleOptionFrame>
namespace QtUtilities { namespace QtUtilities {
/*! /*!
@ -14,6 +17,11 @@ ClearLineEdit::ClearLineEdit(QWidget *parent)
: QLineEdit(parent) : QLineEdit(parent)
, ButtonOverlay(this) , ButtonOverlay(this)
{ {
const QStyle *const s = style();
QStyleOptionFrame opt;
opt.initFrom(this);
setContentsMarginsFromEditFieldRectAndFrameWidth(s->subElementRect(QStyle::SE_LineEditContents, &opt, this),
s->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, m_widget), s->pixelMetric(QStyle::PM_LayoutVerticalSpacing, &opt, m_widget));
ButtonOverlay::setClearButtonEnabled(true); ButtonOverlay::setClearButtonEnabled(true);
connect(this, &ClearLineEdit::textChanged, this, &ClearLineEdit::handleTextChanged); connect(this, &ClearLineEdit::textChanged, this, &ClearLineEdit::handleTextChanged);
} }

View File

@ -2,6 +2,8 @@
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QScrollBar> #include <QScrollBar>
#include <QStyle>
#include <QStyleOptionFrame>
using namespace std; using namespace std;
@ -21,6 +23,11 @@ ClearPlainTextEdit::ClearPlainTextEdit(QWidget *parent)
{ {
// set alignment to show buttons in the bottom right corner // set alignment to show buttons in the bottom right corner
ButtonOverlay::buttonLayout()->setAlignment(Qt::AlignBottom | Qt::AlignRight); ButtonOverlay::buttonLayout()->setAlignment(Qt::AlignBottom | Qt::AlignRight);
const QStyle *const s = style();
QStyleOptionFrame opt;
opt.initFrom(this);
setContentsMarginsFromEditFieldRectAndFrameWidth(s->subElementRect(QStyle::SE_FrameContents, &opt, this),
s->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, m_widget), s->pixelMetric(QStyle::PM_LayoutVerticalSpacing, &opt, m_widget));
ButtonOverlay::setClearButtonEnabled(true); ButtonOverlay::setClearButtonEnabled(true);
connect(this, &QPlainTextEdit::textChanged, this, &ClearPlainTextEdit::handleTextChanged); connect(this, &QPlainTextEdit::textChanged, this, &ClearPlainTextEdit::handleTextChanged);
// ensure button layout is realigned when scrolling // ensure button layout is realigned when scrolling

View File

@ -21,14 +21,11 @@ ClearSpinBox::ClearSpinBox(QWidget *parent)
, ButtonOverlay(this) , ButtonOverlay(this)
, m_minimumHidden(false) , m_minimumHidden(false)
{ {
const QMargins margins = contentsMargins(); const QStyle *const s = style();
QStyleOptionComboBox opt; QStyleOptionSpinBox opt;
opt.initFrom(this); opt.initFrom(this);
const int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth, &opt, this); setContentsMarginsFromEditFieldRectAndFrameWidth(
const int pad = 5; s->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxEditField, this), s->pixelMetric(QStyle::PM_SpinBoxFrameWidth, &opt, this));
const int buttonWidth = style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxUp, this).width() + 10;
buttonLayout()->setContentsMargins(margins.left() + frameWidth + pad, margins.top() + frameWidth,
margins.right() + frameWidth + pad + buttonWidth, margins.bottom() + frameWidth);
setClearButtonEnabled(true); setClearButtonEnabled(true);
connect(this, static_cast<void (ClearSpinBox::*)(int)>(&ClearSpinBox::valueChanged), this, &ClearSpinBox::handleValueChanged); connect(this, static_cast<void (ClearSpinBox::*)(int)>(&ClearSpinBox::valueChanged), this, &ClearSpinBox::handleValueChanged);
} }