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 | |
19 | class 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 | |
35 | CollapseWidget::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 | |
56 | CollapseWidget::CollapseWidget(const QString &title, QWidget *widget, QWidget *parent) |
57 | :CollapseWidget(parent) |
58 | { |
59 | setTitle(title); |
60 | setWidget(widget); |
61 | } |
62 | |
63 | CollapseWidget::~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 | |
76 | QWidget *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 | |
85 | void 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 | |
104 | QWidget *CollapseWidget::widget() |
105 | { |
106 | return d->widget; |
107 | } |
108 | |
109 | void CollapseWidget::setTitle(const QString &title) |
110 | { |
111 | d->title = title; |
112 | } |
113 | |
114 | QString CollapseWidget::title() |
115 | { |
116 | return d->title; |
117 | } |
118 | |
119 | void CollapseWidget::setCheckable(bool checkable) |
120 | { |
121 | if (d->detailsButton) |
122 | d->detailsButton->setCheckable(checkable); |
123 | } |
124 | |
125 | bool CollapseWidget::isCheckable() |
126 | { |
127 | if (d->detailsButton) |
128 | return d->detailsButton->isCheckable(); |
129 | return false; |
130 | } |
131 | |
132 | bool CollapseWidget::isChecked() |
133 | { |
134 | if (d->detailsButton) |
135 | return d->detailsButton->isChecked(); |
136 | return false; |
137 | } |
138 | |
139 | void 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 | |
160 | void 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 | |
184 | void CollapseWidget::setChecked(bool checked) |
185 | { |
186 | if (d->detailsButton) { |
187 | d->detailsButton->setChecked(checked); |
188 | this->doChecked(checked); |
189 | } |
190 | } |
191 | |
192 | void 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 | |
209 | void CollapseWidget::resetHeight(const QVariant &value) |
210 | { |
211 | setFixedHeight(value.toInt()); |
212 | } |
213 | |