| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "export/view/export_view_progress.h"
- #include "ui/effects/animations.h"
- #include "ui/widgets/labels.h"
- #include "ui/widgets/buttons.h"
- #include "ui/wrap/fade_wrap.h"
- #include "ui/wrap/vertical_layout.h"
- #include "lang/lang_keys.h"
- #include "styles/style_boxes.h"
- #include "styles/style_export.h"
- namespace Export {
- namespace View {
- namespace {
- constexpr auto kShowSkipFileTimeout = 5 * crl::time(1000);
- } // namespace
- class ProgressWidget::Row : public Ui::RpWidget {
- public:
- Row(QWidget *parent, Content::Row &&data);
- void updateData(Content::Row &&data);
- protected:
- int resizeGetHeight(int newWidth) override;
- void paintEvent(QPaintEvent *e) override;
- private:
- struct Instance {
- base::unique_qptr<Ui::FadeWrap<Ui::FlatLabel>> label;
- base::unique_qptr<Ui::FadeWrap<Ui::FlatLabel>> info;
- float64 value = 0.;
- Ui::Animations::Simple progress;
- bool hiding = true;
- Ui::Animations::Simple opacity;
- };
- void fillCurrentInstance();
- void hideCurrentInstance();
- void setInstanceProgress(Instance &instance, float64 progress);
- void toggleInstance(Instance &data, bool shown);
- void instanceOpacityCallback(QPointer<Ui::FlatLabel> label);
- void removeOldInstance(QPointer<Ui::FlatLabel> label);
- void paintInstance(QPainter &p, const Instance &data);
- void updateControlsGeometry(int newWidth);
- void updateInstanceGeometry(const Instance &instance, int newWidth);
- Content::Row _data;
- Instance _current;
- std::vector<Instance> _old;
- };
- ProgressWidget::Row::Row(QWidget *parent, Content::Row &&data)
- : RpWidget(parent)
- , _data(std::move(data)) {
- fillCurrentInstance();
- }
- void ProgressWidget::Row::updateData(Content::Row &&data) {
- const auto wasId = _data.id;
- const auto nowId = data.id;
- _data = std::move(data);
- if (nowId.isEmpty()) {
- hideCurrentInstance();
- } else if (wasId.isEmpty()) {
- fillCurrentInstance();
- } else {
- _current.label->entity()->setText(_data.label);
- _current.info->entity()->setText(_data.info);
- setInstanceProgress(_current, _data.progress);
- if (nowId != wasId) {
- _current.progress.stop();
- }
- }
- updateControlsGeometry(width());
- update();
- }
- void ProgressWidget::Row::fillCurrentInstance() {
- _current.label = base::make_unique_q<Ui::FadeWrap<Ui::FlatLabel>>(
- this,
- object_ptr<Ui::FlatLabel>(
- this,
- _data.label,
- st::exportProgressLabel));
- _current.info = base::make_unique_q<Ui::FadeWrap<Ui::FlatLabel>>(
- this,
- object_ptr<Ui::FlatLabel>(
- this,
- _data.info,
- st::exportProgressInfoLabel));
- _current.label->hide(anim::type::instant);
- _current.info->hide(anim::type::instant);
- setInstanceProgress(_current, _data.progress);
- toggleInstance(_current, true);
- if (_data.id == "main") {
- _current.opacity.stop();
- _current.label->finishAnimating();
- _current.info->finishAnimating();
- }
- }
- void ProgressWidget::Row::hideCurrentInstance() {
- if (!_current.label) {
- return;
- }
- setInstanceProgress(_current, 1.);
- toggleInstance(_current, false);
- _old.push_back(std::move(_current));
- }
- void ProgressWidget::Row::setInstanceProgress(
- Instance &instance,
- float64 progress) {
- if (_current.value < progress) {
- _current.progress.start(
- [=] { update(); },
- _current.value,
- progress,
- st::exportProgressDuration,
- anim::sineInOut);
- } else if (_current.value > progress) {
- _current.progress.stop();
- }
- _current.value = progress;
- }
- void ProgressWidget::Row::toggleInstance(Instance &instance, bool shown) {
- Expects(instance.label != nullptr);
- if (instance.hiding != shown) {
- return;
- }
- const auto label = Ui::MakeWeak(instance.label->entity());
- instance.opacity.start(
- [=] { instanceOpacityCallback(label); },
- shown ? 0. : 1.,
- shown ? 1. : 0.,
- st::exportProgressDuration);
- instance.hiding = !shown;
- _current.label->toggle(shown, anim::type::normal);
- _current.info->toggle(shown, anim::type::normal);
- }
- void ProgressWidget::Row::instanceOpacityCallback(
- QPointer<Ui::FlatLabel> label) {
- update();
- const auto i = ranges::find(_old, label, [](const Instance &instance) {
- return Ui::MakeWeak(instance.label->entity());
- });
- if (i != end(_old) && i->hiding && !i->opacity.animating()) {
- crl::on_main(this, [=] {
- removeOldInstance(label);
- });
- }
- }
- void ProgressWidget::Row::removeOldInstance(QPointer<Ui::FlatLabel> label) {
- const auto i = ranges::find(_old, label, [](const Instance &instance) {
- return Ui::MakeWeak(instance.label->entity());
- });
- if (i != end(_old)) {
- _old.erase(i);
- }
- }
- int ProgressWidget::Row::resizeGetHeight(int newWidth) {
- updateControlsGeometry(newWidth);
- return st::exportProgressRowHeight;
- }
- void ProgressWidget::Row::paintEvent(QPaintEvent *e) {
- auto p = QPainter(this);
- const auto thickness = st::exportProgressWidth;
- const auto top = height() - thickness;
- p.fillRect(0, top, width(), thickness, st::shadowFg);
- for (const auto &instance : _old) {
- paintInstance(p, instance);
- }
- paintInstance(p, _current);
- }
- void ProgressWidget::Row::paintInstance(QPainter &p, const Instance &data) {
- const auto opacity = data.opacity.value(data.hiding ? 0. : 1.);
- if (!opacity) {
- return;
- }
- p.setOpacity(opacity);
- const auto thickness = st::exportProgressWidth;
- const auto top = height() - thickness;
- const auto till = qRound(data.progress.value(data.value) * width());
- if (till > 0) {
- p.fillRect(0, top, till, thickness, st::exportProgressFg);
- }
- if (till < width()) {
- const auto left = width() - till;
- p.fillRect(till, top, left, thickness, st::exportProgressBg);
- }
- }
- void ProgressWidget::Row::updateControlsGeometry(int newWidth) {
- updateInstanceGeometry(_current, newWidth);
- for (const auto &instance : _old) {
- updateInstanceGeometry(instance, newWidth);
- }
- }
- void ProgressWidget::Row::updateInstanceGeometry(
- const Instance &instance,
- int newWidth) {
- if (!instance.label) {
- return;
- }
- instance.info->resizeToNaturalWidth(newWidth);
- instance.label->resizeToWidth(newWidth - instance.info->width());
- instance.info->moveToRight(0, 0, newWidth);
- instance.label->moveToLeft(0, 0, newWidth);
- }
- ProgressWidget::ProgressWidget(
- QWidget *parent,
- rpl::producer<Content> content)
- : RpWidget(parent)
- , _body(this)
- , _fileShowSkipTimer([=] { _skipFile->show(anim::type::normal); }) {
- widthValue(
- ) | rpl::start_with_next([=](int width) {
- _body->resizeToWidth(width);
- _body->moveToLeft(0, 0);
- }, _body->lifetime());
- auto skipFileWrap = _body->add(object_ptr<Ui::FixedHeightWidget>(
- _body.data(),
- st::defaultLinkButton.font->height + st::exportProgressRowSkip));
- _skipFile = base::make_unique_q<Ui::FadeWrap<Ui::LinkButton>>(
- skipFileWrap,
- object_ptr<Ui::LinkButton>(
- this,
- tr::lng_export_skip_file(tr::now),
- st::defaultLinkButton));
- _skipFile->hide(anim::type::instant);
- _skipFile->moveToLeft(st::exportProgressRowPadding.left(), 0);
- _about = _body->add(
- object_ptr<Ui::FlatLabel>(
- this,
- tr::lng_export_progress(tr::now),
- st::exportAboutLabel),
- st::exportAboutPadding);
- std::move(
- content
- ) | rpl::start_with_next([=](Content &&content) {
- updateState(std::move(content));
- }, lifetime());
- _cancel = base::make_unique_q<Ui::RoundButton>(
- this,
- tr::lng_export_stop(),
- st::exportCancelButton);
- setupBottomButton(_cancel.get());
- }
- rpl::producer<uint64> ProgressWidget::skipFileClicks() const {
- return _skipFile->entity()->clicks(
- ) | rpl::map([=] { return _fileRandomId; });
- }
- rpl::producer<> ProgressWidget::cancelClicks() const {
- return _cancel
- ? (_cancel->clicks() | rpl::to_empty)
- : (rpl::never<>() | rpl::type_erased());
- }
- rpl::producer<> ProgressWidget::doneClicks() const {
- return _doneClicks.events();
- }
- void ProgressWidget::setupBottomButton(not_null<Ui::RoundButton*> button) {
- button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
- button->show();
- sizeValue(
- ) | rpl::start_with_next([=](QSize size) {
- button->move(
- (size.width() - button->width()) / 2,
- (size.height() - st::exportCancelBottom - button->height()));
- }, button->lifetime());
- }
- void ProgressWidget::updateState(Content &&content) {
- if (!content.rows.empty() && content.rows[0].id == Content::kDoneId) {
- showDone();
- }
- const auto wasCount = _rows.size();
- auto index = 0;
- for (auto &row : content.rows) {
- if (index < _rows.size()) {
- _rows[index]->updateData(std::move(row));
- } else {
- if (index > 0) {
- _body->insert(
- index * 2 - 1,
- object_ptr<Ui::FixedHeightWidget>(
- this,
- st::exportProgressRowSkip));
- }
- _rows.push_back(_body->insert(
- index * 2,
- object_ptr<Row>(this, std::move(row)),
- st::exportProgressRowPadding));
- _rows.back()->show();
- }
- ++index;
- }
- const auto fileRandomId = !content.rows.empty()
- ? content.rows.back().randomId
- : uint64(0);
- if (_fileRandomId != fileRandomId) {
- _fileShowSkipTimer.cancel();
- _skipFile->hide(anim::type::normal);
- _fileRandomId = fileRandomId;
- if (_fileRandomId) {
- _fileShowSkipTimer.callOnce(kShowSkipFileTimeout);
- }
- }
- for (const auto count = _rows.size(); index != count; ++index) {
- _rows[index]->updateData(Content::Row());
- }
- if (_rows.size() != wasCount) {
- _body->resizeToWidth(width());
- }
- }
- void ProgressWidget::showDone() {
- _cancel = nullptr;
- _skipFile->hide(anim::type::instant);
- _fileShowSkipTimer.cancel();
- _about->setText(tr::lng_export_about_done(tr::now));
- _done = base::make_unique_q<Ui::RoundButton>(
- this,
- tr::lng_export_done(),
- st::exportDoneButton);
- const auto desired = std::min(
- st::exportDoneButton.style.font->width(tr::lng_export_done(tr::now))
- + st::exportDoneButton.height
- - st::exportDoneButton.style.font->height,
- st::exportPanelSize.width() - 2 * st::exportCancelBottom);
- if (_done->width() < desired) {
- _done->setFullWidth(desired);
- }
- _done->clicks(
- ) | rpl::to_empty
- | rpl::start_to_stream(_doneClicks, _done->lifetime());
- setupBottomButton(_done.get());
- }
- ProgressWidget::~ProgressWidget() = default;
- } // namespace View
- } // namespace Export
|