123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- #include "PickerBase.h"
- #include "QFluentWidgets.h"
- #include "Widgets/CycleListWidget.h"
- #include "Widgets/Button.h"
- #include "Widgets/ScrollArea.h"
- #include <QPainter>
- #include <QApplication>
- #include <QScreen>
- #include <QDebug>
- ItemMaskWidget::ItemMaskWidget(const QList<CycleListWidget *> &list, QWidget *parent)
- : QWidget(parent), listWidgets(list)
- {
- setFixedHeight(37);
- FluentStyleSheet::apply("TIME_PICKER", this);
- }
- void ItemMaskWidget::paintEvent(QPaintEvent * /*event*/)
- {
- QPainter painter(this);
- painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
- // draw background
- painter.setPen(Qt::NoPen);
- painter.setBrush(themeColor());
- painter.drawRoundedRect(this->rect().adjusted(4, 0, -3, 0), 5, 5);
- // draw text
- if (QFWIns.isDarkTheme()) {
- painter.setPen(Qt::black);
- } else {
- painter.setPen(Qt::white);
- }
- painter.setFont(this->font());
- int w = 0;
- int h = this->height();
- for (auto p : listWidgets) {
- painter.save();
- // draw first item's text
- int x = p->itemSize().width() / 2 + 4 + this->x();
- QListWidgetItem *item1 = p->itemAt(QPoint(x, this->y() + 6));
- item1 = p->currentItem();
- if (!item1) {
- painter.restore();
- continue;
- }
- int iw = item1->sizeHint().width();
- int y = p->visualItemRect(item1).y();
- painter.translate(w, y - this->y() + 7);
- drawText(item1, &painter, 0);
- // draw second item's text
- QListWidgetItem *item2 = p->itemAt(this->pos() + QPoint(x, h - 6));
- drawText(item2, &painter, h);
- painter.restore();
- w += (iw + 8); // margin: 0 4px;
- }
- }
- void ItemMaskWidget::drawText(QListWidgetItem *item, QPainter *p, int y)
- {
- int align = item->textAlignment();
- int w = item->sizeHint().width();
- int h = item->sizeHint().height();
- QRectF rect;
- if (align & Qt::AlignLeft) {
- rect = QRectF(15, y, w, h); // padding-left: 11px
- } else if (align & Qt::AlignRight) {
- rect = QRectF(4, y, w - 15, h); // padding-right: 11px
- } else if (align & Qt::AlignCenter) {
- rect = QRectF(4, y, w, h);
- }
- p->drawText(rect, align, item->text());
- }
- PickerColumnButton::PickerColumnButton(const QString &name, const QVariantList &items, int width,
- Qt::AlignmentFlag align, PickerColumnFormatter *formatter, QWidget *parent)
- : QPushButton(parent), m_name(name), m_value("")
- {
- setItems(items);
- setAlignment(align);
- setFormatter(formatter);
- setFixedSize(width, 30);
- setObjectName("pickerButton");
- setProperty("hasBorder", false);
- setAttribute(Qt::WA_TransparentForMouseEvents);
- }
- QVariantList PickerColumnButton::items() const
- {
- QVariantList ret;
- for (const auto &item : m_items) {
- ret.append(m_formatter->encode(item));
- }
- return ret;
- }
- void PickerColumnButton::setItems(const QVariantList &items)
- {
- m_items = items;
- }
- QString PickerColumnButton::value() const
- {
- if (m_value.isEmpty()) {
- return m_value;
- }
- return m_formatter->encode(m_value);
- }
- void PickerColumnButton::setValue(const QString &value)
- {
- m_value = value;
- if (value.isEmpty()) {
- setText(this->name());
- setProperty("hasValue", false);
- } else {
- setText(this->value());
- setProperty("hasValue", true);
- }
- setStyle(QApplication::style());
- }
- Qt::AlignmentFlag PickerColumnButton::alignment() const
- {
- return m_align;
- }
- void PickerColumnButton::setAlignment(const Qt::AlignmentFlag &align)
- {
- if (align == Qt::AlignLeft) {
- setProperty("align", "left");
- } else if (align == Qt::AlignRight) {
- setProperty("align", "right");
- } else {
- setProperty("align", "center");
- }
- m_align = align;
- setStyle(QApplication::style());
- }
- PickerColumnFormatter *PickerColumnButton::formatter() const
- {
- return m_formatter;
- }
- void PickerColumnButton::setFormatter(PickerColumnFormatter *formatter)
- {
- if (!formatter) {
- m_formatter = new PickerColumnFormatter(this);
- } else {
- m_formatter = formatter;
- }
- }
- QString PickerColumnButton::name() const
- {
- return m_name;
- }
- void PickerColumnButton::setName(const QString &name)
- {
- if (this->text() == m_name) {
- this->setText(name);
- }
- m_name = name;
- }
- PickerBase::PickerBase(QWidget *parent) : QPushButton(parent)
- {
- hBoxLayout = new QHBoxLayout(this);
- hBoxLayout->setSpacing(0);
- hBoxLayout->setContentsMargins(0, 0, 0, 0);
- hBoxLayout->setSizeConstraint(QHBoxLayout::SetFixedSize);
- FluentStyleSheet::apply("TIME_PICKER", this);
- connect(this, &PickerBase::clicked, this, &PickerBase::showPanel);
- }
- void PickerBase::addColumn(const QString &name, const QVariantList &items, int width, Qt::AlignmentFlag align,
- PickerColumnFormatter *formatter)
- {
- PickerColumnButton *button = new PickerColumnButton(name, items, width, align, formatter, this);
- columns.append(button);
- hBoxLayout->addWidget(button, 0, Qt::AlignLeft);
- // update the style of buttons
- for (int i = 0; i < columns.count() - 1; ++i) {
- columns[i]->setProperty("hasBorder", true);
- columns[i]->setStyle(QApplication::style());
- }
- }
- #define checkColumnIndex(index) \
- do { \
- if (index < 0 || index >= this->columns.count()) \
- return; \
- } while (0)
- void PickerBase::setColumnAlignment(int index, Qt::AlignmentFlag align)
- {
- checkColumnIndex(index);
- columns[index]->setAlignment(align);
- }
- void PickerBase::setColumnWidth(int index, int width)
- {
- checkColumnIndex(index);
- columns[index]->setFixedWidth(width);
- }
- void PickerBase::setColumnTight(int index)
- {
- checkColumnIndex(index);
- QFontMetrics fm = fontMetrics();
- int w = -1;
- for (auto c : columns[index]->items()) {
- if (w < fm.width(c.toString())) {
- w = fm.width(c.toString());
- }
- }
- w += 30;
- setColumnWidth(index, w);
- }
- void PickerBase::setColumnVisible(int index, bool visible)
- {
- checkColumnIndex(index);
- columns[index]->setVisible(visible);
- }
- QStringList PickerBase::value() const
- {
- QStringList ret;
- for (auto c : columns) {
- if (c->isVisible()) {
- ret.append(c->value());
- }
- }
- return ret;
- }
- void PickerBase::setColumnValue(int index, const QString &value)
- {
- checkColumnIndex(index);
- columns[index]->setValue(value);
- }
- void PickerBase::setColumnFormatter(int index, PickerColumnFormatter *formatter)
- {
- checkColumnIndex(index);
- columns[index]->setFormatter(formatter);
- }
- void PickerBase::setColumnItems(int index, const QVariantList &items)
- {
- checkColumnIndex(index);
- columns[index]->setItems(items);
- }
- QVariant PickerBase::encodeValue(int index, const QVariant &value)
- {
- if (index < 0 || index >= this->columns.count())
- return QVariant();
- return columns.at(index)->formatter()->encode(value);
- }
- QVariant PickerBase::decodeValue(int index, const QVariant &value)
- {
- if (index < 0 || index >= this->columns.count())
- return QVariant();
- PickerColumnFormatter *f = columns.at(index)->formatter();
- QVariant v = f->decode(value);
- return columns.at(index)->formatter()->decode(value);
- }
- QStringList PickerBase::panelInitialValue()
- {
- return value();
- }
- void PickerBase::setColumn(int index, const QString &name, const QVariantList & /*items*/, int width,
- Qt::AlignmentFlag align)
- {
- checkColumnIndex(index);
- PickerColumnButton *button = columns.at(index);
- button->setText(name);
- button->setFixedWidth(width);
- button->setAlignment(align);
- }
- void PickerBase::clearColumns()
- {
- while (!columns.isEmpty()) {
- PickerColumnButton *btn = columns.takeFirst();
- hBoxLayout->removeWidget(btn);
- btn->deleteLater();
- }
- }
- void PickerBase::showPanel()
- {
- PickerPanel *panel = new PickerPanel(this);
- for (auto column : columns) {
- if (column->isVisible()) {
- panel->addColumn(column->items(), column->width(), column->alignment());
- }
- }
- panel->setValue(this->panelInitialValue());
- connect(panel, &PickerPanel::confirmed, this, &PickerBase::onConfirmed);
- connect(panel, &PickerPanel::columnValueChanged,
- [=](int index, const QString &value) { onColumnValueChanged(panel, index, value); });
- panel->exec(this->mapToGlobal(QPoint(0, -37 * 4)));
- }
- void PickerBase::onConfirmed(const QStringList &value)
- {
- for (int i = 0; i < value.count(); ++i) {
- setColumnValue(i, value.at(i));
- }
- }
- void PickerBase::onColumnValueChanged(PickerPanel *panel, int index, const QString &value)
- {
- Q_UNUSED(panel)
- Q_UNUSED(index)
- Q_UNUSED(value)
- }
- void PickerBase::mousePressEvent(QMouseEvent *event)
- {
- setButtonProperty("pressed", true);
- QPushButton::mousePressEvent(event);
- }
- void PickerBase::mouseReleaseEvent(QMouseEvent *event)
- {
- setButtonProperty("pressed", false);
- QPushButton::mouseReleaseEvent(event);
- }
- void PickerBase::enterEvent(QEvent * /*event*/)
- {
- setButtonProperty("enter", true);
- }
- void PickerBase::leaveEvent(QEvent * /*event*/)
- {
- setButtonProperty("enter", false);
- }
- void PickerBase::setButtonProperty(const char *name, const QVariant &value)
- {
- for (auto c : columns) {
- c->setProperty(name, value);
- c->setStyle(QApplication::style());
- }
- }
- PickerPanel::PickerPanel(QWidget *parent) : QWidget(parent)
- {
- m_itemHeight = 37;
- m_view = new QFrame(this);
- m_itemMaskWidget = new ItemMaskWidget(m_listWidgets, this);
- m_hSeparatorWidget = new SeparatorWidget(Qt::Horizontal, m_view);
- m_yesButton = new TransparentToolButton(NEWFLICON(FluentIcon, ACCEPT), m_view);
- m_cancelButton = new TransparentToolButton(NEWFLICON(FluentIcon, CLOSE), m_view);
- m_hBoxLayout = new QHBoxLayout(this);
- m_listLayout = new QHBoxLayout();
- m_buttonLayout = new QHBoxLayout();
- m_vBoxLayout = new QVBoxLayout(m_view);
- m_isExpanded = false;
- m_ani = nullptr;
- m_shadowEffect = nullptr;
- initWidget();
- }
- void PickerPanel::initWidget()
- {
- setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);
- setAttribute(Qt::WA_TranslucentBackground);
- setShadowEffect();
- m_yesButton->setIconSize(QSize(16, 16));
- m_cancelButton->setIconSize(QSize(13, 13));
- m_yesButton->setFixedHeight(33);
- m_cancelButton->setFixedHeight(33);
- m_hBoxLayout->setContentsMargins(12, 8, 12, 20);
- m_hBoxLayout->addWidget(m_view, 1, Qt::AlignCenter);
- m_hBoxLayout->setSizeConstraint(QHBoxLayout::SetMinimumSize);
- m_vBoxLayout->setSpacing(0);
- m_vBoxLayout->setContentsMargins(0, 0, 0, 0);
- m_vBoxLayout->addLayout(m_listLayout, 1);
- m_vBoxLayout->addWidget(m_hSeparatorWidget);
- m_vBoxLayout->addLayout(m_buttonLayout, 1);
- m_vBoxLayout->setSizeConstraint(QVBoxLayout::SetMinimumSize);
- m_buttonLayout->setSpacing(6);
- m_buttonLayout->setContentsMargins(3, 3, 3, 3);
- m_buttonLayout->addWidget(m_yesButton);
- m_buttonLayout->addWidget(m_cancelButton);
- m_yesButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- m_cancelButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- connect(m_yesButton, &TransparentToolButton::clicked, this, &PickerPanel::fadeOut);
- connect(m_yesButton, &TransparentToolButton::clicked, [this]() { emit confirmed(this->value()); });
- connect(m_cancelButton, &TransparentToolButton::clicked, this, &PickerPanel::fadeOut);
- m_view->setObjectName("view");
- FluentStyleSheet::apply("TIME_PICKER", this);
- }
- void PickerPanel::setShadowEffect(int blurRadius, const QPointF &offset, const QColor &color)
- {
- m_shadowEffect = new QGraphicsDropShadowEffect(m_view);
- m_shadowEffect->setBlurRadius(blurRadius);
- m_shadowEffect->setOffset(offset);
- m_shadowEffect->setColor(color);
- m_view->setGraphicsEffect(nullptr);
- m_view->setGraphicsEffect(m_shadowEffect);
- }
- void PickerPanel::addColumn(const QVariantList &items, int width, Qt::AlignmentFlag align)
- {
- if (!m_listWidgets.isEmpty()) {
- m_listLayout->addWidget(new SeparatorWidget(Qt::Vertical, m_view));
- }
- CycleListWidget *w = new CycleListWidget(items, QSize(width, m_itemHeight), align, this);
- connect(w->vScrollBar, &SmoothScrollBar::valueChanged, m_itemMaskWidget, QOverload<>::of(&ItemMaskWidget::update));
- const int N = m_listWidgets.count();
- connect(w, &CycleListWidget::currentItemChanged,
- [N, this](QListWidgetItem *item) { emit columnValueChanged(N, item->text()); });
- m_listWidgets.append(w);
- m_itemMaskWidget->listWidgets = m_listWidgets;
- m_listLayout->addWidget(w);
- }
- /// return the value of columns
- QStringList PickerPanel::value() const
- {
- QStringList ret;
- for (auto w : m_listWidgets) {
- ret << w->currentItem()->text();
- }
- return ret;
- }
- /// set the value of columns
- void PickerPanel::setValue(const QStringList &value)
- {
- if (value.count() != m_listWidgets.count()) {
- return;
- }
- for (int i = 0; i < value.count(); ++i) {
- m_listWidgets.at(i)->setSelectedItem(value.at(i));
- }
- }
- QString PickerPanel::columnValue(int index) const
- {
- if (index < 0 || index >= m_listWidgets.count()) {
- return "";
- }
- CycleListWidget *w = m_listWidgets.at(index);
- QString s = w->currentItem()->text();
- return m_listWidgets.at(index)->currentItem()->text();
- }
- void PickerPanel::setColumnValue(int index, const QString &value)
- {
- if (index < 0 || index >= m_listWidgets.count()) {
- return;
- }
- m_listWidgets.at(index)->setSelectedItem(value);
- }
- CycleListWidget *PickerPanel::column(int index) const
- {
- if (index < 0 || index >= m_listWidgets.count()) {
- return nullptr;
- }
- return m_listWidgets.at(index);
- }
- void PickerPanel::exec(const QPoint &pos, bool ani)
- {
- if (isVisible()) {
- return;
- }
- // show before running animation, or the height calculation will be wrong
- this->show();
- QRect rect = QApplication::screenAt(QCursor::pos())->availableGeometry();
- int w = this->width() + 5;
- int h = this->height();
- QPoint newPos = pos;
- newPos.setX(qMin(pos.x() - this->layout()->contentsMargins().left(), rect.right() - w));
- newPos.setY(qMax(rect.top(), qMin(pos.y() - 4, rect.bottom() - h + 5)));
- this->move(newPos);
- if (!ani) {
- return;
- }
- m_isExpanded = false;
- m_ani = new QPropertyAnimation(m_view, "windowOpacity", this);
- connect(m_ani, &QPropertyAnimation::valueChanged, this, &PickerPanel::onAniValueChanged);
- m_ani->setStartValue(0);
- m_ani->setEndValue(1);
- m_ani->setDuration(150);
- m_ani->setEasingCurve(QEasingCurve::OutQuad);
- m_ani->start();
- }
- void PickerPanel::resizeEvent(QResizeEvent * /*event*/)
- {
- m_itemMaskWidget->resize(m_view->width() - 3, m_itemHeight);
- QMargins m = m_hBoxLayout->contentsMargins();
- m_itemMaskWidget->move(m.left() + 2, m.top() + 148);
- }
- void PickerPanel::fadeOut()
- {
- m_isExpanded = true;
- m_ani = new QPropertyAnimation(this, "windowOpacity", this);
- connect(m_ani, &QPropertyAnimation::valueChanged, this, &PickerPanel::onAniValueChanged);
- connect(m_ani, &QPropertyAnimation::finished, this, &PickerPanel::deleteLater);
- m_ani->setStartValue(1);
- m_ani->setEndValue(0);
- m_ani->setDuration(150);
- m_ani->setEasingCurve(QEasingCurve::OutQuad);
- m_ani->start();
- }
- void PickerPanel::onAniValueChanged(const QVariant &value)
- {
- QMargins m = this->layout()->contentsMargins();
- int w = m_view->width() + m.left() + m.right() + 120;
- int h = m_view->height() + m.top() + m.bottom() + 12;
- double opacity = value.toDouble();
- if (!m_isExpanded) {
- int y = int(h / 2 * (1 - opacity));
- setMask(QRegion(0, y, w, h - y * 2));
- } else {
- int y = int(h / 3 * (1 - opacity));
- setMask(QRegion(0, y, w, h - y * 2));
- }
- }
|