-
Notifications
You must be signed in to change notification settings - Fork 129
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
119 changed files
with
2,915 additions
and
1,158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
|
||
#include "CollapsibleGroupBox.h" | ||
#include <QSettings> | ||
#include <QtCore/QEvent> | ||
#include <QtGui/QShowEvent> | ||
#include <foundation/ScopedIncDec.h> | ||
|
||
CollapsibleGroupBox::CollapsibleGroupBox(QWidget* parent) | ||
: QGroupBox(parent) { | ||
initialize(); | ||
} | ||
|
||
CollapsibleGroupBox::CollapsibleGroupBox(const QString& title, QWidget* parent) | ||
: QGroupBox(title, parent) { | ||
initialize(); | ||
} | ||
|
||
void CollapsibleGroupBox::initialize() { | ||
collapseIcon.addPixmap( | ||
QPixmap(QString::fromLatin1(":/icons/minus-16.png")) | ||
); | ||
expandIcon.addPixmap( | ||
QPixmap(QString::fromLatin1(":/icons/plus-16.png")) | ||
); | ||
collapseButton = new QToolButton(this); | ||
collapseButton->setObjectName("collapseButton"); | ||
collapseButton->setAutoRaise(true); | ||
collapseButton->setFixedSize(14, 14); | ||
collapseButton->setIconSize(QSize(12, 12)); | ||
collapseButton->setIcon(collapseIcon); | ||
setFocusProxy(collapseButton); | ||
setFocusPolicy(Qt::StrongFocus); | ||
|
||
connect(collapseButton, &QAbstractButton::clicked, this, &CollapsibleGroupBox::toggleCollapsed); | ||
connect(this, &QGroupBox::toggled, this, &CollapsibleGroupBox::checkToggled); | ||
connect(this, &QGroupBox::clicked, this, &CollapsibleGroupBox::checkClicked); | ||
} | ||
|
||
void CollapsibleGroupBox::setCollapsed(const bool collapse) { | ||
const bool changed = (collapse != collapsed); | ||
|
||
if (changed) { | ||
collapsed = collapse; | ||
collapseButton->setIcon(collapse ? expandIcon : collapseIcon); | ||
|
||
updateWidgets(); | ||
|
||
emit collapsedStateChanged(isCollapsed()); | ||
} | ||
} | ||
|
||
bool CollapsibleGroupBox::isCollapsed() const { | ||
return collapsed; | ||
} | ||
|
||
void CollapsibleGroupBox::checkToggled(bool) { | ||
collapseButton->setEnabled(true); | ||
} | ||
|
||
void CollapsibleGroupBox::checkClicked(bool checked) { | ||
if (checked && isCollapsed()) { | ||
setCollapsed(false); | ||
} else if (!checked && !isCollapsed()) { | ||
setCollapsed(true); | ||
} | ||
} | ||
|
||
void CollapsibleGroupBox::toggleCollapsed() { | ||
// verify if sender is this group box's collapse button | ||
auto* sender = dynamic_cast<QToolButton*>(QObject::sender()); | ||
const bool isSenderCollapseButton = (sender && (sender == collapseButton)); | ||
|
||
if (isSenderCollapseButton) { | ||
setCollapsed(!isCollapsed()); | ||
} | ||
} | ||
|
||
void CollapsibleGroupBox::updateWidgets() { | ||
const ScopedIncDec<int> guard(ignoreVisibilityEvents); | ||
|
||
if (collapsed) { | ||
for (QObject* child : children()) { | ||
auto* widget = dynamic_cast<QWidget*>(child); | ||
if (widget && (widget != collapseButton) && widget->isVisible()) { | ||
collapsedWidgets.insert(widget); | ||
widget->hide(); | ||
} | ||
} | ||
} else { | ||
for (QObject* child : children()) { | ||
auto* widget = dynamic_cast<QWidget*>(child); | ||
if (widget && (widget != collapseButton) && (collapsedWidgets.find(widget) != collapsedWidgets.end())) { | ||
collapsedWidgets.erase(widget); | ||
widget->show(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
void CollapsibleGroupBox::showEvent(QShowEvent* event) { | ||
// initialize widget on first show event only | ||
if (shown) { | ||
event->accept(); | ||
return; | ||
} | ||
shown = true; | ||
|
||
loadState(); | ||
|
||
QWidget::showEvent(event); | ||
} | ||
|
||
void CollapsibleGroupBox::changeEvent(QEvent* event) { | ||
QGroupBox::changeEvent(event); | ||
|
||
if ((event->type() == QEvent::EnabledChange) && isEnabled()) { | ||
collapseButton->setEnabled(true); | ||
} | ||
} | ||
|
||
void CollapsibleGroupBox::childEvent(QChildEvent* event) { | ||
auto* childWidget = dynamic_cast<QWidget*>(event->child()); | ||
if (childWidget && (event)->type() == QEvent::ChildAdded) { | ||
if (collapsed) { | ||
if (childWidget->isVisible()) { | ||
collapsedWidgets.insert(childWidget); | ||
childWidget->hide(); | ||
} | ||
} | ||
|
||
childWidget->installEventFilter(this); | ||
} | ||
|
||
QGroupBox::childEvent(event); | ||
} | ||
|
||
bool CollapsibleGroupBox::eventFilter(QObject* watched, QEvent* event) { | ||
if (collapsed && !ignoreVisibilityEvents) { | ||
auto* childWidget = dynamic_cast<QWidget*>(watched); | ||
if (childWidget) { | ||
if (event->type() == QEvent::ShowToParent) { | ||
const ScopedIncDec<int> guard(ignoreVisibilityEvents); | ||
|
||
collapsedWidgets.insert(childWidget); | ||
childWidget->hide(); | ||
} else if (event->type() == QEvent::HideToParent) { | ||
collapsedWidgets.erase(childWidget); | ||
} | ||
} | ||
} | ||
|
||
return QObject::eventFilter(watched, event); | ||
} | ||
|
||
CollapsibleGroupBox::~CollapsibleGroupBox() { | ||
saveState(); | ||
} | ||
|
||
void CollapsibleGroupBox::loadState() { | ||
if (!isEnabled()) { | ||
return; | ||
} | ||
|
||
const QString key = getSettingsKey(); | ||
if (key.isEmpty()) { | ||
return; | ||
} | ||
|
||
setUpdatesEnabled(false); | ||
|
||
QSettings settings; | ||
|
||
if (isCheckable()) { | ||
QVariant val = settings.value(key + "/checked"); | ||
if (!val.isNull()) { | ||
setChecked(val.toBool()); | ||
} | ||
} | ||
|
||
{ | ||
QVariant val = settings.value(key + "/collapsed"); | ||
if (!val.isNull()) { | ||
setCollapsed(val.toBool()); | ||
} | ||
} | ||
|
||
setUpdatesEnabled(true); | ||
} | ||
|
||
void CollapsibleGroupBox::saveState() { | ||
if (!shown || !isEnabled()) { | ||
return; | ||
} | ||
|
||
const QString key = getSettingsKey(); | ||
if (key.isEmpty()) { | ||
return; | ||
} | ||
|
||
QSettings settings; | ||
|
||
if (isCheckable()) { | ||
settings.setValue(key + "/checked", isChecked()); | ||
} | ||
settings.setValue(key + "/collapsed", isCollapsed()); | ||
} | ||
|
||
QString CollapsibleGroupBox::getSettingsKey() const { | ||
if (objectName().isEmpty()) { | ||
return QString(); | ||
} | ||
|
||
QString saveKey = '/' + objectName(); | ||
saveKey = "CollapsibleGroupBox" + saveKey; | ||
return saveKey; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
|
||
#ifndef SCANTAILOR_COLLAPSIBLEGROUPBOX_H | ||
#define SCANTAILOR_COLLAPSIBLEGROUPBOX_H | ||
|
||
|
||
#include <QtWidgets/QToolButton> | ||
#include <QtWidgets/QGroupBox> | ||
#include <unordered_set> | ||
|
||
class CollapsibleGroupBox : public QGroupBox { | ||
Q_OBJECT | ||
|
||
/** | ||
* The collapsed state of this group box. If it is set to true, all content is hidden | ||
* if it is set to false all content is shown. | ||
*/ | ||
Q_PROPERTY(bool collapsed READ isCollapsed WRITE setCollapsed USER true) | ||
|
||
public: | ||
explicit CollapsibleGroupBox(QWidget* parent = nullptr); | ||
|
||
explicit CollapsibleGroupBox(const QString& title, QWidget* parent = nullptr); | ||
|
||
~CollapsibleGroupBox() override; | ||
|
||
/** | ||
* Returns the current collapsed state of this group box. | ||
*/ | ||
bool isCollapsed() const; | ||
|
||
/** | ||
* Collapse or expand this group box. | ||
* | ||
* \param collapse Will collapse on true and expand on false | ||
*/ | ||
void setCollapsed(bool collapse); | ||
|
||
signals: | ||
|
||
/** Signal emitted when the group box collapsed/expanded state is changed, and when first shown */ | ||
void collapsedStateChanged(bool collapsed); | ||
|
||
public slots: | ||
|
||
void checkToggled(bool); | ||
|
||
void checkClicked(bool checked); | ||
|
||
void toggleCollapsed(); | ||
|
||
protected: | ||
void updateWidgets(); | ||
|
||
void showEvent(QShowEvent* event) override; | ||
|
||
void changeEvent(QEvent* event) override; | ||
|
||
void childEvent(QChildEvent *event) override; | ||
|
||
bool eventFilter(QObject* watched, QEvent* event) override; | ||
|
||
void initialize(); | ||
|
||
void loadState(); | ||
|
||
void saveState(); | ||
|
||
QString getSettingsKey() const; | ||
|
||
private: | ||
bool collapsed = false; | ||
bool shown = false; | ||
QToolButton* collapseButton = nullptr; | ||
|
||
QIcon collapseIcon; | ||
QIcon expandIcon; | ||
|
||
int ignoreVisibilityEvents = 0; | ||
std::unordered_set<QWidget*> collapsedWidgets; | ||
}; | ||
|
||
|
||
#endif //SCANTAILOR_COLLAPSIBLEGROUPBOX_H |
Oops, something went wrong.