diff --git a/Telegram/SourceFiles/boxes/abstract_box.cpp b/Telegram/SourceFiles/boxes/abstract_box.cpp index 98b5b69d39..a87b93f957 100644 --- a/Telegram/SourceFiles/boxes/abstract_box.cpp +++ b/Telegram/SourceFiles/boxes/abstract_box.cpp @@ -11,15 +11,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_profile.h" #include "storage/localstorage.h" #include "lang/lang_keys.h" +#include "ui/effects/radial_animation.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/labels.h" #include "ui/widgets/shadow.h" #include "ui/wrap/fade_wrap.h" #include "ui/text/text_utilities.h" +#include "base/timer.h" #include "mainwidget.h" #include "mainwindow.h" +struct AbstractBox::LoadingProgress { + LoadingProgress( + Fn &&callback, + const style::InfiniteRadialAnimation &st); + + Ui::InfiniteRadialAnimation animation; + base::Timer removeTimer; +}; + +AbstractBox::LoadingProgress::LoadingProgress( + Fn &&callback, + const style::InfiniteRadialAnimation &st) +: animation(std::move(callback), st) { +} + void BoxContent::setTitle(rpl::producer title) { getDelegate()->setTitle(std::move(title) | Ui::Text::ToWithEntities()); } @@ -279,15 +296,35 @@ int AbstractBox::titleHeight() const { } int AbstractBox::buttonsHeight() const { - auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding; + const auto padding = _layerType + ? st::boxLayerButtonPadding + : st::boxButtonPadding; return padding.top() + st::defaultBoxButton.height + padding.bottom(); } int AbstractBox::buttonsTop() const { - auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding; + const auto padding = _layerType + ? st::boxLayerButtonPadding + : st::boxButtonPadding; return height() - padding.bottom() - st::defaultBoxButton.height; } +QRect AbstractBox::loadingRect() const { + const auto padding = _layerType + ? st::boxLayerButtonPadding + : st::boxButtonPadding; + const auto size = st::boxLoadingSize; + const auto skipx = _layerType + ? st::boxLayerTitlePosition.x() + : st::boxTitlePosition.x(); + const auto skipy = (st::defaultBoxButton.height - size) / 2; + return QRect( + skipx, + height() - padding.bottom() - skipy - size, + size, + size); +} + void AbstractBox::paintEvent(QPaintEvent *e) { Painter p(this); auto clip = e->rect(); @@ -309,6 +346,14 @@ void AbstractBox::paintEvent(QPaintEvent *e) { && clip.intersects(QRect(0, 0, width(), titleHeight()))) { paintAdditionalTitle(p); } + if (_loadingProgress) { + const auto rect = loadingRect(); + _loadingProgress->animation.draw( + p, + rect.topLeft(), + rect.size(), + width()); + } } void AbstractBox::paintAdditionalTitle(Painter &p) { @@ -441,6 +486,36 @@ QPointer AbstractBox::addTopButton(const style::IconButton &st, return result; } +void AbstractBox::showLoading(bool show) { + const auto &st = st::boxLoadingAnimation; + if (!show) { + if (_loadingProgress && !_loadingProgress->removeTimer.isActive()) { + _loadingProgress->removeTimer.callOnce( + st.sineDuration + st.sinePeriod); + _loadingProgress->animation.stop(); + } + return; + } + if (!_loadingProgress) { + const auto callback = [=] { + if (!anim::Disabled()) { + const auto t = st::boxLoadingAnimation.thickness; + update(loadingRect().marginsAdded({ t, t, t, t })); + } + }; + _loadingProgress = std::make_unique( + callback, + st::boxLoadingAnimation); + _loadingProgress->removeTimer.setCallback([=] { + _loadingProgress = nullptr; + }); + } else { + _loadingProgress->removeTimer.cancel(); + } + _loadingProgress->animation.start(); +} + + void AbstractBox::setDimensions(int newWidth, int maxHeight, bool forceCenterPosition) { _maxContentHeight = maxHeight; diff --git a/Telegram/SourceFiles/boxes/abstract_box.h b/Telegram/SourceFiles/boxes/abstract_box.h index ab3765b10d..f1780fbad2 100644 --- a/Telegram/SourceFiles/boxes/abstract_box.h +++ b/Telegram/SourceFiles/boxes/abstract_box.h @@ -46,6 +46,7 @@ public: virtual QPointer addTopButton( const style::IconButton &st, Fn clickCallback) = 0; + virtual void showLoading(bool show) = 0; virtual void updateButtonsPositions() = 0; virtual void showBox( @@ -133,6 +134,9 @@ public: std::move(clickCallback), st); } + void showLoading(bool show) { + getDelegate()->showLoading(show); + } void updateButtonsGeometry() { getDelegate()->updateButtonsPositions(); } @@ -294,6 +298,7 @@ public: QPointer addTopButton( const style::IconButton &st, Fn clickCallback) override; + void showLoading(bool show) override; void updateButtonsPositions() override; QPointer outerContainer() override; @@ -332,17 +337,20 @@ protected: } private: + struct LoadingProgress; + void paintAdditionalTitle(Painter &p); void updateTitlePosition(); void refreshLang(); - bool hasTitle() const; - int titleHeight() const; - int buttonsHeight() const; - int buttonsTop() const; - int contentTop() const; - int countFullHeight() const; - int countRealHeight() const; + [[nodiscard]] bool hasTitle() const; + [[nodiscard]] int titleHeight() const; + [[nodiscard]] int buttonsHeight() const; + [[nodiscard]] int buttonsTop() const; + [[nodiscard]] int contentTop() const; + [[nodiscard]] int countFullHeight() const; + [[nodiscard]] int countRealHeight() const; + [[nodiscard]] QRect loadingRect() const; void updateSize(); not_null _layer; @@ -363,6 +371,7 @@ private: std::vector> _buttons; object_ptr _leftButton = { nullptr }; base::unique_qptr _topButton = { nullptr }; + std::unique_ptr _loadingProgress; }; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 32c3ffb52f..9381b1fafa 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -161,6 +161,12 @@ boxPhotoCompressedSkip: 20px; boxPhotoCaptionSkip: 8px; boxPhotoTextFg: windowSubTextFg; +boxLoadingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) { + color: windowSubTextFg; + thickness: 2px; +} +boxLoadingSize: 20px; + cropPointSize: 10px; cropSkip: 13px; cropMinSize: 20px; diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index bc937ee5ff..022f26303a 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -481,8 +481,8 @@ Fn SavePreparedTheme( || (fields.createdBy != session->userId()); const auto finish = [=](const MTPTheme &result) { - done(); Background()->clearEditingTheme(ClearEditing::KeepChanges); + done(); const auto cloud = result.match([&](const MTPDtheme &data) { const auto result = Data::CloudTheme::Parse(session, data); @@ -807,11 +807,12 @@ void SaveThemeBox( const auto saving = box->lifetime().make_state(); const auto cancel = std::make_shared>(nullptr); box->lifetime().add([=] { if (*cancel) (*cancel)(); }); - box->addButton(tr::lng_settings_save(), [=] { + const auto save = [=] { if (*saving) { return; } *saving = true; + box->showLoading(true); const auto done = crl::guard(box, [=] { box->closeBox(); window->showRightColumn(nullptr); @@ -820,6 +821,7 @@ void SaveThemeBox( SaveErrorType type, const QString &error) { *saving = false; + box->showLoading(false); if (error == qstr("THEME_TITLE_INVALID")) { type = SaveErrorType::Name; } else if (error == qstr("THEME_SLUG_INVALID")) { @@ -847,7 +849,8 @@ void SaveThemeBox( fields, done, fail); - }); + }; + box->addButton(tr::lng_settings_save(), save); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); }