1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "collapsewidget.h"
6#include "detailsbutton.h"
7
8#include <QLabel>
9#include <QToolBox>
10#include <QPushButton>
11#include <QVBoxLayout>
12#include <QHBoxLayout>
13#include <QPropertyAnimation>
14#include <QDebug>
15#include <QEvent>
16#include <QPainter>
17#include <QtAlgorithms>
18
19class CollapseWidgetPrivate
20{
21 friend class CollapseWidget;
22 QString title;
23 DetailsButton *detailsButton = nullptr;
24 int defaultWidgetHeight = 500;
25 int minHeight = 0;
26 int maxHeight = 0;
27 int widgetSpace = 8;
28 QWidget *widget = nullptr;
29 QRect titleLabelRect;
30 QRect detailsButtonRect;
31 QRect widgetRect;
32 QVariantAnimation animation = nullptr;
33};
34
35CollapseWidget::CollapseWidget(QWidget *parent)
36 : QWidget (parent)
37 , d (new CollapseWidgetPrivate)
38{
39 setObjectName("CollapseWidget");
40 setBackgroundRole(QPalette::Light);
41 d->detailsButton = new DetailsButton(this);
42 d->minHeight = d->detailsButton->height();
43 d->maxHeight = d->detailsButton->height() + d->widgetSpace * 2;
44 setMinimumWidth(260);
45
46 d->animation.setDuration(200);
47 setAutoFillBackground(true);
48
49 QObject::connect(d->detailsButton, &DetailsButton::clicked,
50 this, &CollapseWidget::doChecked);
51
52 QObject::connect(&d->animation, &QVariantAnimation::valueChanged,
53 this, &CollapseWidget::resetHeight, Qt::UniqueConnection);
54}
55
56CollapseWidget::CollapseWidget(const QString &title, QWidget *widget, QWidget *parent)
57 :CollapseWidget(parent)
58{
59 setTitle(title);
60 setWidget(widget);
61}
62
63CollapseWidget::~CollapseWidget()
64{
65 if (d) {
66 if (d->detailsButton) {
67 delete d->detailsButton;
68 }
69 if (d->widget) {
70 delete d->widget;
71 }
72 delete d;
73 }
74}
75
76QWidget *CollapseWidget::takeWidget()
77{
78 QWidget *result = nullptr;
79 if (d->widget && this == d->widget->parent()) {
80 d->widget->setParent(nullptr);
81 }
82 return result;
83}
84
85void CollapseWidget::setWidget(QWidget *widget)
86{
87 if (d->widget) {
88 delete d->widget;
89 d->widget = nullptr;
90 }
91
92 if (widget){
93 d->widget = widget;
94 d->widget->setParent(this);
95 if (d->maxHeight == d->minHeight + d->widgetSpace * 2) {
96 d->maxHeight += d->defaultWidgetHeight;
97 }
98 d->maxHeight = qMax(d->maxHeight, widget->height() + d->widgetSpace * 2);
99 d->widget->resize(width(), d->maxHeight);
100 d->widget->show();
101 }
102}
103
104QWidget *CollapseWidget::widget()
105{
106 return d->widget;
107}
108
109void CollapseWidget::setTitle(const QString &title)
110{
111 d->title = title;
112}
113
114QString CollapseWidget::title()
115{
116 return d->title;
117}
118
119void CollapseWidget::setCheckable(bool checkable)
120{
121 if (d->detailsButton)
122 d->detailsButton->setCheckable(checkable);
123}
124
125bool CollapseWidget::isCheckable()
126{
127 if (d->detailsButton)
128 return d->detailsButton->isCheckable();
129 return false;
130}
131
132bool CollapseWidget::isChecked()
133{
134 if (d->detailsButton)
135 return d->detailsButton->isChecked();
136 return false;
137}
138
139void CollapseWidget::resizeEvent(QResizeEvent *event)
140{
141 QWidget::resizeEvent(event);
142
143 if (d->detailsButton) {
144 d->detailsButtonRect = { QPoint(event->size().width() - d->detailsButton->width(), 0),
145 d->detailsButton->size() };
146 d->detailsButton->setGeometry(d->detailsButtonRect);
147 }
148
149 if (d->widget) {
150 if (d->widget->width() != width() - d->widgetSpace * 2) {
151 d->widget->resize(width() - d->widgetSpace * 2, d->widget->height());
152 }
153 d->maxHeight = qMax(d->widget->height() + d->widgetSpace * 2, d->maxHeight);
154 QPoint widgetOffset = {d->widgetSpace, d->widgetSpace + d->minHeight};
155 QSize widgetResize = event->size() - QSize{d->widgetSpace * 2, d->widgetSpace * 2 + d->minHeight};
156 d->widget->setGeometry(QRect{widgetOffset, widgetResize});
157 }
158}
159
160void CollapseWidget::paintEvent(QPaintEvent *event)
161{
162 QWidget::paintEvent(event);
163 QPainter painter(this);
164 QRect rect = CollapseWidget::rect();
165 painter.drawLine(rect.topLeft() + QPoint{0, d->minHeight -1},
166 rect.topRight() + QPoint{0, d->minHeight -1});
167 painter.drawLine(rect.topLeft(), rect.topRight());
168 painter.drawLine(rect.topLeft(), rect.bottomLeft());
169 painter.drawLine(rect.topRight(), rect.bottomRight());
170 painter.drawLine(rect.bottomLeft(), rect.bottomRight());
171
172 QFont font(d->title);
173 QFontMetrics fontMetrics(this->font());
174 QRect textBoundingRect = fontMetrics.boundingRect(d->title);
175 QSize fontSize = textBoundingRect.size();
176 int fontPosX = textBoundingRect.x();
177 int fontPosY = textBoundingRect.y();
178 int textPosX = 4 + ( fontPosX > 0 ? fontPosX : - fontPosX);;
179 int textPosY = (d->detailsButton->height() - fontSize.height()) / 2
180 + ( fontPosY > 0 ? fontPosY: - fontPosY);
181 painter.drawText(QPoint{textPosX, textPosY}, d->title);
182}
183
184void CollapseWidget::setChecked(bool checked)
185{
186 if (d->detailsButton) {
187 d->detailsButton->setChecked(checked);
188 this->doChecked(checked);
189 }
190}
191
192void CollapseWidget::doChecked(bool checked)
193{
194 //默认展开状态, 点击时首先隐藏
195 if (d->widget) {
196 if (checked){
197 d->widget->hide();
198 d->animation.setStartValue(d->maxHeight);
199 d->animation.setEndValue(d->minHeight);
200 } else{
201 d->widget->show();
202 d->animation.setStartValue(d->minHeight);
203 d->animation.setEndValue(qMax(d->maxHeight, d->minHeight));
204 }
205 d->animation.start();
206 }
207}
208
209void CollapseWidget::resetHeight(const QVariant &value)
210{
211 setFixedHeight(value.toInt());
212}
213