New connecting status design.

This commit is contained in:
John Preston 2018-05-07 20:44:33 +03:00
parent cc2c13d018
commit d2048f3c25
42 changed files with 758 additions and 139 deletions

View file

@ -215,8 +215,6 @@ emojiReplaceHeight: 56px;
emojiReplaceInnerHeight: 42px;
emojiReplacePadding: 14px;
connectingPadding: margins(5px, 5px, 5px, 5px);
dragFont: font(28px semibold);
dragSubfont: font(20px semibold);
dragColor: windowSubTextFg;

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,012 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -168,9 +168,6 @@ namespace {
cSetOtherOnline(0);
clearStorageImages();
if (auto w = wnd()) {
w->updateConnectingStatus();
}
return true;
}
} // namespace

View file

@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peer_list_box.h"
#include "window/window_controller.h"
#include "window/window_slide_animation.h"
#include "window/window_connecting_widget.h"
#include "profile/profile_channel_controllers.h"
#include "storage/storage_media_prepare.h"
#include "data/data_session.h"
@ -170,6 +171,13 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null<Window::Controller*> cont
updateJumpToDateVisibility(true);
updateSearchFromVisibility(true);
setupConnectingWidget();
}
void DialogsWidget::setupConnectingWidget() {
_connecting = Window::ConnectingWidget::CreateDefaultWidget(
this,
Window::AdaptiveIsOneColumn());
}
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
@ -282,6 +290,7 @@ void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window:
_jumpToDate->hide(anim::type::instant);
_chooseFromUser->hide(anim::type::instant);
_lockUnlock->hide();
_connecting->setForceHidden(true);
int delta = st::slideShift;
if (_showDirection == Window::SlideDirection::FromLeft) {
@ -307,6 +316,7 @@ void DialogsWidget::animationCallback() {
_mainMenuToggle->show();
if (_forwardCancel) _forwardCancel->show();
_filter->show();
_connecting->setForceHidden(false);
updateLockUnlockVisibility();
updateJumpToDateVisibility(true);
updateSearchFromVisibility(true);

View file

@ -34,6 +34,7 @@ class FadeWrapScaled;
namespace Window {
class Controller;
class ConnectingWidget;
} // namespace Window
enum DialogsSearchRequestType {
@ -151,6 +152,7 @@ private:
const QVector<MTPDialog> &dialogs,
const QVector<MTPMessage> &messages);
void setupConnectingWidget();
bool searchForPeersRequired(const QString &query) const;
void setSearchInChat(Dialogs::Key chat, UserData *from = nullptr);
void showJumpToDate();
@ -194,6 +196,7 @@ private:
QPointer<DialogsInner> _inner;
class UpdateButton;
object_ptr<UpdateButton> _updateTelegram = { nullptr };
base::unique_qptr<Window::ConnectingWidget> _connecting;
Animation _a_show;
Window::SlideDirection _showDirection;

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "info/info_section_widget.h"
#include "window/window_connecting_widget.h"
#include "info/info_content_widget.h"
#include "info/info_wrap_widget.h"
#include "info/info_layer_widget.h"
@ -42,6 +43,15 @@ void SectionWidget::init() {
auto additionalScroll = 0;
wrap->updateGeometry(wrapGeometry, additionalScroll);
}, _content->lifetime());
_connecting = Window::ConnectingWidget::CreateDefaultWidget(
_content.data(),
Window::AdaptiveIsOneColumn());
_content->contentChanged(
) | rpl::start_with_next([=] {
_connecting->raise();
}, _connecting->lifetime());
}
Dialogs::RowDescriptor SectionWidget::activeChat() const {

View file

@ -14,6 +14,10 @@ namespace Ui {
class SettingsSlider;
} // namespace Ui
namespace Window {
class ConnectingWidget;
} // namespace Window
namespace Info {
class Memento;
@ -65,6 +69,7 @@ private:
object_ptr<WrapWidget> _content;
object_ptr<Ui::RpWidget> _topBarSurrogate = { nullptr };
base::unique_qptr<Window::ConnectingWidget> _connecting;
};

View file

@ -582,6 +582,7 @@ void WrapWidget::finishShowContent() {
_scrollTillBottomChanges.fire(_content->scrollTillBottomChanges());
_topShadow->raise();
_topShadow->finishAnimating();
_contentChanges.fire({});
// This was done for tabs support.
//
@ -680,6 +681,10 @@ void WrapWidget::setWrap(Wrap wrap) {
_wrap = wrap;
}
rpl::producer<> WrapWidget::contentChanged() const {
return _contentChanges.events();
}
bool WrapWidget::hasTopBarShadow() const {
return _topShadow->toggled();
}

View file

@ -90,6 +90,8 @@ public:
rpl::producer<Wrap> wrapValue() const;
void setWrap(Wrap wrap);
rpl::producer<> contentChanged() const;
not_null<Controller*> controller() {
return _controller.get();
}
@ -217,6 +219,7 @@ private:
rpl::event_stream<rpl::producer<bool>> _desiredShadowVisibilities;
rpl::event_stream<rpl::producer<SelectedItems>> _selectedLists;
rpl::event_stream<rpl::producer<int>> _scrollTillBottomChanges;
rpl::event_stream<> _contentChanges;
};

View file

@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/slide_animation.h"
#include "core/update_checker.h"
#include "window/window_slide_animation.h"
#include "window/window_connecting_widget.h"
#include "styles/style_boxes.h"
#include "styles/style_intro.h"
#include "styles/style_window.h"
@ -65,6 +66,7 @@ Widget::Widget(QWidget *parent) : RpWidget(parent)
_settings->entity()->setClickedCallback([] { App::wnd()->showSettings(); });
getNearestDC();
setupConnectingWidget();
appendStep(new StartWidget(this, getData()));
fixOrder();
@ -97,6 +99,12 @@ Widget::Widget(QWidget *parent) : RpWidget(parent)
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
}
void Widget::setupConnectingWidget() {
_connecting = Window::ConnectingWidget::CreateDefaultWidget(
this,
rpl::single(true));
}
void Widget::refreshLang() {
_changeLanguage.destroy();
createLanguageLink();
@ -212,6 +220,7 @@ void Widget::fixOrder() {
if (_update) _update->raise();
_settings->raise();
_back->raise();
_connecting->raise();
}
void Widget::moveToStep(Step *step, Direction direction) {
@ -221,6 +230,7 @@ void Widget::moveToStep(Step *step, Direction direction) {
if (_update) {
_update->raise();
}
_connecting->raise();
historyMove(direction);
}
@ -310,6 +320,7 @@ void Widget::showControls() {
getStep()->show();
_next->show();
_next->setText([this] { return getStep()->nextButtonText(); });
_connecting->setForceHidden(false);
auto hasCover = getStep()->hasCover();
_settings->toggle(!hasCover, anim::type::instant);
if (_update) _update->toggle(!hasCover, anim::type::instant);
@ -320,6 +331,7 @@ void Widget::showControls() {
void Widget::hideControls() {
getStep()->hide();
_next->hide();
_connecting->setForceHidden(true);
_settings->hide(anim::type::instant);
if (_update) _update->hide(anim::type::instant);
if (_changeLanguage) _changeLanguage->hide(anim::type::instant);

View file

@ -21,6 +21,10 @@ template <typename Widget>
class FadeWrap;
} // namespace Ui
namespace Window {
class ConnectingWidget;
} // namespace Window
namespace Intro {
class Widget : public Ui::RpWidget, private MTP::Sender, private base::Subscriber {
@ -202,6 +206,7 @@ public:
};
private:
void setupConnectingWidget();
void refreshLang();
void animationCallback();
void createLanguageLink();
@ -249,6 +254,8 @@ private:
object_ptr<Ui::FadeWrap<Ui::LinkButton>> _changeLanguage = { nullptr };
object_ptr<Ui::FadeWrap<Ui::RoundButton>> _resetAccount = { nullptr };
base::unique_qptr<Window::ConnectingWidget> _connecting;
mtpRequestId _resetRequest = 0;
};

View file

@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/shadow.h"
#include "window/section_memento.h"
#include "window/section_widget.h"
#include "window/window_connecting_widget.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/focus_persister.h"
#include "ui/resize_area.h"
@ -56,6 +57,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/mute_settings_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/download_path_box.h"
#include "boxes/connection_box.h"
#include "storage/localstorage.h"
#include "shortcuts.h"
#include "media/media_audio.h"
@ -214,6 +216,7 @@ MainWidget::MainWidget(
_ptsWaiter.setRequesting(true);
updateScrollColors();
setupConnectingWidget();
connect(_dialogs, SIGNAL(cancelled()), this, SLOT(dialogsCancelled()));
connect(this, SIGNAL(dialogsUpdated()), _dialogs, SLOT(onListScroll()));
@ -329,6 +332,13 @@ MainWidget::MainWidget(
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
}
void MainWidget::setupConnectingWidget() {
using namespace rpl::mappers;
_connecting = Window::ConnectingWidget::CreateDefaultWidget(
this,
Window::AdaptiveIsOneColumn() | rpl::map(!_1));
}
void MainWidget::checkCurrentFloatPlayer() {
const auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice);
const auto fullId = state.contextId();
@ -2567,6 +2577,7 @@ void MainWidget::orderWidgets() {
if (_thirdColumnResizeArea) {
_thirdColumnResizeArea->raise();
}
_connecting->raise();
_playerPlaylist->raise();
_playerPanel->raise();
for (auto &instance : _playerFloats) {

View file

@ -54,6 +54,7 @@ class PlayerWrapWidget;
class SectionMemento;
class SectionWidget;
class AbstractSectionWidget;
class ConnectingWidget;
struct SectionSlideParams;
struct SectionShow;
enum class Column;
@ -426,6 +427,7 @@ private:
-> std::unique_ptr<Window::SectionMemento>;
void userIsContactUpdated(not_null<UserData*> user);
void setupConnectingWidget();
void createPlayer();
void switchToPanelPlayer();
void switchToFixedPlayer();
@ -561,6 +563,7 @@ private:
object_ptr<Window::SectionWidget> _mainSection = { nullptr };
object_ptr<Window::SectionWidget> _thirdSection = { nullptr };
std::unique_ptr<Window::SectionMemento> _thirdSectionFromStack;
base::unique_qptr<Window::ConnectingWidget> _connecting;
base::weak_ptr<Calls::Call> _currentCall;
object_ptr<Ui::SlideWrap<Calls::TopBar>> _callTopBar = { nullptr };

View file

@ -63,50 +63,6 @@ void FeedLangTestingKey(int key) {
} // namespace
ConnectingWidget::ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect) : TWidget(parent)
, _reconnect(this, QString()) {
set(text, reconnect);
connect(_reconnect, SIGNAL(clicked()), this, SLOT(onReconnect()));
}
void ConnectingWidget::set(const QString &text, const QString &reconnect) {
_text = text;
_textWidth = st::linkFont->width(_text) + st::linkFont->spacew;
int32 _reconnectWidth = 0;
if (reconnect.isEmpty()) {
_reconnect->hide();
} else {
_reconnect->setText(reconnect);
_reconnect->show();
_reconnect->move(st::connectingPadding.left() + _textWidth, st::boxRoundShadow.extend.top() + st::connectingPadding.top());
_reconnectWidth = _reconnect->width();
}
resize(st::connectingPadding.left() + _textWidth + _reconnectWidth + st::connectingPadding.right() + st::boxRoundShadow.extend.right(), st::boxRoundShadow.extend.top() + st::connectingPadding.top() + st::normalFont->height + st::connectingPadding.bottom());
update();
}
void ConnectingWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
auto sides = RectPart::Top | RectPart::Right;
Ui::Shadow::paint(p, QRect(0, st::boxRoundShadow.extend.top(), width() - st::boxRoundShadow.extend.right(), height() - st::boxRoundShadow.extend.top()), width(), st::boxRoundShadow, sides);
auto parts = RectPart::Top | RectPart::TopRight | RectPart::Center | RectPart::Right;
App::roundRect(p, QRect(-st::boxRadius, st::boxRoundShadow.extend.top(), width() - st::boxRoundShadow.extend.right() + st::boxRadius, height() - st::boxRoundShadow.extend.top() + st::boxRadius), st::boxBg, BoxCorners, nullptr, parts);
p.setFont(st::normalFont);
p.setPen(st::windowSubTextFg);
p.drawText(st::connectingPadding.left(), st::boxRoundShadow.extend.top() + st::connectingPadding.top() + st::normalFont->ascent, _text);
}
void ConnectingWidget::onReconnect() {
if (Global::UseProxy()) {
//Ui::show(Box<ConnectionBox>());
Ui::show(ProxiesBoxController::CreateOwningBox());
} else {
MTP::restart();
}
}
MainWindow::MainWindow() {
auto logo = Messenger::Instance().logo();
icon16 = logo.scaledToWidth(16, Qt::SmoothTransformation);
@ -244,8 +200,6 @@ void MainWindow::setupIntro() {
fixOrder();
updateConnectingStatus();
_delayedServiceMsgs.clear();
if (_serviceHistoryRequest) {
MTP::cancel(_serviceHistoryRequest);
@ -332,8 +286,6 @@ void MainWindow::setupMain(const MTPUser *self) {
_main->start(self);
fixOrder();
updateConnectingStatus();
}
void MainWindow::showSettings() {
@ -400,28 +352,6 @@ void MainWindow::ui_hideSettingsAndLayer(anim::type animated) {
}
}
void MainWindow::mtpStateChanged(int32 dc, int32 state) {
if (dc == MTP::maindc()) {
updateConnectingStatus();
Global::RefConnectionTypeChanged().notify();
}
}
void MainWindow::updateConnectingStatus() {
const auto state = MTP::dcstate();
const auto throughProxy = Global::UseProxy();
if (state == MTP::ConnectingState || state == MTP::DisconnectedState || (state < 0 && state > -600)) {
if (_main || getms() > 5000 || _connecting) {
showConnecting(lang(throughProxy ? lng_connecting_to_proxy : lng_connecting), throughProxy ? lang(lng_connecting_settings) : QString());
}
} else if (state < 0) {
showConnecting(lng_reconnecting(lt_count, ((-state) / 1000) + 1), lang(throughProxy ? lng_connecting_settings : lng_reconnecting_try_now));
QTimer::singleShot((-state) % 1000, this, SLOT(updateConnectingStatus()));
} else {
hideConnecting();
}
}
MainWidget *MainWindow::mainWidget() {
return _main;
}
@ -493,24 +423,6 @@ void MainWindow::ui_hideMediaPreview() {
_mediaPreview->hidePreview();
}
void MainWindow::showConnecting(const QString &text, const QString &reconnect) {
if (_connecting) {
_connecting->set(text, reconnect);
_connecting->show();
} else {
_connecting.create(bodyWidget(), text, reconnect);
_connecting->show();
updateControlsGeometry();
fixOrder();
}
}
void MainWindow::hideConnecting() {
if (_connecting) {
_connecting->hide();
}
}
void MainWindow::themeUpdated(const Window::Theme::BackgroundUpdate &data) {
using Type = Window::Theme::BackgroundUpdate::Type;
@ -758,7 +670,6 @@ bool MainWindow::takeThirdSectionFromLayer() {
void MainWindow::fixOrder() {
if (_layerBg) _layerBg->raise();
if (_mediaPreview) _mediaPreview->raise();
if (_connecting) _connecting->raise();
if (_testingThemeWarning) _testingThemeWarning->raise();
}
@ -850,7 +761,6 @@ void MainWindow::updateControlsGeometry() {
if (_intro) _intro->setGeometry(body);
if (_layerBg) _layerBg->setGeometry(body);
if (_mediaPreview) _mediaPreview->setGeometry(body);
if (_connecting) _connecting->moveToLeft(0, body.height() - _connecting->height());
if (_testingThemeWarning) _testingThemeWarning->setGeometry(body);
if (_main) _main->checkMainSectionToLayer();

View file

@ -38,26 +38,6 @@ namespace Ui {
class LinkButton;
} // namespace Ui
class ConnectingWidget : public TWidget {
Q_OBJECT
public:
ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect);
void set(const QString &text, const QString &reconnect);
protected:
void paintEvent(QPaintEvent *e) override;
public slots:
void onReconnect();
private:
QString _text;
int _textWidth = 0;
object_ptr<Ui::LinkButton> _reconnect;
};
class MediaPreviewWidget;
class MainWindow : public Platform::MainWindow {
@ -77,8 +57,6 @@ public:
void sendServiceHistoryRequest();
void showDelayedServiceMsgs();
void mtpStateChanged(int32 dc, int32 state);
MainWidget *chatsWidget() {
return mainWidget();
}
@ -153,7 +131,6 @@ protected:
public slots:
void showSettings();
void setInnerFocus();
void updateConnectingStatus();
void quitFromTray();
void showFromTray(QSystemTrayIcon::ActivationReason reason = QSystemTrayIcon::Unknown);
@ -174,9 +151,6 @@ signals:
void checkNewAuthorization();
private:
void showConnecting(const QString &text, const QString &reconnect = QString());
void hideConnecting();
[[nodiscard]] bool skipTrayClick() const;
void ensureLayerCreated();
@ -206,7 +180,6 @@ private:
object_ptr<Window::LayerStackWidget> _layerBg = { nullptr };
object_ptr<MediaPreviewWidget> _mediaPreview = { nullptr };
object_ptr<ConnectingWidget> _connecting = { nullptr };
object_ptr<Window::Theme::WarningWidget> _testingThemeWarning = { nullptr };
Local::ClearManager *_clearManager = nullptr;

View file

@ -397,9 +397,9 @@ void Messenger::startMtp() {
_mtproto->setUserPhone(cLoggedPhoneNumber());
_private->mtpConfig.mainDcId = _mtproto->mainDcId();
_mtproto->setStateChangedHandler([](MTP::ShiftedDcId shiftedDcId, int32 state) {
if (App::wnd()) {
App::wnd()->mtpStateChanged(shiftedDcId, state);
_mtproto->setStateChangedHandler([](MTP::ShiftedDcId dc, int32 state) {
if (dc == MTP::maindc()) {
Global::RefConnectionTypeChanged().notify();
}
});
_mtproto->setSessionResetHandler([](MTP::ShiftedDcId shiftedDcId) {

View file

@ -111,7 +111,6 @@ void AdvancedWidget::connectionTypeUpdated() {
}
void AdvancedWidget::onConnectionType() {
//Ui::show(Box<ConnectionBox>());
Ui::show(ProxiesBoxController::CreateOwningBox());
}
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY

View file

@ -125,6 +125,14 @@ void InfiniteRadialAnimation::draw(
Painter &p,
QPoint position,
int outerWidth) {
draw(p, position, _st.size, outerWidth);
}
void InfiniteRadialAnimation::draw(
Painter &p,
QPoint position,
QSize size,
int outerWidth) {
const auto state = computeState();
auto o = p.opacity();
@ -142,8 +150,8 @@ void InfiniteRadialAnimation::draw(
rtlrect(
position.x(),
position.y(),
_st.size.width(),
_st.size.height(),
size.width(),
size.height(),
outerWidth),
state.arcFrom,
state.arcLength);

View file

@ -73,6 +73,11 @@ public:
Painter &p,
QPoint position,
int outerWidth);
void draw(
Painter &p,
QPoint position,
QSize size,
int outerWidth);
State computeState();

View file

@ -135,7 +135,7 @@ public:
void setVisible(bool visible) final override {
auto wasVisible = !this->isHidden();
Parent::setVisible(visible);
setVisibleHook(visible);
visibilityChangedHook(wasVisible, !this->isHidden());
}
@ -151,6 +151,9 @@ protected:
bool eventHook(QEvent *event) override {
return Parent::event(event);
}
virtual void setVisibleHook(bool visible) {
Parent::setVisible(visible);
}
private:
void callSetVisible(bool visible) override {

View file

@ -14,35 +14,45 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
LinkButton::LinkButton(QWidget *parent, const QString &text, const style::LinkButton &st) : AbstractButton(parent)
LinkButton::LinkButton(
QWidget *parent,
const QString &text,
const style::LinkButton &st)
: AbstractButton(parent)
, _st(st)
, _text(text)
, _textWidth(st.font->width(_text))
, _st(st) {
resize(_textWidth, _st.font->height);
, _textWidth(st.font->width(_text)) {
resize(
naturalWidth(),
_st.padding.top() + _st.font->height + _st.padding.bottom());
setCursor(style::cur_pointer);
}
int LinkButton::naturalWidth() const {
return _textWidth;
return _st.padding.left() + _textWidth + _st.padding.right();
}
void LinkButton::paintEvent(QPaintEvent *e) {
Painter p(this);
auto &font = (isOver() ? _st.overFont : _st.font);
auto &pen = (isOver() ? _st.overColor : _st.color);
p.setFont(font);
p.setPen(pen);
if (_textWidth > width()) {
p.drawText(0, font->ascent, font->elided(_text, width()));
const auto left = _st.padding.left();
const auto top = _st.padding.top() + font->ascent;
if (width() > naturalWidth()) {
const auto available = width() - left - _st.padding.right();
p.drawText(left, top, font->elided(_text, available));
} else {
p.drawText(0, font->ascent, _text);
p.drawText(left, top, _text);
}
}
void LinkButton::setText(const QString &text) {
_text = text;
_textWidth = _st.font->width(_text);
resize(_textWidth, _st.font->height);
resize(naturalWidth(), _st.font->height);
update();
}

View file

@ -31,9 +31,9 @@ protected:
void onStateChanged(State was, StateChangeSource source) override;
private:
const style::LinkButton &_st;
QString _text;
int _textWidth = 0;
const style::LinkButton &_st;
};

View file

@ -29,6 +29,7 @@ LinkButton {
overColor: color;
font: font;
overFont: font;
padding: margins;
}
RippleAnimation {

View file

@ -247,6 +247,27 @@ windowEmojiSuggestionsPopup: PopupMenu(defaultPopupMenu) {
}
}
connectingLeftShadow: icon {{ "connecting_left_shadow", windowShadowFg }};
connectingLeft: icon {{ "connecting_left", windowBg }};
connectingRightShadow: icon {{ "connecting_right_shadow", windowShadowFg }};
connectingRight: icon {{ "connecting_right", windowBg }};
connectingBodyShadow: icon {{ "connecting_body_shadow", windowShadowFg }};
connectingBody: icon {{ "connecting_body", windowBg }};
connectingMargin: margins(2px, 2px, 2px, 2px);
connectingTextPadding: margins(18px, 11px, 18px, 0px);
connectingRadialSkip: 6px;
connectingRadial: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
color: menuIconFg;
thickness: 2px;
size: size(20px, 20px);
}
connectingRetryLink: LinkButton(defaultLinkButton) {
padding: margins(6px, 11px, 6px, 0px);
}
connectingProxyOff: icon {{ "proxy_off", menuIconFg }};
connectingProxyOn: icon {{ "proxy_on", windowBgActive }};
connectingDuration: 150;
// Mac specific
macAccessoryWidth: 450.;

View file

@ -0,0 +1,523 @@
/*
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 "window/window_connecting_widget.h"
#include "ui/widgets/buttons.h"
#include "ui/effects/radial_animation.h"
#include "boxes/connection_box.h"
#include "lang/lang_keys.h"
#include "styles/style_window.h"
namespace Window {
namespace {
constexpr auto kIgnoreStartConnectingFor = TimeMs(3000);
constexpr auto kConnectingStateDelay = TimeMs(1000);
constexpr auto kRefreshTimeout = TimeMs(200);
constexpr auto kMinimalWaitingStateDuration = TimeMs(4000);
class Progress : public Ui::RpWidget {
public:
Progress(QWidget *parent);
protected:
void paintEvent(QPaintEvent *e) override;
private:
void step(TimeMs ms, bool timer);
Ui::InfiniteRadialAnimation _animation;
};
Progress::Progress(QWidget *parent)
: RpWidget(parent)
, _animation(animation(this, &Progress::step), st::connectingRadial) {
setAttribute(Qt::WA_OpaquePaintEvent);
setAttribute(Qt::WA_TransparentForMouseEvents);
resize(st::connectingRadial.size);
_animation.start();
}
void Progress::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::windowBg);
const auto &st = st::connectingRadial;
const auto shift = st.thickness - (st.thickness / 2);
_animation.draw(
p,
{ shift, shift },
QSize(st.size.width() - 2 * shift, st.size.height() - 2 * shift),
width());
}
void Progress::step(TimeMs ms, bool timer) {
if (timer) {
update();
}
}
} // namespace
class ConnectingWidget::ProxyIcon : public Ui::RpWidget {
public:
ProxyIcon(QWidget *parent);
void setToggled(bool toggled);
void setOpacity(float64 opacity);
protected:
void paintEvent(QPaintEvent *e) override;
private:
float64 _opacity = 1.;
QPixmap _cacheOn;
QPixmap _cacheOff;
bool _toggled = true;
};
ConnectingWidget::ProxyIcon::ProxyIcon(QWidget *parent) : RpWidget(parent) {
resize(
std::max(
st::connectingRadial.size.width(),
st::connectingProxyOn.width()),
std::max(
st::connectingRadial.size.height(),
st::connectingProxyOn.height()));
const auto prepareCache = [&](const style::icon &icon) {
auto image = QImage(
size() * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied);
image.fill(st::windowBg->c);
{
Painter p(&image);
icon.paint(
p,
(width() - icon.width()) / 2,
(height() - icon.height()) / 2,
width());
}
return App::pixmapFromImageInPlace(std::move(image));
};
_cacheOn = prepareCache(st::connectingProxyOn);
_cacheOff = prepareCache(st::connectingProxyOff);
}
void ConnectingWidget::ProxyIcon::setToggled(bool toggled) {
if (_toggled != toggled) {
_toggled = toggled;
update();
}
}
void ConnectingWidget::ProxyIcon::setOpacity(float64 opacity) {
_opacity = opacity;
if (_opacity == 0.) {
hide();
} else if (isHidden()) {
show();
}
update();
}
void ConnectingWidget::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 {
return (type == other.type)
&& (useProxy == other.useProxy)
&& (underCursor == other.underCursor)
&& (waitTillRetry == other.waitTillRetry);
}
ConnectingWidget::ConnectingWidget(QWidget *parent)
: AbstractButton(parent)
, _refreshTimer([=] { refreshState(); })
, _currentLayout(computeLayout(_state)) {
_proxyIcon = Ui::CreateChild<ProxyIcon>(this);
_progress = Ui::CreateChild<Progress>(this);
addClickHandler([=] {
Ui::show(ProxiesBoxController::CreateOwningBox());
});
subscribe(Global::RefConnectionTypeChanged(), [=] {
refreshState();
});
refreshState();
}
rpl::producer<float64> ConnectingWidget::visibility() const {
return _visibilityValues.events_starting_with(currentVisibility());
}
void ConnectingWidget::finishAnimating() {
if (_contentWidth.animating()) {
_contentWidth.finish();
updateWidth();
}
if (_visibility.animating()) {
_visibility.finish();
updateVisibility();
}
}
void ConnectingWidget::setForceHidden(bool hidden) {
if (_forceHidden == hidden) {
return;
}
if (hidden) {
const auto real = isHidden();
if (!real) {
hide();
}
_realHidden = real;
}
_forceHidden = hidden;
if (!hidden && isHidden() != _realHidden) {
setVisible(!_realHidden);
}
}
void ConnectingWidget::setVisibleHook(bool visible) {
if (_forceHidden) {
_realHidden = !visible;
return;
}
QWidget::setVisible(visible);
}
base::unique_qptr<ConnectingWidget> ConnectingWidget::CreateDefaultWidget(
Ui::RpWidget *parent,
rpl::producer<bool> shown) {
auto result = base::make_unique_q<Window::ConnectingWidget>(parent);
const auto weak = result.get();
rpl::combine(
result->visibility(),
parent->heightValue(),
std::move(shown)
) | rpl::start_with_next([=](float64 visible, int height, bool shown) {
const auto hidden = (visible == 0.) || !shown;
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());
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() {
const auto state = [&]() -> State {
const auto under = isOver();
const auto mtp = MTP::dcstate();
const auto throughProxy = Global::UseProxy();
if (mtp == MTP::ConnectingState
|| mtp == MTP::DisconnectedState
|| (mtp < 0 && mtp > -600)) {
return { State::Type::Connecting, throughProxy, under };
} else if (mtp < 0
&& mtp >= -kMinimalWaitingStateDuration
&& _state.type != State::Type::Waiting) {
return { State::Type::Connecting, throughProxy, under };
} else if (mtp < 0) {
const auto seconds = ((-mtp) / 1000) + 1;
return { State::Type::Waiting, throughProxy, under, seconds };
}
return { State::Type::Connected, throughProxy, under };
}();
if (state.waitTillRetry > 0) {
_refreshTimer.callOnce(kRefreshTimeout);
}
if (state == _state) {
return;
} else if (state.type == State::Type::Connecting
&& _state.type == State::Type::Connected) {
const auto now = getms();
if (!_connectingStartedAt) {
_connectingStartedAt = now;
_refreshTimer.callOnce(kConnectingStateDelay);
return;
}
const auto applyConnectingAt = std::max(
_connectingStartedAt + kConnectingStateDelay,
kIgnoreStartConnectingFor);
if (now < applyConnectingAt) {
_refreshTimer.callOnce(applyConnectingAt - now);
return;
}
}
applyState(state);
}
void ConnectingWidget::applyState(const State &state) {
const auto newLayout = computeLayout(state);
const auto guard = gsl::finally([&] {
updateWidth();
update();
});
_state = state;
if (_currentLayout.visible != newLayout.visible) {
changeVisibilityWithLayout(newLayout);
return;
}
if (_currentLayout.contentWidth != newLayout.contentWidth) {
if (!_currentLayout.contentWidth
|| !newLayout.contentWidth
|| _contentWidth.animating()) {
_contentWidth.start(
[=] { updateWidth(); },
_currentLayout.contentWidth,
newLayout.contentWidth,
st::connectingDuration);
}
}
const auto saved = _currentLayout;
setLayout(newLayout);
if (_currentLayout.text.isEmpty()
&& !saved.text.isEmpty()
&& _contentWidth.animating()) {
_currentLayout.text = saved.text;
_currentLayout.textWidth = saved.textWidth;
}
refreshRetryLink(_currentLayout.hasRetry);
}
void ConnectingWidget::changeVisibilityWithLayout(const Layout &layout) {
Expects(_currentLayout.visible != layout.visible);
const auto changeLayout = !_currentLayout.visible;
_visibility.start(
[=] { updateVisibility(); },
layout.visible ? 0. : 1.,
layout.visible ? 1. : 0.,
st::connectingDuration);
if (_contentWidth.animating()) {
_contentWidth.start(
[=] { updateWidth(); },
_currentLayout.contentWidth,
(changeLayout ? layout : _currentLayout).contentWidth,
st::connectingDuration);
}
if (changeLayout) {
setLayout(layout);
} else {
_currentLayout.visible = layout.visible;
}
}
void ConnectingWidget::setLayout(const Layout &layout) {
_currentLayout = layout;
_proxyIcon->setToggled(_currentLayout.proxyEnabled);
_progress->setVisible(_contentWidth.animating()
|| _currentLayout.progressShown);
}
void ConnectingWidget::refreshRetryLink(bool hasRetry) {
if (hasRetry && !_retry) {
_retry = base::make_unique_q<Ui::LinkButton>(
this,
lang(lng_reconnecting_try_now),
st::connectingRetryLink);
_retry->addClickHandler([=] {
MTP::restart();
});
updateRetryGeometry();
} else if (!hasRetry) {
_retry = nullptr;
}
}
void ConnectingWidget::updateVisibility() {
const auto value = currentVisibility();
if (value == 0. && _contentWidth.animating()) {
_contentWidth.finish();
updateWidth();
}
_visibilityValues.fire_copy(value);
}
float64 ConnectingWidget::currentVisibility() const {
return _visibility.current(_currentLayout.visible ? 1. : 0.);
}
auto ConnectingWidget::computeLayout(const State &state) const -> Layout {
auto result = Layout();
result.proxyEnabled = state.useProxy;
result.progressShown = (state.type != State::Type::Connected);
result.visible = state.useProxy
|| state.type == State::Type::Connecting
|| state.type == State::Type::Waiting;
switch (state.type) {
case State::Type::Connecting:
result.text = state.underCursor ? lang(lng_connecting) : QString();
break;
case State::Type::Waiting:
Assert(state.waitTillRetry > 0);
result.text = lng_reconnecting(lt_count, state.waitTillRetry);
break;
}
result.textWidth = st::normalFont->width(result.text);
const auto maxTextWidth = (state.type == State::Type::Waiting)
? st::normalFont->width(lng_reconnecting(lt_count, 88))
: result.textWidth;
result.contentWidth = (result.textWidth > 0)
? (st::connectingTextPadding.left()
+ result.textWidth
+ st::connectingTextPadding.right())
: 0;
if (state.type == State::Type::Waiting) {
result.contentWidth += st::connectingRetryLink.padding.left()
+ st::connectingRetryLink.font->width(
lang(lng_reconnecting_try_now))
+ st::connectingRetryLink.padding.right();
}
result.hasRetry = (state.type == State::Type::Waiting);
return result;
}
void ConnectingWidget::updateWidth() {
const auto current = _contentWidth.current(_currentLayout.contentWidth);
const auto height = st::connectingLeft.height();
const auto desired = QRect(0, 0, current, height).marginsAdded(
style::margins(
st::connectingLeft.width(),
0,
st::connectingRight.width(),
0)
).marginsAdded(
st::connectingMargin
);
resize(desired.size());
if (!_contentWidth.animating()) {
_progress->setVisible(_currentLayout.progressShown);
}
update();
}
rpl::producer<bool> AdaptiveIsOneColumn() {
return rpl::single(
Adaptive::OneColumn()
) | rpl::then(base::ObservableViewer(
Adaptive::Changed()
) | rpl::map([] {
return Adaptive::OneColumn();
}));
}
} // namespace Window

View file

@ -0,0 +1,100 @@
/*
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
*/
#pragma once
#include "ui/abstract_button.h"
#include "base/timer.h"
namespace Ui {
class LinkButton;
} // namespace Ui
namespace Window {
class ConnectingWidget
: public Ui::AbstractButton
, private base::Subscriber {
public:
ConnectingWidget(QWidget *parent);
rpl::producer<float64> visibility() const;
void finishAnimating();
void setForceHidden(bool hidden);
void setVisibleHook(bool visible) override;
static base::unique_qptr<ConnectingWidget> CreateDefaultWidget(
Ui::RpWidget *parent,
rpl::producer<bool> shown);
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void onStateChanged(State was, StateChangeSource source) override;
private:
class ProxyIcon;
struct State {
enum class Type {
Connected,
Connecting,
Waiting,
};
Type type = Type::Connected;
bool useProxy = false;
bool underCursor = false;
int waitTillRetry = 0;
bool operator==(const State &other) const;
};
struct Layout {
bool visible = false;
bool hasRetry = false;
bool proxyEnabled = false;
bool progressShown = false;
int contentWidth = 0;
QString text;
int textWidth = 0;
};
void updateRetryGeometry();
void updateWidth();
void updateVisibility();
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;
QRect innerRect() const;
QRect contentRect() const;
QRect textRect() const;
base::Timer _refreshTimer;
State _state;
Layout _currentLayout;
TimeMs _connectingStartedAt = 0;
Animation _contentWidth;
Animation _visibility;
base::unique_qptr<Ui::LinkButton> _retry;
QPointer<Ui::RpWidget> _progress;
QPointer<ProxyIcon> _proxyIcon;
bool _forceHidden = false;
bool _realHidden = false;
rpl::event_stream<float64> _visibilityValues;
};
rpl::producer<bool> AdaptiveIsOneColumn();
} // namespace Window

View file

@ -701,6 +701,8 @@
<(src_loc)/window/section_memento.h
<(src_loc)/window/section_widget.cpp
<(src_loc)/window/section_widget.h
<(src_loc)/window/window_connecting_widget.cpp
<(src_loc)/window/window_connecting_widget.h
<(src_loc)/window/window_controller.cpp
<(src_loc)/window/window_controller.h
<(src_loc)/window/window_main_menu.cpp