Qt: Add CollapsibleWidget

pull/3780/head
Stenzek 2 months ago
parent 145474389d
commit 30d351e4e5
No known key found for this signature in database

@ -40,6 +40,8 @@ set(SRCS
biossettingswidget.ui
cardradiobutton.cpp
cardradiobutton.h
collapsiblewidget.cpp
collapsiblewidget.h
colorpickerbutton.cpp
colorpickerbutton.h
capturesettingswidget.cpp

@ -0,0 +1,122 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "collapsiblewidget.h"
#include <QtCore/QPropertyAnimation>
#include <QtGui/QPaintEvent>
#include <QtGui/QRegion>
#include <QtGui/QShowEvent>
#include <QtWidgets/QStyle>
#include <QtWidgets/QStyleOptionGroupBox>
#include <QtWidgets/QStylePainter>
#include <algorithm>
#include "moc_collapsiblewidget.cpp"
CollapsibleWidget::CollapsibleWidget(QWidget* parent) : QGroupBox(parent)
{
init();
}
CollapsibleWidget::CollapsibleWidget(const QString& title, QWidget* parent) : QGroupBox(title, parent)
{
init();
}
CollapsibleWidget::~CollapsibleWidget() = default;
void CollapsibleWidget::init()
{
setCheckable(true);
setChecked(false);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
m_animation = new QPropertyAnimation(this, "maximumHeight", this);
m_animation->setDuration(300);
m_animation->setEasingCurve(QEasingCurve::InOutCubic);
connect(this, &QGroupBox::toggled, this, &CollapsibleWidget::toggleCollapsed);
connect(m_animation, &QPropertyAnimation::finished, this, [this]() {
if (isChecked())
setMaximumHeight(QWIDGETSIZE_MAX);
});
setMaximumHeight(collapsedHeight());
}
int CollapsibleWidget::collapsedHeight() const
{
QStyleOptionGroupBox option;
initStyleOption(&option);
const QRect label_rect = style()->subControlRect(QStyle::CC_GroupBox, &option, QStyle::SC_GroupBoxLabel, this);
const QRect check_box_rect = style()->subControlRect(QStyle::CC_GroupBox, &option, QStyle::SC_GroupBoxCheckBox, this);
const int header_bottom = std::max(label_rect.bottom(), check_box_rect.bottom()) + 1;
return std::max(header_bottom + contentsMargins().bottom(), fontMetrics().height());
}
void CollapsibleWidget::updateExpandedHeight()
{
const int old_maximum_height = maximumHeight();
setMaximumHeight(QWIDGETSIZE_MAX);
m_expanded_height = sizeHint().height();
setMaximumHeight(old_maximum_height);
}
void CollapsibleWidget::toggleCollapsed(bool expanded)
{
m_animation->stop();
if (expanded)
updateExpandedHeight();
m_animation->setStartValue(height());
m_animation->setEndValue(expanded ? m_expanded_height : collapsedHeight());
m_animation->start();
}
void CollapsibleWidget::setCollapsed(bool collapsed)
{
setChecked(!collapsed);
}
void CollapsibleWidget::changeEvent(QEvent* event)
{
QGroupBox::changeEvent(event);
if (!isChecked() && (event->type() == QEvent::FontChange || event->type() == QEvent::StyleChange))
setMaximumHeight(collapsedHeight());
}
void CollapsibleWidget::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
QStylePainter painter(this);
QStyleOptionGroupBox option;
initStyleOption(&option);
const QRect indicator_rect = style()->subControlRect(QStyle::CC_GroupBox, &option, QStyle::SC_GroupBoxCheckBox, this);
if (!isChecked())
option.subControls &= ~QStyle::SC_GroupBoxFrame;
const QRegion group_box_region = QRegion(rect()).subtracted(indicator_rect.adjusted(-1, -1, 1, 1));
painter.setClipRegion(group_box_region);
painter.drawComplexControl(QStyle::CC_GroupBox, option);
painter.setClipping(false);
QStyleOption arrow_option;
arrow_option.initFrom(this);
arrow_option.rect = indicator_rect;
painter.drawPrimitive(isChecked() ? QStyle::PE_IndicatorArrowDown : QStyle::PE_IndicatorArrowRight, arrow_option);
}
void CollapsibleWidget::showEvent(QShowEvent* event)
{
QGroupBox::showEvent(event);
if (!isChecked())
setMaximumHeight(collapsedHeight());
}

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
#include <QtWidgets/QGroupBox>
class QPropertyAnimation;
class QPaintEvent;
class QShowEvent;
class CollapsibleWidget final : public QGroupBox
{
Q_OBJECT
public:
explicit CollapsibleWidget(QWidget* parent = nullptr);
explicit CollapsibleWidget(const QString& title, QWidget* parent = nullptr);
~CollapsibleWidget() override;
bool isCollapsed() const { return !isChecked(); }
void setCollapsed(bool collapsed);
protected:
void changeEvent(QEvent* event) override;
void paintEvent(QPaintEvent* event) override;
void showEvent(QShowEvent* event) override;
private:
void init();
int collapsedHeight() const;
void updateExpandedHeight();
void toggleCollapsed(bool expanded);
QPropertyAnimation* m_animation = nullptr;
int m_expanded_height = 0;
};

@ -14,6 +14,7 @@
<ClCompile Include="biossettingswidget.cpp" />
<ClCompile Include="cardradiobutton.cpp" />
<ClCompile Include="capturesettingswidget.cpp" />
<ClCompile Include="collapsiblewidget.cpp" />
<ClCompile Include="colorpickerbutton.cpp" />
<ClCompile Include="consolesettingswidget.cpp" />
<ClCompile Include="controllerbindingwidgets.cpp" />
@ -97,6 +98,7 @@
<QtMoc Include="capturesettingswidget.h" />
<QtMoc Include="asyncpixmaploader.h" />
<QtMoc Include="asynchttprequest.h" />
<QtMoc Include="collapsiblewidget.h" />
<ClInclude Include="controllersettingwidgetbinder.h" />
<QtMoc Include="memoryeditorwindow.h" />
<QtMoc Include="memoryviewwidget.h" />

@ -59,6 +59,7 @@
<ClCompile Include="svgiconengine.cpp" />
<ClCompile Include="svgwidget.cpp" />
<ClCompile Include="searchbox.cpp" />
<ClCompile Include="collapsiblewidget.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="qtutils.h" />
@ -130,6 +131,7 @@
<QtMoc Include="cardradiobutton.h" />
<QtMoc Include="animatedstackedwidget.h" />
<QtMoc Include="searchbox.h" />
<QtMoc Include="collapsiblewidget.h" />
</ItemGroup>
<ItemGroup>
<QtUi Include="consolesettingswidget.ui" />

Loading…
Cancel
Save