refactor: cleanup and modernize actiongroup

This commit is contained in:
Alfredo Monclus
2025-02-04 05:00:40 -03:00
parent d560ddb826
commit 235c3c098a
2 changed files with 203 additions and 253 deletions

View File

@@ -4,82 +4,85 @@
* License: LGPL *
* *
***************************************************************************/
#include "actiongroup.h"
#include "taskheader_p.h"
#include "taskgroup_p.h"
#include <QPainter>
#include <QVBoxLayout>
#include <QTimer>
namespace QSint
{
ActionGroup::ActionGroup(QWidget *parent)
: QWidget(parent)
: QWidget(parent),
myHeader(new TaskHeader(QPixmap(), "", false, this))
{
myHeader = new TaskHeader(QPixmap(), "", false, this);
myHeader->setVisible(false);
init(false);
}
ActionGroup::ActionGroup(const QString &title, bool expandable, QWidget *parent)
: QWidget(parent)
: QWidget(parent),
myHeader(new TaskHeader(QPixmap(), title, expandable, this))
{
myHeader = new TaskHeader(QPixmap(), title, expandable, this);
init(true);
}
ActionGroup::ActionGroup(const QPixmap &icon, const QString &title, bool expandable, QWidget *parent)
: QWidget(parent)
: QWidget(parent),
myHeader(new TaskHeader(icon, title, expandable, this))
{
myHeader = new TaskHeader(icon, title, expandable, this);
init(true);
}
void ActionGroup::init(bool header)
ActionGroup::~ActionGroup()
{
m_foldStep = 0;
myScheme = ActionPanelScheme::defaultScheme();
QVBoxLayout *vbl = new QVBoxLayout();
vbl->setContentsMargins(0, 0, 0, 0);
vbl->setSpacing(0);
setLayout(vbl);
vbl->addWidget(myHeader);
myGroup = new TaskGroup(this, header);
vbl->addWidget(myGroup);
myDummy = new QWidget(this);
vbl->addWidget(myDummy);
myDummy->hide();
connect(myHeader, &TaskHeader::activated, this, &ActionGroup::showHide);
delete myHeader;
delete myGroup;
delete myDummy;
}
void ActionGroup::setScheme(ActionPanelScheme *pointer)
void ActionGroup::init(bool hasHeader)
{
myScheme = pointer;
myHeader->setScheme(pointer);
myGroup->setScheme(pointer);
update();
m_foldStep = 0;
myScheme = ActionPanelScheme::defaultScheme();
auto *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
layout->addWidget(myHeader);
myGroup = new TaskGroup(this, hasHeader);
layout->addWidget(myGroup);
myDummy = new QWidget(this);
layout->addWidget(myDummy);
myDummy->hide();
connect(myHeader, &TaskHeader::activated, this, &ActionGroup::showHide);
}
void ActionGroup::setScheme(ActionPanelScheme *scheme)
{
myScheme = scheme;
myHeader->setScheme(scheme);
myGroup->setScheme(scheme);
update();
}
QBoxLayout* ActionGroup::groupLayout()
{
return myGroup->groupLayout();
return myGroup->groupLayout();
}
ActionLabel* ActionGroup::addAction(QAction *action, bool addToLayout, bool addStretch)
{
if (!action)
return nullptr;
if (!action) return nullptr;
ActionLabel* label = new ActionLabel(action, this);
auto *label = new ActionLabel(action, this);
myGroup->addActionLabel(label, addToLayout, addStretch);
return label;
@@ -87,11 +90,9 @@ ActionLabel* ActionGroup::addAction(QAction *action, bool addToLayout, bool addS
ActionLabel* ActionGroup::addActionLabel(ActionLabel *label, bool addToLayout, bool addStretch)
{
if (!label)
return nullptr;
if (!label) return nullptr;
myGroup->addActionLabel(label, addToLayout, addStretch);
return label;
}
@@ -102,121 +103,101 @@ bool ActionGroup::addWidget(QWidget *widget, bool addToLayout, bool addStretch)
void ActionGroup::showHide()
{
if (m_foldStep)
return;
if (m_foldStep || !myHeader->expandable()) return;
if (!myHeader->expandable())
return;
if (myGroup->isVisible())
{
m_foldPixmap = myGroup->transparentRender();
m_tempHeight = m_fullHeight = myGroup->height();
m_foldDelta = m_fullHeight / myScheme->groupFoldSteps;
m_foldStep = myScheme->groupFoldSteps;
m_foldDirection = -1;
if (myGroup->isVisible())
{
m_foldPixmap = myGroup->transparentRender();
// m_foldPixmap = QPixmap::grabWidget(myGroup, myGroup->rect());
myGroup->hide();
myDummy->setFixedSize(myGroup->size());
myDummy->show();
m_tempHeight = m_fullHeight = myGroup->height();
m_foldDelta = m_fullHeight / myScheme->groupFoldSteps;
m_foldStep = myScheme->groupFoldSteps;
m_foldDirection = -1;
QTimer::singleShot(myScheme->groupFoldDelay, this, &ActionGroup::processHide);
}
else
{
m_foldStep = myScheme->groupFoldSteps;
m_foldDirection = 1;
m_tempHeight = 0;
myGroup->hide();
myDummy->setFixedSize(myGroup->size());
myDummy->show();
QTimer::singleShot(myScheme->groupFoldDelay, this, &ActionGroup::processHide);
}
else
{
m_foldStep = myScheme->groupFoldSteps;
m_foldDirection = 1;
m_tempHeight = 0;
QTimer::singleShot(myScheme->groupFoldDelay, this, &ActionGroup::processShow);
}
myDummy->show();
QTimer::singleShot(myScheme->groupFoldDelay, this, &ActionGroup::processShow);
}
}
void ActionGroup::processHide()
{
if (!--m_foldStep) {
myDummy->setFixedHeight(0);
myDummy->hide();
myHeader->setFold(false);
setFixedHeight(myHeader->height());
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
return;
}
if (--m_foldStep == 0)
{
myDummy->hide();
myHeader->setFold(false);
setFixedHeight(myHeader->height());
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
return;
}
setUpdatesEnabled(false);
m_tempHeight -= m_foldDelta;
myDummy->setFixedHeight(m_tempHeight);
setFixedHeight(myDummy->height() + myHeader->height());
m_tempHeight -= m_foldDelta;
myDummy->setFixedHeight(m_tempHeight);
setFixedHeight(myDummy->height()+myHeader->height());
QTimer::singleShot(myScheme->groupFoldDelay, this, &ActionGroup::processHide);
setUpdatesEnabled(true);
QTimer::singleShot(myScheme->groupFoldDelay, this, &ActionGroup::processHide);
}
void ActionGroup::processShow()
{
if (!--m_foldStep) {
myDummy->hide();
m_foldPixmap = QPixmap();
myGroup->show();
myHeader->setFold(true);
setFixedHeight(m_fullHeight+myHeader->height());
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
setMaximumHeight(QWIDGETSIZE_MAX);
setMinimumHeight(0);
return;
}
setUpdatesEnabled(false);
m_tempHeight += m_foldDelta;
myDummy->setFixedHeight(m_tempHeight);
setFixedHeight(myDummy->height()+myHeader->height());
QTimer::singleShot(myScheme->groupFoldDelay, this, &ActionGroup::processShow);
setUpdatesEnabled(true);
}
void ActionGroup::paintEvent ( QPaintEvent * event )
{
Q_UNUSED(event);
QPainter p(this);
if (myDummy->isVisible()) {
if (myScheme->groupFoldThaw) {
if (m_foldDirection < 0)
p.setOpacity((double)m_foldStep / myScheme->groupFoldSteps);
else
p.setOpacity((double)(myScheme->groupFoldSteps-m_foldStep) / myScheme->groupFoldSteps);
}
switch (myScheme->groupFoldEffect)
if (--m_foldStep == 0)
{
case ActionPanelScheme::ShrunkFolding:
p.drawPixmap(myDummy->pos(), m_foldPixmap.scaled(myDummy->size()) );
break;
case ActionPanelScheme::SlideFolding:
p.drawPixmap(myDummy->pos(), m_foldPixmap,
QRect(0, m_foldPixmap.height()-myDummy->height(),
m_foldPixmap.width(), myDummy->width()
) );
break;
default:
p.drawPixmap(myDummy->pos(), m_foldPixmap);
myDummy->hide();
m_foldPixmap = QPixmap();
myGroup->show();
myHeader->setFold(true);
setFixedHeight(m_fullHeight + myHeader->height());
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
setMaximumHeight(QWIDGETSIZE_MAX);
return;
}
return;
}
m_tempHeight += m_foldDelta;
myDummy->setFixedHeight(m_tempHeight);
setFixedHeight(myDummy->height() + myHeader->height());
QTimer::singleShot(myScheme->groupFoldDelay, this, &ActionGroup::processShow);
}
void ActionGroup::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter p(this);
if (myDummy->isVisible())
{
if (myScheme->groupFoldThaw)
{
double opacity = (m_foldDirection < 0)
? static_cast<double>(m_foldStep) / myScheme->groupFoldSteps
: static_cast<double>(myScheme->groupFoldSteps - m_foldStep) / myScheme->groupFoldSteps;
p.setOpacity(opacity);
}
switch (myScheme->groupFoldEffect)
{
case ActionPanelScheme::ShrunkFolding:
p.drawPixmap(myDummy->pos(), m_foldPixmap.scaled(myDummy->size()));
break;
case ActionPanelScheme::SlideFolding:
p.drawPixmap(myDummy->pos(), m_foldPixmap,
QRect(0, m_foldPixmap.height() - myDummy->height(),
m_foldPixmap.width(), myDummy->width()));
break;
default:
p.drawPixmap(myDummy->pos(), m_foldPixmap);
}
}
}
bool ActionGroup::isExpandable() const
{
@@ -243,21 +224,19 @@ QString ActionGroup::headerText() const
return myHeader->myTitle->text();
}
void ActionGroup::setHeaderText(const QString & headerText)
void ActionGroup::setHeaderText(const QString &headerText)
{
myHeader->myTitle->setText(headerText);
}
void ActionGroup::setHeaderIcon(const QPixmap& icon)
void ActionGroup::setHeaderIcon(const QPixmap &icon)
{
myHeader->myTitle->setIcon(icon);
}
QSize ActionGroup::minimumSizeHint() const
{
return {200,65};
return {200, 65};
}
}
} // namespace

