Use QLineEdit's functions to implement ButtonOverlay functions if possible
This commit is contained in:
parent
7db2fd02fc
commit
5974690006
|
@ -9,8 +9,8 @@ set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
|
|||
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 7)
|
||||
set(META_VERSION_MINOR 1)
|
||||
set(META_VERSION_PATCH 0)
|
||||
set(META_APP_VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH})
|
||||
|
||||
project(${META_PROJECT_NAME})
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#include "./buttonoverlay.h"
|
||||
#include "./iconbutton.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QComboBox>
|
||||
#include <QCursor>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QStyle>
|
||||
#include <QStyleOption>
|
||||
#include <QToolTip>
|
||||
|
@ -14,31 +17,60 @@ namespace QtUtilities {
|
|||
|
||||
/*!
|
||||
* \class ButtonOverlay
|
||||
* \brief The ButtonOverlay class is used to display buttons on top of other
|
||||
* widgets.
|
||||
* \brief The ButtonOverlay class is used to display buttons on top of other widgets.
|
||||
*
|
||||
* The class creates a new layout manager and sets it to the widget which is
|
||||
* specified
|
||||
* when constructing an instance. Thus this widget must not already have a
|
||||
* layout manager.
|
||||
* This class had been created before QLineEdit's functions setClearButtonEnabled() and
|
||||
* addAction() have been available. (These functions have been available only since Qt 5.2.)
|
||||
*
|
||||
* The class is used to implement widget customization like ClearLineEidt and
|
||||
* ClearComboBox.
|
||||
* The downside of the "custom approach" compared to QLineEdit's own functions is that the
|
||||
* buttons are shown over the text as the text margins are not updated accordingly. Hence
|
||||
* the ButtonOverlay class has been updated to use QLineEdit's functions internally when the
|
||||
* specified widget is QLineEdit-based and its QLineEdit has been passed to the constructor.
|
||||
* However, when using any functions which can not be implemented using QLineEdit's own
|
||||
* functions, the ButtonOverlay has to fallback to its "custom approach". All functions which
|
||||
* cause this have a remark in their documentation.
|
||||
*
|
||||
* When QLineEdit's functions can not be used, the ButtonOverlay class creates a new layout
|
||||
* manager and sets it to the widget specified when constructing an instance. Thus this widget
|
||||
* must not already have a layout manager.
|
||||
*
|
||||
* The class is used to implement widget customization like ClearLineEidt and ClearComboBox
|
||||
* and most of the times it makes sense to use these widgets instead of using ButtonOverlay
|
||||
* directly.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a button overlay for the specified \a widget.
|
||||
* \param widget Specifies the widget to display the buttons on.
|
||||
* \remarks This function enforces the "custom approach" mentioned in the class documentation
|
||||
* and should therefore be avoided.
|
||||
*/
|
||||
ButtonOverlay::ButtonOverlay(QWidget *widget)
|
||||
: m_widget(widget)
|
||||
, m_buttonWidget(new QWidget(widget))
|
||||
, m_buttonLayout(new QHBoxLayout(m_buttonWidget))
|
||||
, m_buttonWidget(nullptr)
|
||||
, m_buttonLayout(nullptr)
|
||||
, m_clearButton(nullptr)
|
||||
, m_infoButton(nullptr)
|
||||
, m_infoButtonOrAction(nullptr)
|
||||
{
|
||||
buttonLayout()->setAlignment(Qt::AlignCenter | Qt::AlignRight);
|
||||
widget->setLayout(m_buttonLayout);
|
||||
fallbackToUsingCustomLayout();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Constructs a button overlay for the specified \a widget.
|
||||
* \param widget Specifies the widget to display the buttons on.
|
||||
* \param lineEdit Specifies the line edit used by \a widget to use the QLineEdit's functions
|
||||
* for adding actions instead of a custom layout.
|
||||
*/
|
||||
ButtonOverlay::ButtonOverlay(QWidget *widget, QLineEdit *lineEdit)
|
||||
: m_widget(widget)
|
||||
, m_buttonWidget(lineEdit)
|
||||
, m_buttonLayout(nullptr)
|
||||
, m_clearButton(nullptr)
|
||||
, m_infoButtonOrAction(nullptr)
|
||||
{
|
||||
if (!m_buttonWidget) {
|
||||
fallbackToUsingCustomLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -48,17 +80,60 @@ ButtonOverlay::~ButtonOverlay()
|
|||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the "custom approach" mentioned in the class documentation is used.
|
||||
*/
|
||||
bool ButtonOverlay::isUsingCustomLayout() const
|
||||
{
|
||||
return m_buttonLayout != nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the layout manager holding the buttons.
|
||||
* \remarks This function enforces the "custom approach" mentioned in the class documentation
|
||||
* and should therefore be avoided.
|
||||
*/
|
||||
QHBoxLayout *ButtonOverlay::buttonLayout()
|
||||
{
|
||||
fallbackToUsingCustomLayout();
|
||||
return m_buttonLayout;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the clear button is enabled.
|
||||
*/
|
||||
bool ButtonOverlay::isClearButtonEnabled() const
|
||||
{
|
||||
if (isUsingCustomLayout()) {
|
||||
return m_clearButton != nullptr;
|
||||
}
|
||||
return lineEditForWidget()->isClearButtonEnabled();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the info button is enabled.
|
||||
*/
|
||||
bool ButtonOverlay::isInfoButtonEnabled() const
|
||||
{
|
||||
return m_infoButtonOrAction != nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets whether the clear button is enabled.
|
||||
*/
|
||||
void ButtonOverlay::setClearButtonEnabled(bool enabled)
|
||||
{
|
||||
if (isClearButtonEnabled() && !enabled) {
|
||||
if (auto *const le = lineEditForWidget()) {
|
||||
le->setClearButtonEnabled(enabled);
|
||||
return;
|
||||
}
|
||||
const auto clearButtonEnabled = isClearButtonEnabled();
|
||||
if (clearButtonEnabled && !enabled) {
|
||||
// disable clear button
|
||||
m_buttonLayout->removeWidget(m_clearButton);
|
||||
delete m_clearButton;
|
||||
m_clearButton = nullptr;
|
||||
} else if (!isClearButtonEnabled() && enabled) {
|
||||
} else if (!clearButtonEnabled && enabled) {
|
||||
// enable clear button
|
||||
m_clearButton = new IconButton;
|
||||
m_clearButton->setHidden(isCleared());
|
||||
|
@ -80,18 +155,27 @@ void ButtonOverlay::setClearButtonEnabled(bool enabled)
|
|||
*/
|
||||
void ButtonOverlay::enableInfoButton(const QPixmap &pixmap, const QString &infoText)
|
||||
{
|
||||
if (!m_infoButton) {
|
||||
m_infoButton = new IconButton;
|
||||
m_infoButton->setGeometry(0, 0, 16, 16);
|
||||
QObject::connect(m_infoButton, &IconButton::clicked, std::bind(&ButtonOverlay::showInfo, this));
|
||||
if (auto *const le = lineEditForWidget()) {
|
||||
disableInfoButton();
|
||||
auto *const action = le->addAction(QIcon(pixmap), QLineEdit::TrailingPosition);
|
||||
action->setToolTip(infoText);
|
||||
QObject::connect(action, &QAction::triggered, std::bind(&ButtonOverlay::showInfo, this));
|
||||
m_infoButtonOrAction = action;
|
||||
return;
|
||||
}
|
||||
auto *infoButton = static_cast<IconButton *>(m_infoButtonOrAction);
|
||||
if (!infoButton) {
|
||||
m_infoButtonOrAction = infoButton = new IconButton;
|
||||
infoButton->setGeometry(0, 0, 16, 16);
|
||||
QObject::connect(infoButton, &IconButton::clicked, std::bind(&ButtonOverlay::showInfo, this));
|
||||
if (m_clearButton) {
|
||||
m_buttonLayout->insertWidget(m_buttonLayout->count() - 2, m_infoButton);
|
||||
m_buttonLayout->insertWidget(m_buttonLayout->count() - 2, infoButton);
|
||||
} else {
|
||||
m_buttonLayout->addWidget(m_infoButton);
|
||||
m_buttonLayout->addWidget(infoButton);
|
||||
}
|
||||
}
|
||||
m_infoButton->setPixmap(pixmap);
|
||||
m_infoButton->setToolTip(infoText);
|
||||
infoButton->setPixmap(pixmap);
|
||||
infoButton->setToolTip(infoText);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -100,10 +184,17 @@ void ButtonOverlay::enableInfoButton(const QPixmap &pixmap, const QString &infoT
|
|||
*/
|
||||
void ButtonOverlay::disableInfoButton()
|
||||
{
|
||||
if (m_infoButton) {
|
||||
m_buttonLayout->removeWidget(m_infoButton);
|
||||
delete m_infoButton;
|
||||
m_infoButton = nullptr;
|
||||
if (auto *const le = lineEditForWidget()) {
|
||||
if (auto *const infoAction = static_cast<QAction *>(m_infoButtonOrAction)) {
|
||||
le->removeAction(infoAction);
|
||||
m_infoButtonOrAction = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (auto *infoButton = static_cast<IconButton *>(m_infoButtonOrAction)) {
|
||||
m_buttonLayout->removeWidget(infoButton);
|
||||
delete infoButton;
|
||||
m_infoButtonOrAction = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,9 +202,13 @@ void ButtonOverlay::disableInfoButton()
|
|||
* \brief Adds a custom \a button.
|
||||
*
|
||||
* The button overlay takes ownership over the specified \a button.
|
||||
*
|
||||
* \remarks This function enforces the "custom approach" mentioned in the class documentation
|
||||
* and should therefore be avoided.
|
||||
*/
|
||||
void ButtonOverlay::addCustomButton(QWidget *button)
|
||||
{
|
||||
fallbackToUsingCustomLayout();
|
||||
m_buttonLayout->addWidget(button);
|
||||
}
|
||||
|
||||
|
@ -121,9 +216,13 @@ void ButtonOverlay::addCustomButton(QWidget *button)
|
|||
* \brief Inserts a custom \a button at the specified \a index.
|
||||
*
|
||||
* The button overlay takes ownership over the specified \a button.
|
||||
*
|
||||
* \remarks This function enforces the "custom approach" mentioned in the class documentation
|
||||
* and should therefore be avoided.
|
||||
*/
|
||||
void ButtonOverlay::insertCustomButton(int index, QWidget *button)
|
||||
{
|
||||
fallbackToUsingCustomLayout();
|
||||
m_buttonLayout->insertWidget(index, button);
|
||||
}
|
||||
|
||||
|
@ -131,16 +230,20 @@ void ButtonOverlay::insertCustomButton(int index, QWidget *button)
|
|||
* \brief Removes the specified custom \a button.
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Updates the visibility of the clear button.
|
||||
*
|
||||
* This method is meant to be called when subclassing.
|
||||
* This function is meant to be called when subclassing.
|
||||
*/
|
||||
void ButtonOverlay::updateClearButtonVisibility(bool visible)
|
||||
{
|
||||
|
@ -152,12 +255,75 @@ void ButtonOverlay::updateClearButtonVisibility(bool visible)
|
|||
/*!
|
||||
* \brief Clears the related widget.
|
||||
*
|
||||
* This method is meant to be implemented when subclassing.
|
||||
* This function is meant to be implemented when subclassing to support the clear button.
|
||||
*/
|
||||
void ButtonOverlay::handleClearButtonClicked()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Applies additional handling when the button layout has been created.
|
||||
*
|
||||
* This function is meant to be implemented when subclassing when additional handling is
|
||||
* required.
|
||||
*/
|
||||
void ButtonOverlay::handleCustomLayoutCreated()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Switches to the "custom approach".
|
||||
* \remarks This function is internally used when any legacy function is called
|
||||
* or when the QLineEdit for the specified widget can not be determined.
|
||||
*/
|
||||
void ButtonOverlay::fallbackToUsingCustomLayout()
|
||||
{
|
||||
// skip if custom layout is already used
|
||||
if (isUsingCustomLayout()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// disable QLineEdit's clear button and actions; save configuration
|
||||
const auto clearButtonEnabled = isClearButtonEnabled();
|
||||
if (clearButtonEnabled) {
|
||||
setClearButtonEnabled(false);
|
||||
}
|
||||
auto *const iconAction = static_cast<QAction *>(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();
|
||||
}
|
||||
|
||||
// initialize custom layout
|
||||
m_buttonLayout = new QHBoxLayout(m_buttonWidget);
|
||||
m_buttonWidget = new QWidget(m_widget);
|
||||
m_buttonLayout->setAlignment(Qt::AlignCenter | Qt::AlignRight);
|
||||
m_widget->setLayout(m_buttonLayout);
|
||||
handleCustomLayoutCreated();
|
||||
|
||||
// restore old configuration
|
||||
if (clearButtonEnabled) {
|
||||
setClearButtonEnabled(true);
|
||||
}
|
||||
if (iconAction) {
|
||||
enableInfoButton(infoPixmap, infoText);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the QLineEdit used to implement the button overlay.
|
||||
* \remarks This is always nullptr in case the "custom approach" is used.
|
||||
*/
|
||||
QLineEdit *ButtonOverlay::lineEditForWidget() const
|
||||
{
|
||||
return isUsingCustomLayout() ? nullptr : static_cast<QLineEdit *>(m_buttonWidget);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the related widget is cleared.
|
||||
*
|
||||
|
@ -172,17 +338,27 @@ bool ButtonOverlay::isCleared() const
|
|||
* \brief Shows the info text using a tool tip.
|
||||
*
|
||||
* This method is called when the info button is clicked.
|
||||
*
|
||||
* \todo Don't use QCursor::pos() here because it will not work under Wayland.
|
||||
*/
|
||||
void ButtonOverlay::showInfo()
|
||||
{
|
||||
if (m_infoButton) {
|
||||
QToolTip::showText(QCursor::pos(), m_infoButton->toolTip(), m_infoButton);
|
||||
if (!isUsingCustomLayout()) {
|
||||
if (auto *const infoAction = static_cast<QAction *>(m_infoButtonOrAction)) {
|
||||
QToolTip::showText(QCursor::pos(), infoAction->toolTip(), m_widget);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (auto *const infoButton = static_cast<IconButton *>(m_infoButtonOrAction)) {
|
||||
QToolTip::showText(QCursor::pos(), infoButton->toolTip(), 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.
|
||||
* \remarks This function enforces the "custom approach" mentioned in the class documentation
|
||||
* and should therefore be avoided. Of course it makes sense to call it within handleCustomLayoutCreated().
|
||||
*/
|
||||
void ButtonOverlay::setContentsMarginsFromEditFieldRectAndFrameWidth(const QRect &editFieldRect, int frameWidth, int padding)
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@ QT_FORWARD_DECLARE_CLASS(QString)
|
|||
QT_FORWARD_DECLARE_CLASS(QPixmap)
|
||||
QT_FORWARD_DECLARE_CLASS(QMargins)
|
||||
QT_FORWARD_DECLARE_CLASS(QRect)
|
||||
QT_FORWARD_DECLARE_CLASS(QLineEdit)
|
||||
|
||||
namespace QtUtilities {
|
||||
|
||||
|
@ -29,8 +30,10 @@ class QT_UTILITIES_EXPORT ButtonOverlay {
|
|||
|
||||
public:
|
||||
explicit ButtonOverlay(QWidget *widget);
|
||||
explicit ButtonOverlay(QWidget *widget, QLineEdit *lineEdit);
|
||||
virtual ~ButtonOverlay();
|
||||
|
||||
bool isUsingCustomLayout() const;
|
||||
QHBoxLayout *buttonLayout();
|
||||
bool isClearButtonEnabled() const;
|
||||
void setClearButtonEnabled(bool enabled);
|
||||
|
@ -45,8 +48,11 @@ public:
|
|||
protected:
|
||||
void updateClearButtonVisibility(bool visible);
|
||||
virtual void handleClearButtonClicked();
|
||||
virtual void handleCustomLayoutCreated();
|
||||
|
||||
private:
|
||||
void fallbackToUsingCustomLayout();
|
||||
QLineEdit *lineEditForWidget() const;
|
||||
void showInfo();
|
||||
void setContentsMarginsFromEditFieldRectAndFrameWidth(const QRect &editFieldRect, int frameWidth, int padding = 0);
|
||||
|
||||
|
@ -54,32 +60,9 @@ private:
|
|||
QWidget *m_buttonWidget;
|
||||
QHBoxLayout *m_buttonLayout;
|
||||
IconButton *m_clearButton;
|
||||
IconButton *m_infoButton;
|
||||
void *m_infoButtonOrAction;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Returns the layout manager holding the buttons.
|
||||
*/
|
||||
inline QHBoxLayout *ButtonOverlay::buttonLayout()
|
||||
{
|
||||
return m_buttonLayout;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the clear button is enabled.
|
||||
*/
|
||||
inline bool ButtonOverlay::isClearButtonEnabled() const
|
||||
{
|
||||
return m_clearButton != nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the info button is enabled.
|
||||
*/
|
||||
inline bool ButtonOverlay::isInfoButtonEnabled() const
|
||||
{
|
||||
return m_infoButton != nullptr;
|
||||
}
|
||||
} // namespace QtUtilities
|
||||
|
||||
#endif // WIDGETS_BUTTONOVERLAY_H
|
||||
|
|
|
@ -11,20 +11,22 @@ namespace QtUtilities {
|
|||
* \brief A QComboBox with an embedded button for clearing its contents.
|
||||
*/
|
||||
|
||||
/// \cond
|
||||
static inline auto *getComboBoxLineEdit(QComboBox *comboBox)
|
||||
{
|
||||
comboBox->setEditable(true);
|
||||
return comboBox->lineEdit();
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
* \brief Constructs a clear combo box.
|
||||
* \remarks The combo box is initialized to be editable and which must not be changed.
|
||||
*/
|
||||
ClearComboBox::ClearComboBox(QWidget *parent)
|
||||
: QComboBox(parent)
|
||||
, ButtonOverlay(this)
|
||||
, ButtonOverlay(this, getComboBoxLineEdit(this))
|
||||
{
|
||||
const QStyle *const s = style();
|
||||
QStyleOptionComboBox opt;
|
||||
opt.initFrom(this);
|
||||
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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -47,6 +49,16 @@ void ClearComboBox::handleClearButtonClicked()
|
|||
clearEditText();
|
||||
}
|
||||
|
||||
void ClearComboBox::handleCustomLayoutCreated()
|
||||
{
|
||||
const QStyle *const s = style();
|
||||
QStyleOptionComboBox opt;
|
||||
opt.initFrom(this);
|
||||
setContentsMarginsFromEditFieldRectAndFrameWidth(
|
||||
s->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this), s->pixelMetric(QStyle::PM_ComboBoxFrameWidth, &opt, this));
|
||||
connect(this, &ClearComboBox::currentTextChanged, this, &ClearComboBox::handleTextChanged);
|
||||
}
|
||||
|
||||
bool ClearComboBox::isCleared() const
|
||||
{
|
||||
return currentText().isEmpty();
|
||||
|
|
|
@ -19,6 +19,7 @@ public:
|
|||
private Q_SLOTS:
|
||||
void handleTextChanged(const QString &text);
|
||||
void handleClearButtonClicked() override;
|
||||
void handleCustomLayoutCreated() override;
|
||||
};
|
||||
|
||||
} // namespace QtUtilities
|
||||
|
|
|
@ -15,15 +15,9 @@ namespace QtUtilities {
|
|||
*/
|
||||
ClearLineEdit::ClearLineEdit(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
, ButtonOverlay(this)
|
||||
, ButtonOverlay(this, 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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -46,6 +40,16 @@ void ClearLineEdit::handleClearButtonClicked()
|
|||
clear();
|
||||
}
|
||||
|
||||
void ClearLineEdit::handleCustomLayoutCreated()
|
||||
{
|
||||
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));
|
||||
connect(this, &ClearLineEdit::textChanged, this, &ClearLineEdit::handleTextChanged);
|
||||
}
|
||||
|
||||
bool ClearLineEdit::isCleared() const
|
||||
{
|
||||
return text().isEmpty();
|
||||
|
|
|
@ -23,6 +23,7 @@ public:
|
|||
private Q_SLOTS:
|
||||
void handleTextChanged(const QString &text);
|
||||
void handleClearButtonClicked() override;
|
||||
void handleCustomLayoutCreated() override;
|
||||
};
|
||||
} // namespace QtUtilities
|
||||
|
||||
|
|
|
@ -21,18 +21,8 @@ ClearPlainTextEdit::ClearPlainTextEdit(QWidget *parent)
|
|||
: QPlainTextEdit(parent)
|
||||
, ButtonOverlay(viewport())
|
||||
{
|
||||
// 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));
|
||||
handleCustomLayoutCreated();
|
||||
ButtonOverlay::setClearButtonEnabled(true);
|
||||
connect(this, &QPlainTextEdit::textChanged, this, &ClearPlainTextEdit::handleTextChanged);
|
||||
// ensure button layout is realigned when scrolling
|
||||
connect(verticalScrollBar(), &QScrollBar::actionTriggered, this, &ClearPlainTextEdit::handleScroll);
|
||||
connect(this, &QPlainTextEdit::cursorPositionChanged, this, &ClearPlainTextEdit::handleScroll);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -58,6 +48,21 @@ void ClearPlainTextEdit::handleClearButtonClicked()
|
|||
cursor.removeSelectedText();
|
||||
}
|
||||
|
||||
void ClearPlainTextEdit::handleCustomLayoutCreated()
|
||||
{
|
||||
// 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));
|
||||
connect(this, &QPlainTextEdit::textChanged, this, &ClearPlainTextEdit::handleTextChanged);
|
||||
// ensure button layout is realigned when scrolling
|
||||
connect(verticalScrollBar(), &QScrollBar::actionTriggered, this, &ClearPlainTextEdit::handleScroll);
|
||||
connect(this, &QPlainTextEdit::cursorPositionChanged, this, &ClearPlainTextEdit::handleScroll);
|
||||
}
|
||||
|
||||
void ClearPlainTextEdit::handleScroll()
|
||||
{
|
||||
buttonLayout()->update();
|
||||
|
|
|
@ -19,6 +19,7 @@ public:
|
|||
private Q_SLOTS:
|
||||
void handleTextChanged();
|
||||
void handleClearButtonClicked() override;
|
||||
void handleCustomLayoutCreated() override;
|
||||
void handleScroll();
|
||||
};
|
||||
|
||||
|
|
|
@ -18,16 +18,10 @@ namespace QtUtilities {
|
|||
*/
|
||||
ClearSpinBox::ClearSpinBox(QWidget *parent)
|
||||
: QSpinBox(parent)
|
||||
, ButtonOverlay(this)
|
||||
, ButtonOverlay(this, lineEdit())
|
||||
, m_minimumHidden(false)
|
||||
{
|
||||
const QStyle *const s = style();
|
||||
QStyleOptionSpinBox opt;
|
||||
opt.initFrom(this);
|
||||
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);
|
||||
ButtonOverlay::setClearButtonEnabled(true);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -50,6 +44,16 @@ void ClearSpinBox::handleClearButtonClicked()
|
|||
setValue(minimum());
|
||||
}
|
||||
|
||||
void ClearSpinBox::handleCustomLayoutCreated()
|
||||
{
|
||||
const QStyle *const s = style();
|
||||
QStyleOptionSpinBox opt;
|
||||
opt.initFrom(this);
|
||||
setContentsMarginsFromEditFieldRectAndFrameWidth(
|
||||
s->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxEditField, this), s->pixelMetric(QStyle::PM_SpinBoxFrameWidth, &opt, this));
|
||||
connect(this, static_cast<void (ClearSpinBox::*)(int)>(&ClearSpinBox::valueChanged), this, &ClearSpinBox::handleValueChanged);
|
||||
}
|
||||
|
||||
bool ClearSpinBox::isCleared() const
|
||||
{
|
||||
return value() == minimum();
|
||||
|
|
|
@ -34,6 +34,7 @@ protected:
|
|||
private Q_SLOTS:
|
||||
void handleValueChanged(int value);
|
||||
void handleClearButtonClicked() override;
|
||||
void handleCustomLayoutCreated() override;
|
||||
|
||||
private:
|
||||
bool m_minimumHidden;
|
||||
|
|
Loading…
Reference in New Issue