From 26f1ade5ba22b2b351b12e3ce8cc179f2e013758 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 10 Feb 2019 19:29:55 +0300 Subject: [PATCH] Optimize connecting widget. --- Telegram/SourceFiles/core/sandbox.cpp | 39 +- Telegram/SourceFiles/core/sandbox.h | 2 +- .../SourceFiles/dialogs/dialogs_widget.cpp | 2 +- Telegram/SourceFiles/dialogs/dialogs_widget.h | 4 +- .../SourceFiles/info/info_section_widget.cpp | 6 +- .../SourceFiles/info/info_section_widget.h | 4 +- Telegram/SourceFiles/intro/introwidget.cpp | 2 +- Telegram/SourceFiles/intro/introwidget.h | 4 +- Telegram/SourceFiles/mainwidget.cpp | 2 +- Telegram/SourceFiles/mainwidget.h | 4 +- .../ui/effects/radial_animation.cpp | 4 +- .../SourceFiles/ui/effects/radial_animation.h | 2 +- .../window/window_connecting_widget.cpp | 453 ++++++++++-------- .../window/window_connecting_widget.h | 55 +-- 14 files changed, 315 insertions(+), 268 deletions(-) diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index 4f9ef1326..f06c6d58c 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -137,10 +137,27 @@ int Sandbox::start() { } void Sandbox::launchApplication() { - if (_application) { - return; - } + InvokeQueued(this, [=] { + if (App::quitting()) { + quit(); + } else if (_application) { + return; + } + setupScreenScale(); + _application = std::make_unique(_launcher); + + // Ideally this should go to constructor. + // But we want to catch all native events and Application installs + // its own filter that can filter out some of them. So we install + // our filter after the Application constructor installs his. + installNativeEventFilter(this); + + _application->run(); + }); +} + +void Sandbox::setupScreenScale() { const auto dpi = Sandbox::primaryScreen()->logicalDotsPerInch(); LOG(("Primary screen DPI: %1").arg(dpi)); if (dpi <= 108) { @@ -170,8 +187,6 @@ void Sandbox::launchApplication() { cSetIntRetinaFactor(int32(ratio)); cSetScreenScale(kInterfaceScaleDefault); } - - runApplication(); } Sandbox::~Sandbox() = default; @@ -409,20 +424,6 @@ void Sandbox::checkForQuit() { } } -void Sandbox::runApplication() { - Expects(!App::quitting()); - - _application = std::make_unique(_launcher); - - // Ideally this should go to constructor. - // But we want to catch all native events and Application installs - // its own filter that can filter out some of them. So we install - // our filter after the Application constructor installs his. - installNativeEventFilter(this); - - _application->run(); -} - void Sandbox::refreshGlobalProxy() { #ifndef TDESKTOP_DISABLE_NETWORK_PROXY const auto proxy = !Global::started() diff --git a/Telegram/SourceFiles/core/sandbox.h b/Telegram/SourceFiles/core/sandbox.h index 89bc7d9e5..283c03dca 100644 --- a/Telegram/SourceFiles/core/sandbox.h +++ b/Telegram/SourceFiles/core/sandbox.h @@ -81,7 +81,7 @@ private: void processPostponedCalls(int level); void singleInstanceChecked(); void launchApplication(); - void runApplication(); + void setupScreenScale(); void execExternal(const QString &cmd); // Single instance application diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index a46aca9fb..34940dd4b 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -246,7 +246,7 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null cont } void DialogsWidget::setupConnectingWidget() { - _connecting = Window::ConnectingWidget::CreateDefaultWidget( + _connecting = std::make_unique( this, Window::AdaptiveIsOneColumn()); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index a229787de..75857f72a 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -34,7 +34,7 @@ class FadeWrapScaled; namespace Window { class Controller; -class ConnectingWidget; +class ConnectionState; } // namespace Window enum DialogsSearchRequestType { @@ -195,7 +195,7 @@ private: class BottomButton; object_ptr _updateTelegram = { nullptr }; object_ptr _loadMoreChats = { nullptr }; - base::unique_qptr _connecting; + std::unique_ptr _connecting; Animation _a_show; Window::SlideDirection _showDirection; diff --git a/Telegram/SourceFiles/info/info_section_widget.cpp b/Telegram/SourceFiles/info/info_section_widget.cpp index 5edee8198..4a9cb66c0 100644 --- a/Telegram/SourceFiles/info/info_section_widget.cpp +++ b/Telegram/SourceFiles/info/info_section_widget.cpp @@ -37,6 +37,8 @@ SectionWidget::SectionWidget( } void SectionWidget::init() { + Expects(_connecting == nullptr); + sizeValue( ) | rpl::start_with_next([wrap = _content.data()](QSize size) { auto wrapGeometry = QRect{ { 0, 0 }, size }; @@ -44,14 +46,14 @@ void SectionWidget::init() { wrap->updateGeometry(wrapGeometry, additionalScroll); }, _content->lifetime()); - _connecting = Window::ConnectingWidget::CreateDefaultWidget( + _connecting = std::make_unique( _content.data(), Window::AdaptiveIsOneColumn()); _content->contentChanged( ) | rpl::start_with_next([=] { _connecting->raise(); - }, _connecting->lifetime()); + }, _content->lifetime()); } Dialogs::RowDescriptor SectionWidget::activeChat() const { diff --git a/Telegram/SourceFiles/info/info_section_widget.h b/Telegram/SourceFiles/info/info_section_widget.h index 605cadca6..aca76131b 100644 --- a/Telegram/SourceFiles/info/info_section_widget.h +++ b/Telegram/SourceFiles/info/info_section_widget.h @@ -15,7 +15,7 @@ class SettingsSlider; } // namespace Ui namespace Window { -class ConnectingWidget; +class ConnectionState; } // namespace Window namespace Info { @@ -69,7 +69,7 @@ private: object_ptr _content; object_ptr _topBarSurrogate = { nullptr }; - base::unique_qptr _connecting; + std::unique_ptr _connecting; }; diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index beac34764..f1513a868 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -114,7 +114,7 @@ Widget::Widget(QWidget *parent) : RpWidget(parent) } void Widget::setupConnectingWidget() { - _connecting = Window::ConnectingWidget::CreateDefaultWidget( + _connecting = std::make_unique( this, rpl::single(true)); } diff --git a/Telegram/SourceFiles/intro/introwidget.h b/Telegram/SourceFiles/intro/introwidget.h index 2857667c8..36d75a30a 100644 --- a/Telegram/SourceFiles/intro/introwidget.h +++ b/Telegram/SourceFiles/intro/introwidget.h @@ -24,7 +24,7 @@ class FadeWrap; } // namespace Ui namespace Window { -class ConnectingWidget; +class ConnectionState; } // namespace Window namespace Intro { @@ -281,7 +281,7 @@ private: object_ptr> _resetAccount = { nullptr }; object_ptr> _terms = { nullptr }; - base::unique_qptr _connecting; + std::unique_ptr _connecting; mtpRequestId _resetRequest = 0; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index be4ebf6c3..63f6575b6 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -501,7 +501,7 @@ AuthSession &MainWidget::session() const { void MainWidget::setupConnectingWidget() { using namespace rpl::mappers; - _connecting = Window::ConnectingWidget::CreateDefaultWidget( + _connecting = std::make_unique( this, Window::AdaptiveIsOneColumn() | rpl::map(!_1)); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index ef1d6b17b..469ce7eae 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -68,7 +68,7 @@ class TopBarWrapWidget; class SectionMemento; class SectionWidget; class AbstractSectionWidget; -class ConnectingWidget; +class ConnectionState; struct SectionSlideParams; struct SectionShow; enum class Column; @@ -486,7 +486,7 @@ private: object_ptr _mainSection = { nullptr }; object_ptr _thirdSection = { nullptr }; std::unique_ptr _thirdSectionFromStack; - base::unique_qptr _connecting; + std::unique_ptr _connecting; base::weak_ptr _currentCall; object_ptr> _callTopBar = { nullptr }; diff --git a/Telegram/SourceFiles/ui/effects/radial_animation.cpp b/Telegram/SourceFiles/ui/effects/radial_animation.cpp index 4133e7053..34e93912c 100644 --- a/Telegram/SourceFiles/ui/effects/radial_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/radial_animation.cpp @@ -116,10 +116,10 @@ InfiniteRadialAnimation::InfiniteRadialAnimation( , _animation(std::move(callbacks)) { } -void InfiniteRadialAnimation::start() { +void InfiniteRadialAnimation::start(TimeMs skip) { const auto now = getms(); if (_workFinished <= now && (_workFinished || !_workStarted)) { - _workStarted = now + _st.sineDuration; + _workStarted = std::max(now + _st.sineDuration - skip, TimeMs(1)); _workFinished = 0; } if (!_animation.animating()) { diff --git a/Telegram/SourceFiles/ui/effects/radial_animation.h b/Telegram/SourceFiles/ui/effects/radial_animation.h index a68c77a7c..24ef4a669 100644 --- a/Telegram/SourceFiles/ui/effects/radial_animation.h +++ b/Telegram/SourceFiles/ui/effects/radial_animation.h @@ -69,7 +69,7 @@ public: return _animation.animating(); } - void start(); + void start(TimeMs skip = 0); void stop(); void step(TimeMs ms); diff --git a/Telegram/SourceFiles/window/window_connecting_widget.cpp b/Telegram/SourceFiles/window/window_connecting_widget.cpp index 0e91b92fa..d55b32aca 100644 --- a/Telegram/SourceFiles/window/window_connecting_widget.cpp +++ b/Telegram/SourceFiles/window/window_connecting_widget.cpp @@ -42,7 +42,7 @@ Progress::Progress(QWidget *parent) setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_TransparentForMouseEvents); resize(st::connectingRadial.size); - _animation.start(); + _animation.start(st::connectingRadial.sineDuration); } void Progress::paintEvent(QPaintEvent *e) { @@ -66,7 +66,41 @@ void Progress::step(TimeMs ms, bool timer) { } // namespace -class ConnectingWidget::ProxyIcon +class ConnectionState::Widget : public Ui::AbstractButton { +public: + Widget(QWidget *parent, const Layout &layout); + + void refreshRetryLink(bool hasRetry); + void setLayout(const Layout &layout); + void setProgressVisibility(bool visible); + + rpl::producer<> refreshStateRequests() const; + +protected: + void resizeEvent(QResizeEvent *e) override; + void paintEvent(QPaintEvent *e) override; + + void onStateChanged(State was, StateChangeSource source) override; + +private: + class ProxyIcon; + using State = ConnectionState::State; + using Layout = ConnectionState::Layout; + + void updateRetryGeometry(); + QRect innerRect() const; + QRect contentRect() const; + QRect textRect() const; + + Layout _currentLayout; + base::unique_qptr _retry; + QPointer _progress; + QPointer _proxyIcon; + rpl::event_stream<> _refreshStateRequests; + +}; + +class ConnectionState::Widget::ProxyIcon : public Ui::RpWidget , private base::Subscriber { public: @@ -88,7 +122,7 @@ private: }; -ConnectingWidget::ProxyIcon::ProxyIcon(QWidget *parent) : RpWidget(parent) { +ConnectionState::Widget::ProxyIcon::ProxyIcon(QWidget *parent) : RpWidget(parent) { resize( std::max( st::connectingRadial.size.width(), @@ -107,7 +141,7 @@ ConnectingWidget::ProxyIcon::ProxyIcon(QWidget *parent) : RpWidget(parent) { refreshCacheImages(); } -void ConnectingWidget::ProxyIcon::refreshCacheImages() { +void ConnectionState::Widget::ProxyIcon::refreshCacheImages() { const auto prepareCache = [&](const style::icon &icon) { auto image = QImage( size() * cIntRetinaFactor(), @@ -128,14 +162,14 @@ void ConnectingWidget::ProxyIcon::refreshCacheImages() { _cacheOff = prepareCache(st::connectingProxyOff); } -void ConnectingWidget::ProxyIcon::setToggled(bool toggled) { +void ConnectionState::Widget::ProxyIcon::setToggled(bool toggled) { if (_toggled != toggled) { _toggled = toggled; update(); } } -void ConnectingWidget::ProxyIcon::setOpacity(float64 opacity) { +void ConnectionState::Widget::ProxyIcon::setOpacity(float64 opacity) { _opacity = opacity; if (_opacity == 0.) { hide(); @@ -145,29 +179,35 @@ void ConnectingWidget::ProxyIcon::setOpacity(float64 opacity) { update(); } -void ConnectingWidget::ProxyIcon::paintEvent(QPaintEvent *e) { +void ConnectionState::Widget::ProxyIcon::paintEvent(QPaintEvent *e) { Painter p(this); p.setOpacity(_opacity); p.drawPixmap(0, 0, _toggled ? _cacheOn : _cacheOff); } -bool ConnectingWidget::State::operator==(const State &other) const { +bool ConnectionState::Widget::State::operator==(const State &other) const { return (type == other.type) && (useProxy == other.useProxy) && (underCursor == other.underCursor) && (waitTillRetry == other.waitTillRetry); } -ConnectingWidget::ConnectingWidget(QWidget *parent) -: AbstractButton(parent) +ConnectionState::ConnectionState( + not_null parent, + rpl::producer shown) +: _parent(parent) , _refreshTimer([=] { refreshState(); }) , _currentLayout(computeLayout(_state)) { - _proxyIcon = Ui::CreateChild(this); - _progress = Ui::CreateChild(this); - - addClickHandler([=] { - Ui::show(ProxiesBoxController::CreateOwningBox()); - }); + rpl::combine( + std::move(shown), + visibility() + ) | rpl::start_with_next([=](bool shown, float64 visible) { + if (!shown || visible == 0.) { + _widget = nullptr; + } else if (!_widget) { + createWidget(); + } + }, _lifetime); subscribe(Global::RefConnectionTypeChanged(), [=] { refreshState(); @@ -175,11 +215,35 @@ ConnectingWidget::ConnectingWidget(QWidget *parent) refreshState(); } -rpl::producer ConnectingWidget::visibility() const { - return _visibilityValues.events_starting_with(currentVisibility()); +void ConnectionState::createWidget() { + _widget = base::make_unique_q(_parent, _currentLayout); + _widget->setVisible(!_forceHidden); + + updateWidth(); + + rpl::combine( + visibility(), + _parent->heightValue() + ) | rpl::start_with_next([=](float64 visible, int height) { + _widget->moveToLeft(0, anim::interpolate( + height - st::connectingMargin.top(), + height - _widget->height(), + visible)); + }, _widget->lifetime()); + + _widget->refreshStateRequests( + ) | rpl::start_with_next([=] { + refreshState(); + }, _widget->lifetime()); } -void ConnectingWidget::finishAnimating() { +void ConnectionState::raise() { + if (_widget) { + _widget->raise(); + } +} + +void ConnectionState::finishAnimating() { if (_contentWidth.animating()) { _contentWidth.finish(); updateWidth(); @@ -190,160 +254,16 @@ void ConnectingWidget::finishAnimating() { } } -void ConnectingWidget::setForceHidden(bool hidden) { - if (_forceHidden == hidden) { - return; - } - if (hidden) { - const auto real = isHidden(); - if (!real) { - hide(); - } - _realHidden = real; - } +void ConnectionState::setForceHidden(bool hidden) { _forceHidden = hidden; - if (!hidden && isHidden() != _realHidden) { - setVisible(!_realHidden); + if (_widget) { + _widget->setVisible(!hidden); } } -void ConnectingWidget::setVisibleHook(bool visible) { - if (_forceHidden) { - _realHidden = !visible; - return; - } - QWidget::setVisible(visible); -} - -base::unique_qptr ConnectingWidget::CreateDefaultWidget( - Ui::RpWidget *parent, - rpl::producer shown) { - auto result = base::make_unique_q(parent); - const auto weak = result.get(); - rpl::combine( - result->visibility(), - parent->heightValue() - ) | rpl::start_with_next([=](float64 visible, int height) { - const auto hidden = (visible == 0.); - if (weak->isHidden() != hidden) { - weak->setVisible(!hidden); - } - const auto size = weak->size(); - weak->moveToLeft(0, anim::interpolate( - height - st::connectingMargin.top(), - height - weak->height(), - visible)); - }, weak->lifetime()); - std::move( - shown - ) | rpl::start_with_next([=](bool shown) { - weak->setForceHidden(!shown); - }, weak->lifetime()); - result->finishAnimating(); - return result; -} - -void ConnectingWidget::onStateChanged( - AbstractButton::State was, - StateChangeSource source) { - crl::on_main(this, [=] { refreshState(); }); -} - -void ConnectingWidget::paintEvent(QPaintEvent *e) { - Painter p(this); - PainterHighQualityEnabler hq(p); - - p.setPen(Qt::NoPen); - p.setBrush(st::windowBg); - const auto inner = innerRect(); - const auto content = contentRect(); - const auto text = textRect(); - const auto left = inner.topLeft(); - const auto right = content.topLeft() + QPoint(content.width(), 0); - st::connectingLeftShadow.paint(p, left, width()); - st::connectingLeft.paint(p, left, width()); - st::connectingRightShadow.paint(p, right, width()); - st::connectingRight.paint(p, right, width()); - st::connectingBodyShadow.fill(p, content); - st::connectingBody.fill(p, content); - - const auto available = text.width(); - if (available > 0 && !_currentLayout.text.isEmpty()) { - p.setFont(st::normalFont); - p.setPen(st::windowSubTextFg); - if (available >= _currentLayout.textWidth) { - p.drawTextLeft( - text.x(), - text.y(), - width(), - _currentLayout.text, - _currentLayout.textWidth); - } else { - p.drawTextLeft( - text.x(), - text.y(), - width(), - st::normalFont->elided(_currentLayout.text, available)); - } - } -} - -QRect ConnectingWidget::innerRect() const { - return rect().marginsRemoved( - st::connectingMargin - ); -} - -QRect ConnectingWidget::contentRect() const { - return innerRect().marginsRemoved(style::margins( - st::connectingLeft.width(), - 0, - st::connectingRight.width(), - 0)); -} - -QRect ConnectingWidget::textRect() const { - return contentRect().marginsRemoved( - st::connectingTextPadding - ); -} - -void ConnectingWidget::resizeEvent(QResizeEvent *e) { - { - const auto xShift = (height() - _progress->width()) / 2; - const auto yShift = (height() - _progress->height()) / 2; - _progress->moveToLeft(xShift, yShift); - } - { - const auto xShift = (height() - _proxyIcon->width()) / 2; - const auto yShift = (height() - _proxyIcon->height()) / 2; - _proxyIcon->moveToRight(xShift, yShift); - } - updateRetryGeometry(); -} - -void ConnectingWidget::updateRetryGeometry() { - if (!_retry) { - return; - } - const auto text = textRect(); - const auto available = text.width() - _currentLayout.textWidth; - if (available <= 0) { - _retry->hide(); - } else { - _retry->show(); - _retry->resize( - std::min(available, _retry->naturalWidth()), - innerRect().height()); - _retry->moveToLeft( - text.x() + text.width() - _retry->width(), - st::connectingMargin.top()); - } -} - -void ConnectingWidget::refreshState() { +void ConnectionState::refreshState() { const auto state = [&]() -> State { - const auto under = isOver(); + const auto under = _widget && _widget->isOver(); const auto mtp = MTP::dcstate(); const auto throughProxy = (Global::ProxySettings() == ProxyData::Settings::Enabled); @@ -385,12 +305,9 @@ void ConnectingWidget::refreshState() { applyState(state); } -void ConnectingWidget::applyState(const State &state) { +void ConnectionState::applyState(const State &state) { const auto newLayout = computeLayout(state); - const auto guard = gsl::finally([&] { - updateWidth(); - update(); - }); + const auto guard = gsl::finally([&] { updateWidth(); }); _state = state; if (_currentLayout.visible != newLayout.visible) { @@ -416,10 +333,9 @@ void ConnectingWidget::applyState(const State &state) { _currentLayout.text = saved.text; _currentLayout.textWidth = saved.textWidth; } - refreshRetryLink(_currentLayout.hasRetry); } -void ConnectingWidget::changeVisibilityWithLayout(const Layout &layout) { +void ConnectionState::changeVisibilityWithLayout(const Layout &layout) { Expects(_currentLayout.visible != layout.visible); const auto changeLayout = !_currentLayout.visible; @@ -442,29 +358,22 @@ void ConnectingWidget::changeVisibilityWithLayout(const Layout &layout) { } } -void ConnectingWidget::setLayout(const Layout &layout) { +void ConnectionState::setLayout(const Layout &layout) { _currentLayout = layout; - _proxyIcon->setToggled(_currentLayout.proxyEnabled); - _progress->setVisible(_contentWidth.animating() - || _currentLayout.progressShown); + if (_widget) { + _widget->setLayout(layout); + } + refreshProgressVisibility(); } -void ConnectingWidget::refreshRetryLink(bool hasRetry) { - if (hasRetry && !_retry) { - _retry = base::make_unique_q( - this, - lang(lng_reconnecting_try_now), - st::connectingRetryLink); - _retry->addClickHandler([=] { - MTP::restart(); - }); - updateRetryGeometry(); - } else if (!hasRetry) { - _retry = nullptr; +void ConnectionState::refreshProgressVisibility() { + if (_widget) { + _widget->setProgressVisibility(_contentWidth.animating() + || _currentLayout.progressShown); } } -void ConnectingWidget::updateVisibility() { +void ConnectionState::updateVisibility() { const auto value = currentVisibility(); if (value == 0. && _contentWidth.animating()) { _contentWidth.finish(); @@ -473,11 +382,15 @@ void ConnectingWidget::updateVisibility() { _visibilityValues.fire_copy(value); } -float64 ConnectingWidget::currentVisibility() const { +float64 ConnectionState::currentVisibility() const { return _visibility.current(_currentLayout.visible ? 1. : 0.); } -auto ConnectingWidget::computeLayout(const State &state) const -> Layout { +rpl::producer ConnectionState::visibility() const { + return _visibilityValues.events_starting_with(currentVisibility()); +} + +auto ConnectionState::computeLayout(const State &state) const -> Layout { auto result = Layout(); result.proxyEnabled = state.useProxy; result.progressShown = (state.type != State::Type::Connected); @@ -513,7 +426,7 @@ auto ConnectingWidget::computeLayout(const State &state) const -> Layout { return result; } -void ConnectingWidget::updateWidth() { +void ConnectionState::updateWidth() { const auto current = _contentWidth.current(_currentLayout.contentWidth); const auto height = st::connectingLeft.height(); const auto desired = QRect(0, 0, current, height).marginsAdded( @@ -525,11 +438,153 @@ void ConnectingWidget::updateWidth() { ).marginsAdded( st::connectingMargin ); - resize(desired.size()); - if (!_contentWidth.animating()) { - _progress->setVisible(_currentLayout.progressShown); + if (_widget) { + _widget->resize(desired.size()); + _widget->update(); + } + refreshProgressVisibility(); +} + +ConnectionState::Widget::Widget(QWidget *parent, const Layout &layout) +: AbstractButton(parent) +, _currentLayout(layout) { + _proxyIcon = Ui::CreateChild(this); + _progress = Ui::CreateChild(this); + + addClickHandler([=] { + Ui::show(ProxiesBoxController::CreateOwningBox()); + }); +} + +void ConnectionState::Widget::onStateChanged( + AbstractButton::State was, + StateChangeSource source) { + Ui::PostponeCall(crl::guard(this, [=] { + _refreshStateRequests.fire({}); + })); +} + +rpl::producer<> ConnectionState::Widget::refreshStateRequests() const { + return _refreshStateRequests.events(); +} + +void ConnectionState::Widget::paintEvent(QPaintEvent *e) { + Painter p(this); + PainterHighQualityEnabler hq(p); + + p.setPen(Qt::NoPen); + p.setBrush(st::windowBg); + const auto inner = innerRect(); + const auto content = contentRect(); + const auto text = textRect(); + const auto left = inner.topLeft(); + const auto right = content.topLeft() + QPoint(content.width(), 0); + st::connectingLeftShadow.paint(p, left, width()); + st::connectingLeft.paint(p, left, width()); + st::connectingRightShadow.paint(p, right, width()); + st::connectingRight.paint(p, right, width()); + st::connectingBodyShadow.fill(p, content); + st::connectingBody.fill(p, content); + + const auto available = text.width(); + if (available > 0 && !_currentLayout.text.isEmpty()) { + p.setFont(st::normalFont); + p.setPen(st::windowSubTextFg); + if (available >= _currentLayout.textWidth) { + p.drawTextLeft( + text.x(), + text.y(), + width(), + _currentLayout.text, + _currentLayout.textWidth); + } else { + p.drawTextLeft( + text.x(), + text.y(), + width(), + st::normalFont->elided(_currentLayout.text, available)); + } + } +} + +QRect ConnectionState::Widget::innerRect() const { + return rect().marginsRemoved( + st::connectingMargin + ); +} + +QRect ConnectionState::Widget::contentRect() const { + return innerRect().marginsRemoved(style::margins( + st::connectingLeft.width(), + 0, + st::connectingRight.width(), + 0)); +} + +QRect ConnectionState::Widget::textRect() const { + return contentRect().marginsRemoved( + st::connectingTextPadding + ); +} + +void ConnectionState::Widget::resizeEvent(QResizeEvent *e) { + { + const auto xShift = (height() - _progress->width()) / 2; + const auto yShift = (height() - _progress->height()) / 2; + _progress->moveToLeft(xShift, yShift); + } + { + const auto xShift = (height() - _proxyIcon->width()) / 2; + const auto yShift = (height() - _proxyIcon->height()) / 2; + _proxyIcon->moveToRight(xShift, yShift); + } + updateRetryGeometry(); +} + +void ConnectionState::Widget::updateRetryGeometry() { + if (!_retry) { + return; + } + const auto text = textRect(); + const auto available = text.width() - _currentLayout.textWidth; + if (available <= 0) { + _retry->hide(); + } else { + _retry->show(); + _retry->resize( + std::min(available, _retry->naturalWidth()), + innerRect().height()); + _retry->moveToLeft( + text.x() + text.width() - _retry->width(), + st::connectingMargin.top()); + } +} + +void ConnectionState::Widget::setLayout(const Layout &layout) { + _currentLayout = layout; + _proxyIcon->setToggled(_currentLayout.proxyEnabled); + refreshRetryLink(_currentLayout.hasRetry); +} + +void ConnectionState::Widget::setProgressVisibility(bool visible) { + if (_progress->isHidden() == visible) { + _progress->setVisible(visible); + } +} + +void ConnectionState::Widget::refreshRetryLink(bool hasRetry) { + if (hasRetry && !_retry) { + _retry = base::make_unique_q( + this, + lang(lng_reconnecting_try_now), + st::connectingRetryLink); + _retry->addClickHandler([=] { + MTP::restart(); + }); + updateRetryGeometry(); + } else if (!hasRetry) { + _retry = nullptr; } - update(); } rpl::producer AdaptiveIsOneColumn() { diff --git a/Telegram/SourceFiles/window/window_connecting_widget.h b/Telegram/SourceFiles/window/window_connecting_widget.h index ec9318540..3ac916d3c 100644 --- a/Telegram/SourceFiles/window/window_connecting_widget.h +++ b/Telegram/SourceFiles/window/window_connecting_widget.h @@ -7,39 +7,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "ui/abstract_button.h" #include "base/timer.h" +#include "base/unique_qptr.h" namespace Ui { -class LinkButton; +class RpWidget; } // namespace Ui namespace Window { -class ConnectingWidget - : public Ui::AbstractButton - , private base::Subscriber { +class ConnectionState : private base::Subscriber { public: - ConnectingWidget(QWidget *parent); - - rpl::producer visibility() const; - - void finishAnimating(); - void setForceHidden(bool hidden); - void setVisibleHook(bool visible) override; - - static base::unique_qptr CreateDefaultWidget( - Ui::RpWidget *parent, + ConnectionState( + not_null parent, rpl::producer shown); -protected: - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; + void raise(); + void setForceHidden(bool hidden); - void onStateChanged(State was, StateChangeSource source) override; + rpl::lifetime &lifetime() { + return _lifetime; + } private: - class ProxyIcon; + class Widget; struct State { enum class Type { Connected, @@ -64,34 +55,32 @@ private: int textWidth = 0; }; - void updateRetryGeometry(); - void updateWidth(); - void updateVisibility(); + + void createWidget(); + void finishAnimating(); void refreshState(); void applyState(const State &state); void changeVisibilityWithLayout(const Layout &layout); - void refreshRetryLink(bool hasRetry); Layout computeLayout(const State &state) const; void setLayout(const Layout &layout); float64 currentVisibility() const; + rpl::producer visibility() const; + void updateWidth(); + void updateVisibility(); + void refreshProgressVisibility(); - QRect innerRect() const; - QRect contentRect() const; - QRect textRect() const; - + not_null _parent; + base::unique_qptr _widget; + bool _forceHidden = false; base::Timer _refreshTimer; State _state; Layout _currentLayout; TimeMs _connectingStartedAt = 0; Animation _contentWidth; Animation _visibility; - base::unique_qptr _retry; - QPointer _progress; - QPointer _proxyIcon; - bool _forceHidden = false; - bool _realHidden = false; rpl::event_stream _visibilityValues; + rpl::lifetime _lifetime; };