diff --git a/examples/collections/progressbarexample.cpp b/examples/collections/progressbarexample.cpp index 8c7010c9e..a79dfe507 100644 --- a/examples/collections/progressbarexample.cpp +++ b/examples/collections/progressbarexample.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "progressbarexample.h" DWIDGET_USE_NAMESPACE @@ -28,6 +29,7 @@ ProgressBarExampleWindow::ProgressBarExampleWindow(QWidget *parent) addExampleWindow(new DProgressBarExample(this)); addExampleWindow(new DWaterProgressExample(this)); addExampleWindow(new DColoredProgressBarExample(this)); + addExampleWindow(new DIndeterminateProgressBarExample(this)); } DProgressBarExample::DProgressBarExample(QWidget *parent) @@ -181,3 +183,32 @@ int DColoredProgressBarExample::getFixedHeight() const { return 200; } + +DIndeterminateProgressBarExample::DIndeterminateProgressBarExample(QWidget *parent) + : ExampleWindowInterface(parent) +{ + auto mainLayout = new QVBoxLayout(this); + auto indeterBar = new DIndeterminateProgressbar(); + indeterBar->setFixedSize(500, 35); + mainLayout->addWidget(indeterBar, 0, Qt::AlignCenter); + setLayout(mainLayout); +} + +QString DIndeterminateProgressBarExample::getTitleName() const +{ + return "DIndeterminateProgressbar"; +} + +QString DIndeterminateProgressBarExample::getDescriptionInfo() const +{ + return QString("一个模糊进度条,不展示具体进度值,\n" + "用于等待时间不确定的情况。主要用\n" + "在小工具主窗口内部,作为一个中间状态\n" + "展示给用户,最终的结果往往会跟随成功\n" + "或者失败的图标。"); +} + +int DIndeterminateProgressBarExample::getFixedHeight() const +{ + return 200; +} diff --git a/examples/collections/progressbarexample.h b/examples/collections/progressbarexample.h index fb71257c5..c22ee1319 100644 --- a/examples/collections/progressbarexample.h +++ b/examples/collections/progressbarexample.h @@ -53,4 +53,16 @@ class DColoredProgressBarExample : public ExampleWindowInterface int getFixedHeight() const override; }; +class DIndeterminateProgressBarExample : public ExampleWindowInterface +{ + Q_OBJECT + +public: + explicit DIndeterminateProgressBarExample(QWidget *parent = nullptr); + + QString getTitleName() const override; + QString getDescriptionInfo() const override; + int getFixedHeight() const override; +}; + #endif // PROGRESSBAREXAMPLE_H diff --git a/include/DWidget/DIndeterminateProgressbar b/include/DWidget/DIndeterminateProgressbar new file mode 100644 index 000000000..1d45e0b4a --- /dev/null +++ b/include/DWidget/DIndeterminateProgressbar @@ -0,0 +1 @@ +#include "dindeterminateprogressbar.h" diff --git a/include/widgets/dindeterminateprogressbar.h b/include/widgets/dindeterminateprogressbar.h new file mode 100644 index 000000000..7883b1bea --- /dev/null +++ b/include/widgets/dindeterminateprogressbar.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DINDETERMINATEPROGRESSBAR_H +#define DINDETERMINATEPROGRESSBAR_H + +#include +#include + +class DIndeterminateProgressbarPrivate; +class DIndeterminateProgressbar : public QWidget, public DTK_CORE_NAMESPACE::DObject +{ + Q_OBJECT +public: + explicit DIndeterminateProgressbar(QWidget *parent = nullptr); + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +private: + D_DECLARE_PRIVATE(DIndeterminateProgressbar) +}; + +#endif // DINDETERMINATEPROGRESSBAR_H diff --git a/src/widgets/dindeterminateprogressbar.cpp b/src/widgets/dindeterminateprogressbar.cpp new file mode 100644 index 000000000..bb0713140 --- /dev/null +++ b/src/widgets/dindeterminateprogressbar.cpp @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "private/dindeterminateprogressbar_p.h" + +#include + +#include +#include +#include +#include +#include + +const int SPOT_WIDGET_WIDTH = 200; + +DIndeterminateProgressbarPrivate::DIndeterminateProgressbarPrivate(DIndeterminateProgressbar *qq) + : DObjectPrivate(qq) + , m_sliderWidget(new QWidget(qq)) + , m_timer(new QTimer(qq)) + , m_leftToRight(true) + , m_spotWidget(new QWidget(qq)) + , m_animation(new QPropertyAnimation(m_spotWidget, "pos", qq)) +{ +} + +DIndeterminateProgressbar::DIndeterminateProgressbar(QWidget *parent) + : QWidget(parent) + , DObject(*new DIndeterminateProgressbarPrivate(this)) +{ + D_D(DIndeterminateProgressbar); + d->m_spotWidget->setFixedSize(SPOT_WIDGET_WIDTH, height()); + d->m_spotWidget->move(-SPOT_WIDGET_WIDTH, 0); + + d->m_sliderWidget->setFixedWidth(150); + d->m_sliderWidget->move(0, 0); + + d->m_timer->setInterval(10); + static int step = 0; + connect(d->m_timer, &QTimer::timeout, this, [this, d]() { + if (d->m_sliderWidget->geometry().right() >= rect().right()) { + d->m_leftToRight = false; + } + + if (d->m_sliderWidget->geometry().left() <= rect().left()) { + d->m_leftToRight = true; + } + + d->m_leftToRight ? step += 2 : step -= 2; + d->m_sliderWidget->move(step, 0); + }); + d->m_timer->start(); +} + +void DIndeterminateProgressbar::resizeEvent(QResizeEvent *e) +{ + D_D(DIndeterminateProgressbar); + d->m_sliderWidget->setFixedHeight(height()); + d->m_spotWidget->setFixedSize(SPOT_WIDGET_WIDTH, height()); + + d->m_animation->setStartValue(QPoint(-SPOT_WIDGET_WIDTH, 0)); + d->m_animation->setEndValue(QPoint(rect().right(), 0)); + d->m_animation->setDuration(3000); + d->m_animation->setEasingCurve(QEasingCurve::InQuad); + d->m_animation->start(); + connect(d->m_animation, &QPropertyAnimation::finished, this, [d]() { + d->m_animation->start(); + }); + QWidget::resizeEvent(e); +} + +void DIndeterminateProgressbar::paintEvent(QPaintEvent *e) +{ + D_D(DIndeterminateProgressbar); + QWidget::paintEvent(e); + QPainter p(this); + + p.setRenderHint(QPainter::Antialiasing); + int radius; + this->height() <= DTK_WIDGET_NAMESPACE::DStyle::pixelMetric(style(), DTK_WIDGET_NAMESPACE::DStyle::PM_FrameRadius) * 2 + ? radius = height() / 2 + : radius = DTK_WIDGET_NAMESPACE::DStyle::pixelMetric(style(), DTK_WIDGET_NAMESPACE::DStyle::PM_FrameRadius); + + p.setBrush(QColor(0, 0, 0, int(0.1 * 255))); + p.setPen(Qt::NoPen); + + p.drawRoundedRect(rect(), radius, radius); + + QPen pen; + pen.setWidth(1); + pen.setColor(QColor(0, 0, 0, int(0.2 * 255))); + p.setBrush(Qt::NoBrush); + p.setPen(pen); + p.drawRoundedRect(rect().marginsRemoved(QMargins(1, 1, 1, 1)), radius, radius); + + p.setPen(Qt::NoPen); + p.setBrush(palette().highlight().color()); + p.drawRoundedRect(d->m_sliderWidget->geometry(), radius, radius); + + pen.setColor(QColor(0, 0, 0, int(0.3 * 255))); + p.setBrush(Qt::NoBrush); + p.setPen(pen); + p.drawRoundedRect(d->m_sliderWidget->geometry().marginsRemoved(QMargins(1, 1, 1, 1)), radius, radius); + + if (d->m_sliderWidget->width() < d->m_spotWidget->width() / 2) + return; + + QPointF pointStart(d->m_spotWidget->geometry().left(), d->m_spotWidget->geometry().center().y()); + QPointF pointEnd(d->m_spotWidget->geometry().right(), d->m_spotWidget->geometry().center().y()); + + QColor shadowColor(0, 0, 0, int(0.15 * 255)); + QColor spotColor(255, 255, 255, int(0.5 * 255)); + QColor highLightColor(palette().highlight().color()); + + QLinearGradient linear(pointStart, pointEnd); + linear.setColorAt(0, highLightColor); + linear.setColorAt(0.35, shadowColor); + linear.setColorAt(0.5, spotColor); + linear.setColorAt(0.65, shadowColor); + linear.setColorAt(1, highLightColor); + linear.setSpread(QGradient::PadSpread); + linear.setInterpolationMode(QLinearGradient::InterpolationMode::ColorInterpolation); + + p.setBrush(linear); + p.setPen(Qt::NoPen); + + QPainterPath clipPath; + clipPath.addRoundedRect(d->m_sliderWidget->geometry(), radius, radius); + p.setClipPath(clipPath); + p.setClipping(true); + p.drawRoundedRect(d->m_spotWidget->geometry().marginsRemoved(QMargins(2, 2, 2, 2)), radius, radius); + p.setClipping(false); +} diff --git a/src/widgets/dsearchedit.cpp b/src/widgets/dsearchedit.cpp index afafab3a0..fa5fdb3a0 100644 --- a/src/widgets/dsearchedit.cpp +++ b/src/widgets/dsearchedit.cpp @@ -39,6 +39,9 @@ DWIDGET_BEGIN_NAMESPACE DCORE_USE_NAMESPACE DGUI_USE_NAMESPACE +constexpr int ANI_DURATION = 200; +constexpr int HIDE_CURSOR_MARGIN = -4; + #ifdef ENABLE_AI class VoiceDevice : public QIODevice { @@ -162,7 +165,7 @@ class VoiceDevice : public QIODevice \class Dtk::Widget::DSearchEdit \inmodule dtkwidget @brief The DSearchEdit class provides input box widget specifically used for searching - + @details The DSearchEdit class inherits from the DLineEdit class. Compared to regular input box widgets, DSearchEdit provides search button instructions,thus users can use it more naturally. As shown in the figure: @@ -270,7 +273,11 @@ DSearchEditPrivate::DSearchEditPrivate(DSearchEdit *q) , action(nullptr) , iconWidget(nullptr) , label(nullptr) + , animation(new QPropertyAnimation) { + animation->setPropertyName("pos"); + animation->setEasingCurve(QEasingCurve::OutCubic); + animation->setDuration(ANI_DURATION); } DSearchEditPrivate::~DSearchEditPrivate() @@ -377,15 +384,31 @@ void DSearchEditPrivate::_q_toEditMode(bool focus) { D_Q(DSearchEdit); - if (focus || !q->lineEdit()->text().isEmpty()) { - action->setVisible(true); - iconWidget->setVisible(false); - lineEdit->setPlaceholderText(placeholderText); - } else { - action->setVisible(false); - iconWidget->setVisible(true); - lineEdit->setPlaceholderText(QString()); - } + if (animation->state() == QPropertyAnimation::Running) + return; + + auto textMargins = q->lineEdit()->textMargins(); + QMargins marginsInAnimation(HIDE_CURSOR_MARGIN, 0, 0, 0); + + if (!animation->parent()) + animation->setParent(iconWidget); + + animation->setTargetObject(iconWidget); + animation->setStartValue(QPoint(q->lineEdit()->geometry().center().x() - iconWidget->width() / 2, iconWidget->pos().y())); + animation->setEndValue(QPoint(0, iconWidget->pos().y())); + + q->connect(animation, &QPropertyAnimation::finished, q, [q, this, textMargins]() { + q->lineEdit()->setTextMargins(textMargins); + if (animation->direction() == QPropertyAnimation::Direction::Forward) { + action->setVisible(true); + iconWidget->setVisible(false); + lineEdit->setPlaceholderText(placeholderText); + } else { + iconWidget->setVisible(true); + lineEdit->setPlaceholderText(QString()); + iconWidget->move(QPoint(q->lineEdit()->geometry().center().x() - iconWidget->width() / 2, iconWidget->pos().y())); + } + }); #ifdef ENABLE_AI //Focus disappears, clear voice check @@ -394,6 +417,20 @@ void DSearchEditPrivate::_q_toEditMode(bool focus) _q_onVoiceActionTrigger(false); } #endif + + if (!q->lineEdit()->text().isEmpty()) + return; + + if (focus) { + animation->setDirection(QPropertyAnimation::Direction::Forward); + } else { + action->setVisible(false); + animation->setDirection(QPropertyAnimation::Direction::Backward); + } + + iconWidget->setVisible(true); + q->lineEdit()->setTextMargins(marginsInAnimation); + animation->start(); } void DSearchEditPrivate::_q_onVoiceActionTrigger(bool checked) diff --git a/src/widgets/dtitlebar.cpp b/src/widgets/dtitlebar.cpp index 23d604ad0..6c32eeaf1 100644 --- a/src/widgets/dtitlebar.cpp +++ b/src/widgets/dtitlebar.cpp @@ -775,7 +775,7 @@ void DTitlebarPrivate::setIconVisible(bool visible) return; if (visible) { - if (auto spacerItem = dynamic_cast(leftLayout->takeAt(0))) + if (dynamic_cast(leftLayout->itemAt(0))) delete leftLayout->takeAt(0); leftLayout->insertSpacing(0, 10); diff --git a/src/widgets/private/dindeterminateprogressbar_p.h b/src/widgets/private/dindeterminateprogressbar_p.h new file mode 100644 index 000000000..330504358 --- /dev/null +++ b/src/widgets/private/dindeterminateprogressbar_p.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DINDETERMINATEPROGRESSBAR_P_H +#define DINDETERMINATEPROGRESSBAR_P_H + +#include +#include + +#include + +class QPropertyAnimation; +class DIndeterminateProgressbarPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate +{ +public: + DIndeterminateProgressbarPrivate(DIndeterminateProgressbar *qq); + + QWidget *m_sliderWidget; + QTimer *m_timer; + bool m_leftToRight; + QWidget *m_spotWidget; + QPropertyAnimation *m_animation; + +private: + D_DECLARE_PUBLIC(DIndeterminateProgressbar) +}; + +#endif // DINDETERMINATEPROGRESSBAR_P_H diff --git a/src/widgets/private/dsearchedit_p.h b/src/widgets/private/dsearchedit_p.h index d5c3eaac3..58d614b33 100644 --- a/src/widgets/private/dsearchedit_p.h +++ b/src/widgets/private/dsearchedit_p.h @@ -9,6 +9,7 @@ #include #include +#include QT_BEGIN_NAMESPACE class QAudioInput; @@ -37,6 +38,7 @@ class DSearchEditPrivate : DLineEditPrivate QWidget *iconWidget; QLabel *label; + QPropertyAnimation *animation; #ifdef ENABLE_AI QAction *voiceAction = nullptr;