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")
set(META_VERSION_MAJOR 6)
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})
project(${META_PROJECT_NAME})

View File

@ -37,15 +37,7 @@ ButtonOverlay::ButtonOverlay(QWidget *widget)
, m_clearButton(nullptr)
, m_infoButton(nullptr)
{
// setup button widget and layout
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);
buttonLayout()->setAlignment(Qt::AlignCenter | Qt::AlignRight);
widget->setLayout(m_buttonLayout);
}
@ -187,4 +179,17 @@ void ButtonOverlay::showInfo()
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

View File

@ -9,12 +9,24 @@ QT_FORWARD_DECLARE_CLASS(QWidget)
QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
QT_FORWARD_DECLARE_CLASS(QString)
QT_FORWARD_DECLARE_CLASS(QPixmap)
QT_FORWARD_DECLARE_CLASS(QMargins)
QT_FORWARD_DECLARE_CLASS(QRect)
namespace QtUtilities {
class IconButton;
class ClearComboBox;
class ClearSpinBox;
class ClearPlainTextEdit;
class ClearLineEdit;
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:
explicit ButtonOverlay(QWidget *widget);
virtual ~ButtonOverlay();
@ -36,6 +48,7 @@ protected:
private:
void showInfo();
void setContentsMarginsFromEditFieldRectAndFrameWidth(const QRect &editFieldRect, int frameWidth, int padding = 0);
QWidget *m_widget;
QWidget *m_buttonWidget;

View File

@ -18,14 +18,11 @@ ClearComboBox::ClearComboBox(QWidget *parent)
: QComboBox(parent)
, ButtonOverlay(this)
{
const QMargins margins = contentsMargins();
const QStyle *const s = style();
QStyleOptionComboBox opt;
opt.initFrom(this);
const int frameWidth = style()->pixelMetric(QStyle::PM_ComboBoxFrameWidth, &opt, this);
const int pad = 2;
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);
setContentsMarginsFromEditFieldRectAndFrameWidth(
s->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this), s->pixelMetric(QStyle::PM_ComboBoxFrameWidth, &opt, this));
setClearButtonEnabled(isEditable());
connect(this, &ClearComboBox::currentTextChanged, this, &ClearComboBox::handleTextChanged);
}

View File

@ -1,5 +1,8 @@
#include "./clearlineedit.h"
#include <QStyle>
#include <QStyleOptionFrame>
namespace QtUtilities {
/*!
@ -14,6 +17,11 @@ ClearLineEdit::ClearLineEdit(QWidget *parent)
: QLineEdit(parent)
, 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);
connect(this, &ClearLineEdit::textChanged, this, &ClearLineEdit::handleTextChanged);
}

View File

@ -2,6 +2,8 @@
#include <QHBoxLayout>
#include <QScrollBar>
#include <QStyle>
#include <QStyleOptionFrame>
using namespace std;
@ -21,6 +23,11 @@ ClearPlainTextEdit::ClearPlainTextEdit(QWidget *parent)
{
// set alignment to show buttons in the bottom right corner
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);
connect(this, &QPlainTextEdit::textChanged, this, &ClearPlainTextEdit::handleTextChanged);
// ensure button layout is realigned when scrolling

View File

@ -21,14 +21,11 @@ ClearSpinBox::ClearSpinBox(QWidget *parent)
, ButtonOverlay(this)
, m_minimumHidden(false)
{
const QMargins margins = contentsMargins();
QStyleOptionComboBox opt;
const QStyle *const s = style();
QStyleOptionSpinBox opt;
opt.initFrom(this);
const int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth, &opt, this);
const int pad = 5;
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);
setContentsMarginsFromEditFieldRectAndFrameWidth(
s->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxEditField, this), s->pixelMetric(QStyle::PM_SpinBoxFrameWidth, &opt, this));
setClearButtonEnabled(true);
connect(this, static_cast<void (ClearSpinBox::*)(int)>(&ClearSpinBox::valueChanged), this, &ClearSpinBox::handleValueChanged);
}