View File

@@ -8,9 +8,9 @@
#ifndef ACTIONGROUP_H
#define ACTIONGROUP_H
#include <QWidget>
#include <QBoxLayout>
#include <QTimer>
#include <QWidget>
#include "qsint_global.h"
@@ -20,157 +20,128 @@ namespace QSint
class ActionLabel;
class ActionPanelScheme;
class TaskHeader;
class TaskGroup;
/**
\brief Class representing a single group of actions similar to Windows XP task panels.
\since 0.2
\image html ActionGroup.png An example of ActionGroup
ActionGroup consists from optional header and set of actions represented by ActionLabel.
It can contain arbitrary widgets as well.
*/
* @brief A collapsible group widget for organizing actions
*
* ActionGroup consists of an optional header and a collection of actions represented by ActionLabel.
* It can also contain arbitrary widgets.
*
*/
class QSINT_EXPORT ActionGroup : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool expandable READ isExpandable WRITE setExpandable) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(bool header READ hasHeader WRITE setHeader) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(QString headerText READ headerText WRITE setHeaderText) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(bool expandable READ isExpandable WRITE setExpandable)
Q_PROPERTY(bool header READ hasHeader WRITE setHeader)
Q_PROPERTY(QString headerText READ headerText WRITE setHeaderText)
public:
/** Constructor. Creates ActionGroup without header.
*/
explicit ActionGroup(QWidget *parent = nullptr);
/** Constructor. Creates ActionGroup with header's
text set to \a title, but with no icon.
If \a expandable set to \a true (default), the group can be expanded/collapsed by the user.
*/
explicit ActionGroup(const QString& title,
bool expandable = true,
QWidget *parent = nullptr);
/** Constructor. Creates ActionGroup with header's
text set to \a title and icon set to \a icon.
If \a expandable set to \a true (default), the group can be expanded/collapsed by the user.
*/
explicit ActionGroup(const QPixmap& icon,
const QString& title,
bool expandable = true,
QWidget *parent = nullptr);
/** Creates action item from the \a action and returns it.
If \a addToLayout is set to \a true (default),
the action is added to the default vertical layout, i.e. subsequent
calls of this function will create several ActionLabels arranged vertically,
one below another.
Set \a addToLayout to \a false if you want to add the action to the specified layout manually.
This allows one to do custom actions arrangements, i.e. horizontal etc.
If \a addStretch is set to \a true (default),
ActionLabel will be automatically aligned to the left side of the ActionGroup.
Set \a addStretch to \a false if you want ActionLabel to occupy all the horizontal space.
*/
explicit ActionGroup(const QString& title, bool expandable = true, QWidget *parent = nullptr);
explicit ActionGroup(const QPixmap& icon, const QString& title, bool expandable = true, QWidget *parent = nullptr);
~ActionGroup() override;
/**
* @brief Creates an action item from the given `action` and returns it.
*
* If `addToLayout` is `true` (default), the action is added to the default vertical layout, meaning
* subsequent calls will arrange multiple `ActionLabel`s vertically, one below another.
*
* If `addToLayout` is `false`, the action must be added to a layout manually.
* This allows for custom arrangements, such as horizontal layouts.
*
* If `addStretch` is `true` (default),`ActionLabel` will be automatically aligned to the left side.
* if `addStretch` is `false` `ActionLabel` will occupy all available horizontal space.
*/
ActionLabel* addAction(QAction *action, bool addToLayout = true, bool addStretch = true);
/** Adds \a label to the group.
\sa addAction() for the description.
*/
/**
* @brief Adds an `ActionLabel` to the group.
* See `addAction()` for parameter details.
*/
ActionLabel* addActionLabel(ActionLabel *label, bool addToLayout = true, bool addStretch = true);
/** Adds \a widget to the group. Returns \a true if it has been added successfully.
\sa addAction() for the description.
*/
/**
* @brief Adds a `QWidget` to the group. Returns `true` if added successfully.
* See `addAction()` for parameter details.
*/
bool addWidget(QWidget *widget, bool addToLayout = true, bool addStretch = true);
/** Returns group's layout (QVBoxLayout by default).
*/
/**
* @brief Returns the group's layout (QVBoxLayout by default).
*/
QBoxLayout* groupLayout();
/** Sets the scheme of the panel and all the child groups to \a scheme.
By default, ActionPanelScheme::defaultScheme() is used.
*/
void setScheme(ActionPanelScheme *pointer);
/**
* @brief Sets the scheme for the panel and all its child groups.
*
* By default, `ActionPanelScheme::defaultScheme()` is used.
*/
void setScheme(ActionPanelScheme *scheme);
/** Returns \a true if the group is expandable.
\sa setExpandable().
*/
/**
* @brief Checks if the group can collapse or expand.
*/
bool isExpandable() const;
/** Returns \a true if the group has header.
/**
* @brief Makes the group expandable or not.
*/
void setExpandable(bool expandable);
\sa setHeader().
*/
/**
* @brief Checks if the group has a header.
*/
bool hasHeader() const;
/** Returns text of the header.
Only valid if the group has header (see hasHeader()).
/**
* @brief Enables or disables the group's header.
*/
void setHeader(bool enable);
\sa setHeaderText().
*/
/**
* @brief Returns the text of the header.
*/
QString headerText() const;
/**
* @brief Sets the text of the header.
*/
void setHeaderText(const QString &text);
/**
* @brief Sets the icon of the header.
*/
void setHeaderIcon(const QPixmap &icon);
QSize minimumSizeHint() const override;
public Q_SLOTS:
/** Expands/collapses the group.
Only valid if the group has header (see hasHeader()).
*/
void showHide();
/** Makes the group expandable if \a expandable is set to \a true.
\sa isExpandable().
*/
void setExpandable(bool expandable = true);
/** Enables/disables group's header according to \a enable.
\sa hasHeader().
*/
void setHeader(bool enable = true);
/** Sets text of the header to \a title.
Only valid if the group has header (see hasHeader()).
\sa headerText().
*/
void setHeaderText(const QString & title);
void setHeaderIcon(const QPixmap& icon);
protected Q_SLOTS:
void processHide();
void processShow();
protected:
void init(bool header);
void paintEvent(QPaintEvent *event) override;
void init(bool hasHeader);
void paintEvent ( QPaintEvent * event ) override;
double m_foldStep, m_foldDelta, m_fullHeight, m_tempHeight;
int m_foldDirection;
double m_foldStep = 0;
double m_foldDelta = 0;
double m_fullHeight = 0;
double m_tempHeight = 0;
int m_foldDirection = 0;
QPixmap m_foldPixmap;
class TaskHeader *myHeader;
class TaskGroup *myGroup;
QWidget *myDummy;
ActionPanelScheme *myScheme;
TaskHeader *myHeader = nullptr;
TaskGroup *myGroup = nullptr;
QWidget *myDummy = nullptr;
ActionPanelScheme *myScheme = nullptr;
};
} // namespace
} // namespace QSint
#endif // ACTIONGROUP_H