Moved FloatAnimation->Animation, Animation->BasicAnimation.

This commit is contained in:
John Preston 2016-12-07 16:32:25 +03:00
parent 06ed7b8eaf
commit 47977009b8
90 changed files with 865 additions and 1139 deletions

View file

@ -436,9 +436,7 @@ SetupChannelBox::SetupChannelBox(ChannelData *channel, bool existing) : Abstract
, _link(this, st::defaultInputField, QString(), channel->username, true) , _link(this, st::defaultInputField, QString(), channel->username, true)
, _linkOver(false) , _linkOver(false)
, _save(this, lang(lng_settings_save), st::defaultBoxButton) , _save(this, lang(lng_settings_save), st::defaultBoxButton)
, _skip(this, lang(existing ? lng_cancel : lng_create_group_skip), st::cancelBoxButton) , _skip(this, lang(existing ? lng_cancel : lng_create_group_skip), st::cancelBoxButton) {
, a_goodOpacity(0, 0)
, _a_goodFade(animation(this, &SetupChannelBox::step_goodFade)) {
setMouseTracking(true); setMouseTracking(true);
_checkRequestId = MTP::send(MTPchannels_CheckUsername(_channel->inputChannel, MTP_string("preston")), RPCDoneHandlerPtr(), rpcFail(&SetupChannelBox::onFirstCheckFail)); _checkRequestId = MTP::send(MTPchannels_CheckUsername(_channel->inputChannel, MTP_string("preston")), RPCDoneHandlerPtr(), rpcFail(&SetupChannelBox::onFirstCheckFail));
@ -515,14 +513,17 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) {
p.setFont(_linkOver ? st::boxTextFont->underline() : st::boxTextFont); p.setFont(_linkOver ? st::boxTextFont->underline() : st::boxTextFont);
p.setPen(st::defaultLinkButton.color); p.setPen(st::defaultLinkButton.color);
p.drawText(_invitationLink, _channel->inviteLink(), option); p.drawText(_invitationLink, _channel->inviteLink(), option);
if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) { if (!_goodTextLink.isEmpty()) {
p.setOpacity(a_goodOpacity.current()); auto opacity = _a_goodOpacity.current(getms(), 0.);
if (opacity > 0.) {
p.setOpacity(opacity);
p.setPen(st::boxTextFgGood); p.setPen(st::boxTextFgGood);
p.setFont(st::boxTextFont); p.setFont(st::boxTextFont);
p.drawTextRight(st::boxPadding.right(), _link->y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _goodTextLink); p.drawTextRight(st::boxPadding.right(), _link->y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _goodTextLink);
p.setOpacity(1); p.setOpacity(1);
} }
} }
}
} else { } else {
if (!_errorText.isEmpty()) { if (!_errorText.isEmpty()) {
p.setPen(st::boxTextFgError); p.setPen(st::boxTextFgError);
@ -557,8 +558,8 @@ void SetupChannelBox::mousePressEvent(QMouseEvent *e) {
if (_linkOver) { if (_linkOver) {
Application::clipboard()->setText(_channel->inviteLink()); Application::clipboard()->setText(_channel->inviteLink());
_goodTextLink = lang(lng_create_channel_link_copied); _goodTextLink = lang(lng_create_channel_link_copied);
a_goodOpacity = anim::value(1, 0); _a_goodOpacity.finish();
_a_goodFade.start(); _a_goodOpacity.start([this] { update(); }, 1., 0., st::newGroupLinkFadeDuration);
} }
} }
@ -577,17 +578,6 @@ void SetupChannelBox::updateSelected(const QPoint &cursorGlobalPosition) {
} }
} }
void SetupChannelBox::step_goodFade(float64 ms, bool timer) {
float dt = ms / st::newGroupLinkFadeDuration;
if (dt >= 1) {
_a_goodFade.stop();
a_goodOpacity.finish();
} else {
a_goodOpacity.update(dt, anim::linear);
}
if (timer) update();
}
void SetupChannelBox::closePressed() { void SetupChannelBox::closePressed() {
if (!_existing) { if (!_existing) {
Ui::showLayer(new ContactsBox(_channel)); Ui::showLayer(new ContactsBox(_channel));

View file

@ -154,7 +154,6 @@ protected:
private: private:
void updateSelected(const QPoint &cursorGlobalPosition); void updateSelected(const QPoint &cursorGlobalPosition);
void step_goodFade(float64 ms, bool timer);
void onUpdateDone(const MTPBool &result); void onUpdateDone(const MTPBool &result);
bool onUpdateFail(const RPCError &error); bool onUpdateFail(const RPCError &error);
@ -190,8 +189,7 @@ private:
QString _sentUsername, _checkUsername, _errorText, _goodText; QString _sentUsername, _checkUsername, _errorText, _goodText;
QString _goodTextLink; QString _goodTextLink;
anim::value a_goodOpacity; Animation _a_goodOpacity;
Animation _a_goodFade;
QTimer _checkTimer; QTimer _checkTimer;

View file

@ -204,10 +204,7 @@ void ConfirmBotGameBox::onOpenLink() {
MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth) MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth)
, _close(this, lang(lng_box_ok), st::defaultBoxButton) , _close(this, lang(lng_box_ok), st::defaultBoxButton)
, _text(st::boxTextFont, lng_participant_invite_sorry(lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) , _text(st::boxTextFont, lng_participant_invite_sorry(lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right())
, _link(link) , _link(link) {
, _linkOver(false)
, a_goodOpacity(0, 0)
, _a_good(animation(this, &MaxInviteBox::step_good)) {
setMouseTracking(true); setMouseTracking(true);
_textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right(); _textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right();
@ -226,8 +223,8 @@ void MaxInviteBox::mousePressEvent(QMouseEvent *e) {
if (_linkOver) { if (_linkOver) {
Application::clipboard()->setText(_link); Application::clipboard()->setText(_link);
_goodTextLink = lang(lng_create_channel_link_copied); _goodTextLink = lang(lng_create_channel_link_copied);
a_goodOpacity = anim::value(1, 0); _a_goodOpacity.finish();
_a_good.start(); _a_goodOpacity.start([this] { update(); }, 1., 0., st::newGroupLinkFadeDuration);
} }
} }
@ -246,17 +243,6 @@ void MaxInviteBox::updateSelected(const QPoint &cursorGlobalPosition) {
} }
} }
void MaxInviteBox::step_good(float64 ms, bool timer) {
float dt = ms / st::newGroupLinkFadeDuration;
if (dt >= 1) {
_a_good.stop();
a_goodOpacity.finish();
} else {
a_goodOpacity.update(dt, anim::linear);
}
if (timer) update();
}
void MaxInviteBox::paintEvent(QPaintEvent *e) { void MaxInviteBox::paintEvent(QPaintEvent *e) {
AbstractBox::paintEvent(e); AbstractBox::paintEvent(e);
@ -271,14 +257,17 @@ void MaxInviteBox::paintEvent(QPaintEvent *e) {
p.setFont(_linkOver ? st::defaultInputField.font->underline() : st::defaultInputField.font); p.setFont(_linkOver ? st::defaultInputField.font->underline() : st::defaultInputField.font);
p.setPen(st::defaultLinkButton.color); p.setPen(st::defaultLinkButton.color);
p.drawText(_invitationLink, _link, option); p.drawText(_invitationLink, _link, option);
if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) { if (!_goodTextLink.isEmpty()) {
p.setOpacity(a_goodOpacity.current()); auto opacity = _a_goodOpacity.current(getms(), 0.);
if (opacity > 0.) {
p.setOpacity(opacity);
p.setPen(st::boxTextFgGood); p.setPen(st::boxTextFgGood);
p.setFont(st::boxTextFont); p.setFont(st::boxTextFont);
p.drawTextLeft(st::boxPadding.left(), height() - st::boxButtonPadding.bottom() - _close->height() + st::defaultBoxButton.textTop + st::defaultBoxButton.font->ascent - st::boxTextFont->ascent, width(), _goodTextLink); p.drawTextLeft(st::boxPadding.left(), height() - st::boxButtonPadding.bottom() - _close->height() + st::defaultBoxButton.textTop + st::defaultBoxButton.font->ascent - st::boxTextFont->ascent, width(), _goodTextLink);
p.setOpacity(1); p.setOpacity(1);
} }
} }
}
void MaxInviteBox::resizeEvent(QResizeEvent *e) { void MaxInviteBox::resizeEvent(QResizeEvent *e) {
_close->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close->height()); _close->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close->height());

View file

@ -164,7 +164,6 @@ protected:
private: private:
void updateSelected(const QPoint &cursorGlobalPosition); void updateSelected(const QPoint &cursorGlobalPosition);
void step_good(float64 ms, bool timer);
ChildWidget<Ui::RoundButton> _close; ChildWidget<Ui::RoundButton> _close;
@ -173,13 +172,12 @@ private:
QString _link; QString _link;
QRect _invitationLink; QRect _invitationLink;
bool _linkOver; bool _linkOver = false;
QPoint _lastMousePos; QPoint _lastMousePos;
QString _goodTextLink; QString _goodTextLink;
anim::value a_goodOpacity; Animation _a_goodOpacity;
Animation _a_good;
}; };

View file

@ -102,7 +102,7 @@ private:
NotificationsBox *_owner; NotificationsBox *_owner;
QPixmap _cache; QPixmap _cache;
FloatAnimation _opacity; Animation _opacity;
bool _hiding = false; bool _hiding = false;
bool _deleted = false; bool _deleted = false;
@ -116,7 +116,7 @@ NotificationsBox::NotificationsBox() : AbstractBox()
_sampleOpacities.reserve(kMaxNotificationsCount); _sampleOpacities.reserve(kMaxNotificationsCount);
for (int i = 0; i != kMaxNotificationsCount; ++i) { for (int i = 0; i != kMaxNotificationsCount; ++i) {
_countSlider->addSection(QString::number(i + 1)); _countSlider->addSection(QString::number(i + 1));
_sampleOpacities.push_back(FloatAnimation()); _sampleOpacities.push_back(Animation());
} }
_countSlider->setActiveSectionFast(_oldCount - 1); _countSlider->setActiveSectionFast(_oldCount - 1);
_countSlider->setSectionActivatedCallback([this] { countChanged(); }); _countSlider->setSectionActivatedCallback([this] { countChanged(); });

View file

@ -62,7 +62,7 @@ private:
QPixmap _notificationSampleSmall; QPixmap _notificationSampleSmall;
QPixmap _notificationSampleLarge; QPixmap _notificationSampleLarge;
ScreenCorner _chosenCorner; ScreenCorner _chosenCorner;
std_::vector_of_moveable<FloatAnimation> _sampleOpacities; std_::vector_of_moveable<Animation> _sampleOpacities;
bool _isOverCorner = false; bool _isOverCorner = false;
ScreenCorner _overCorner = ScreenCorner::TopLeft; ScreenCorner _overCorner = ScreenCorner::TopLeft;

View file

@ -273,12 +273,15 @@ void ShareBox::onMustScrollTo(int top, int bottom) {
to = bottom - (scrollBottom - scrollTop); to = bottom - (scrollBottom - scrollTop);
} }
if (from != to) { if (from != to) {
_scrollAnimation.start([this]() { _scrollAnimation.start([this]() { scrollAnimationCallback(); }, from, to, st::shareScrollDuration, anim::sineInOut);
scrollArea()->scrollToY(qRound(_scrollAnimation.current(scrollArea()->scrollTop())));
}, from, to, st::shareScrollDuration, anim::sineInOut);
} }
} }
void ShareBox::scrollAnimationCallback() {
auto scrollTop = qRound(_scrollAnimation.current(scrollArea()->scrollTop()));
scrollArea()->scrollToY(scrollTop);
}
void ShareBox::onScroll() { void ShareBox::onScroll() {
auto scroll = scrollArea(); auto scroll = scrollArea();
auto scrollTop = scroll->scrollTop(); auto scrollTop = scroll->scrollTop();
@ -505,7 +508,8 @@ void ShareBox::Inner::paintChat(Painter &p, TimeMs ms, Chat *chat, int index) {
auto photoTop = st::sharePhotoTop; auto photoTop = st::sharePhotoTop;
chat->checkbox.paint(p, ms, x + photoLeft, y + photoTop, outerWidth); chat->checkbox.paint(p, ms, x + photoLeft, y + photoTop, outerWidth);
p.setPen(anim::pen(st::shareNameFg, st::shareNameActiveFg, chat->nameActive.current((index == _active) ? 1. : 0.))); auto nameActive = chat->nameActive.current(ms, (index == _active) ? 1. : 0.);
p.setPen(anim::pen(st::shareNameFg, st::shareNameActiveFg, nameActive));
auto nameWidth = (_rowWidth - st::shareColumnSkip); auto nameWidth = (_rowWidth - st::shareColumnSkip);
auto nameLeft = st::shareColumnSkip / 2; auto nameLeft = st::shareColumnSkip / 2;

View file

@ -69,6 +69,8 @@ protected:
void doSetInnerFocus() override; void doSetInnerFocus() override;
private: private:
void scrollAnimationCallback();
void onFilterUpdate(const QString &query); void onFilterUpdate(const QString &query);
void onSelectedChanged(); void onSelectedChanged();
void moveButtons(); void moveButtons();
@ -107,7 +109,7 @@ private:
using PeopleQueries = QMap<mtpRequestId, QString>; using PeopleQueries = QMap<mtpRequestId, QString>;
PeopleQueries _peopleQueries; PeopleQueries _peopleQueries;
FloatAnimation _scrollAnimation; Animation _scrollAnimation;
}; };
@ -161,7 +163,7 @@ private:
PeerData *peer; PeerData *peer;
Ui::RoundImageCheckbox checkbox; Ui::RoundImageCheckbox checkbox;
Text name; Text name;
FloatAnimation nameActive; Animation nameActive;
}; };
void paintChat(Painter &p, TimeMs ms, Chat *chat, int index); void paintChat(Painter &p, TimeMs ms, Chat *chat, int index);
void updateChat(PeerData *peer); void updateChat(PeerData *peer);

View file

@ -246,7 +246,7 @@ private:
QList<TimeMs> _animStartTimes; QList<TimeMs> _animStartTimes;
TimeMs _aboveShadowFadeStart = 0; TimeMs _aboveShadowFadeStart = 0;
anim::value _aboveShadowFadeOpacity; anim::value _aboveShadowFadeOpacity;
Animation _a_shifting; BasicAnimation _a_shifting;
base::lambda<void(uint64 setId)> _installSetCallback; base::lambda<void(uint64 setId)> _installSetCallback;

View file

@ -177,7 +177,7 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
if (!doc || !doc->sticker()) continue; if (!doc || !doc->sticker()) continue;
_pack.push_back(doc); _pack.push_back(doc);
_packOvers.push_back(FloatAnimation()); _packOvers.push_back(Animation());
} }
auto &packs(d.vpacks.c_vector().v); auto &packs(d.vpacks.c_vector().v);
for (int i = 0, l = packs.size(); i < l; ++i) { for (int i = 0, l = packs.size(); i < l; ++i) {
@ -389,6 +389,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
if (_pack.isEmpty()) return; if (_pack.isEmpty()) return;
auto ms = getms();
int32 rows = _pack.size() / StickerPanPerRow + ((_pack.size() % StickerPanPerRow) ? 1 : 0); int32 rows = _pack.size() / StickerPanPerRow + ((_pack.size() % StickerPanPerRow) ? 1 : 0);
int32 from = qFloor(e->rect().top() / st::stickersSize.height()), to = qFloor(e->rect().bottom() / st::stickersSize.height()) + 1; int32 from = qFloor(e->rect().top() / st::stickersSize.height()), to = qFloor(e->rect().bottom() / st::stickersSize.height()) + 1;
@ -401,7 +402,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
DocumentData *doc = _pack.at(index); DocumentData *doc = _pack.at(index);
QPoint pos(st::stickersPadding.left() + j * st::stickersSize.width(), st::stickersPadding.top() + i * st::stickersSize.height()); QPoint pos(st::stickersPadding.left() + j * st::stickersSize.width(), st::stickersPadding.top() + i * st::stickersSize.height());
if (auto over = _packOvers[index].current((index == _selected) ? 1. : 0.)) { if (auto over = _packOvers[index].current(ms, (index == _selected) ? 1. : 0.)) {
p.setOpacity(over); p.setOpacity(over);
QPoint tl(pos); QPoint tl(pos);
if (rtl()) tl.setX(width() - tl.x() - st::stickersSize.width()); if (rtl()) tl.setX(width() - tl.x() - st::stickersSize.width());

View file

@ -113,7 +113,7 @@ private:
return (_setFlags & MTPDstickerSet::Flag::f_masks); return (_setFlags & MTPDstickerSet::Flag::f_masks);
} }
std_::vector_of_moveable<FloatAnimation> _packOvers; std_::vector_of_moveable<Animation> _packOvers;
StickerPack _pack; StickerPack _pack;
StickersByEmojiMap _emoji; StickersByEmojiMap _emoji;
bool _loaded = false; bool _loaded = false;

View file

@ -172,8 +172,6 @@ historyViewsSendingIcon: icon {{ "dialogs_sending", #a0adb5, point(3px, 0px) }};
historyViewsSendingInvertedIcon: icon {{ "dialogs_sending", #ffffffc8, point(3px, 0px) }}; historyViewsSendingInvertedIcon: icon {{ "dialogs_sending", #ffffffc8, point(3px, 0px) }};
dialogsUpdateButton: FlatButton { dialogsUpdateButton: FlatButton {
duration: 0;
color: activeButtonFg; color: activeButtonFg;
overColor: activeButtonFgOver; overColor: activeButtonFgOver;

View file

@ -477,7 +477,7 @@ void DialogsInner::mousePressEvent(QMouseEvent *e) {
setHashtagPressed(_hashtagSelected); setHashtagPressed(_hashtagSelected);
_hashtagDeletePressed = _hashtagDeleteSelected; _hashtagDeletePressed = _hashtagDeleteSelected;
setFilteredPressed(_filteredSelected); setFilteredPressed(_filteredSelected);
setPeerSearchPressed(_peerSearchPressed); setPeerSearchPressed(_peerSearchSelected);
setSearchedPressed(_searchedSelected); setSearchedPressed(_searchedSelected);
if (_importantSwitchPressed) { if (_importantSwitchPressed) {
_importantSwitch->row.addRipple(e->pos(), QSize(getFullWidth(), st::dialogsImportantBarHeight), [this] { _importantSwitch->row.addRipple(e->pos(), QSize(getFullWidth(), st::dialogsImportantBarHeight), [this] {

View file

@ -387,7 +387,7 @@ private:
ChildWidget<DialogsInner> _inner; ChildWidget<DialogsInner> _inner;
ChildWidget<Ui::FlatButton> _updateTelegram = { nullptr }; ChildWidget<Ui::FlatButton> _updateTelegram = { nullptr };
FloatAnimation _a_show; Animation _a_show;
Window::SlideDirection _showDirection; Window::SlideDirection _showDirection;
QPixmap _cacheUnder, _cacheOver; QPixmap _cacheUnder, _cacheOver;

View file

@ -61,7 +61,7 @@ public:
typedef QMap<History*, TimeMs> TypingHistories; // when typing in this history started typedef QMap<History*, TimeMs> TypingHistories; // when typing in this history started
TypingHistories typing; TypingHistories typing;
Animation _a_typings; BasicAnimation _a_typings;
int unreadBadge() const; int unreadBadge() const;
int unreadMutedCount() const { int unreadMutedCount() const {

View file

@ -31,9 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent) FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent)
, _scroll(this, st::mentionScroll) , _scroll(this, st::mentionScroll)
, _inner(this, &_mrows, &_hrows, &_brows, &_srows) , _inner(this, &_mrows, &_hrows, &_brows, &_srows) {
, a_opacity(0)
, _a_appearance(animation(this, &FieldAutocomplete::step_appearance)) {
connect(_inner, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)));
connect(_inner, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)));
connect(_inner, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)));
@ -47,15 +45,22 @@ FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent)
_scroll->show(); _scroll->show();
_inner->show(); _inner->show();
hide();
connect(_scroll, SIGNAL(geometryChanged()), _inner, SLOT(onParentGeometryChanged())); connect(_scroll, SIGNAL(geometryChanged()), _inner, SLOT(onParentGeometryChanged()));
} }
void FieldAutocomplete::paintEvent(QPaintEvent *e) { void FieldAutocomplete::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
if (_a_appearance.animating()) { auto opacity = _a_opacity.current(getms(), _hiding ? 0. : 1.);
p.setOpacity(a_opacity.current()); if (opacity < 1.) {
if (opacity > 0.) {
p.setOpacity(opacity);
p.drawPixmap(0, 0, _cache); p.drawPixmap(0, 0, _cache);
} else if (_hiding) {
}
return; return;
} }
@ -392,10 +397,7 @@ void FieldAutocomplete::recount(bool resetScroll) {
} }
void FieldAutocomplete::hideFast() { void FieldAutocomplete::hideFast() {
if (_a_appearance.animating()) { _a_opacity.finish();
_a_appearance.stop();
}
a_opacity = anim::value();
hideFinish(); hideFinish();
} }
@ -410,9 +412,8 @@ void FieldAutocomplete::hideAnimated() {
} }
_scroll->hide(); _scroll->hide();
_hiding = true; _hiding = true;
a_opacity.start(0); _a_opacity.start([this] { animationCallback(); }, 1., 0., st::defaultDropdownDuration);
setAttribute(Qt::WA_OpaquePaintEvent, false); setAttribute(Qt::WA_OpaquePaintEvent, false);
_a_appearance.start();
} }
void FieldAutocomplete::hideFinish() { void FieldAutocomplete::hideFinish() {
@ -423,7 +424,7 @@ void FieldAutocomplete::hideFinish() {
} }
void FieldAutocomplete::showAnimated() { void FieldAutocomplete::showAnimated() {
if (!isHidden() && a_opacity.current() == 1 && !_hiding) { if (!isHidden() && !_hiding) {
return; return;
} }
if (_cache.isNull()) { if (_cache.isNull()) {
@ -433,16 +434,13 @@ void FieldAutocomplete::showAnimated() {
_scroll->hide(); _scroll->hide();
_hiding = false; _hiding = false;
show(); show();
a_opacity.start(1); _a_opacity.start([this] { animationCallback(); }, 0., 1., st::defaultDropdownDuration);
setAttribute(Qt::WA_OpaquePaintEvent, false); setAttribute(Qt::WA_OpaquePaintEvent, false);
_a_appearance.start();
} }
void FieldAutocomplete::step_appearance(float64 ms, bool timer) { void FieldAutocomplete::animationCallback() {
float64 dt = ms / st::defaultDropdownDuration; update();
if (dt >= 1) { if (!_a_opacity.animating()) {
_a_appearance.stop();
a_opacity.finish();
_cache = QPixmap(); _cache = QPixmap();
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
if (_hiding) { if (_hiding) {
@ -451,10 +449,7 @@ void FieldAutocomplete::step_appearance(float64 ms, bool timer) {
_scroll->show(); _scroll->show();
_inner->clearSel(); _inner->clearSel();
} }
} else {
a_opacity.update(dt, anim::linear);
} }
if (timer) update();
} }
const QString &FieldAutocomplete::filter() const { const QString &FieldAutocomplete::filter() const {

View file

@ -48,8 +48,6 @@ public:
void showStickers(EmojiPtr emoji); void showStickers(EmojiPtr emoji);
void setBoundings(QRect boundings); void setBoundings(QRect boundings);
void step_appearance(float64 ms, bool timer);
const QString &filter() const; const QString &filter() const;
ChatData *chat() const; ChatData *chat() const;
ChannelData *channel() const; ChannelData *channel() const;
@ -97,6 +95,7 @@ protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
private: private:
void animationCallback();
void hideFinish(); void hideFinish();
void updateFiltered(bool resetScroll = false); void updateFiltered(bool resetScroll = false);
@ -131,8 +130,7 @@ private:
int32 _width, _height; int32 _width, _height;
bool _hiding = false; bool _hiding = false;
anim::value a_opacity; Animation _a_opacity;
Animation _a_appearance;
friend class internal::FieldAutocompleteInner; friend class internal::FieldAutocompleteInner;

View file

@ -189,8 +189,6 @@ historySendPadding: 9px;
historySendRight: 2px; historySendRight: 2px;
historyComposeButton: FlatButton { historyComposeButton: FlatButton {
duration: 200;
color: windowActiveTextFg; color: windowActiveTextFg;
overColor: windowActiveTextFg; overColor: windowActiveTextFg;
@ -214,12 +212,14 @@ historyUnblock: FlatButton(historyComposeButton) {
overColor: #d15948; overColor: #d15948;
} }
historySendIcon: icon {{ "send_control_send", historySendIconFg }};
historySendIconOver: icon {{ "send_control_send", historySendIconFgOver }};
historySend: IconButton { historySend: IconButton {
width: 46px; width: 46px;
height: 46px; height: 46px;
icon: icon {{ "send_control_send", historySendIconFg }}; icon: historySendIcon;
iconOver: icon {{ "send_control_send", historySendIconFgOver }}; iconOver: historySendIconOver;
iconPosition: point(11px, 11px); iconPosition: point(11px, 11px);
} }
historyEditSaveIcon: icon {{ "send_control_save", historySendIconFg, point(3px, 7px) }}; historyEditSaveIcon: icon {{ "send_control_save", historySendIconFg, point(3px, 7px) }};
@ -265,6 +265,7 @@ historyBotCommandStart: IconButton(historyAttach) {
historyRecordVoiceFg: historyComposeIconFg; historyRecordVoiceFg: historyComposeIconFg;
historyRecordVoiceFgOver: historyComposeIconFgOver; historyRecordVoiceFgOver: historyComposeIconFgOver;
historyRecordVoiceFgActive: windowBgActive; historyRecordVoiceFgActive: windowBgActive;
historyRecordVoiceDuration: 200;
historyRecordVoice: icon {{ "send_control_record", historyRecordVoiceFg }}; historyRecordVoice: icon {{ "send_control_record", historyRecordVoiceFg }};
historyRecordVoiceOver: icon {{ "send_control_record", historyRecordVoiceFgOver }}; historyRecordVoiceOver: icon {{ "send_control_record", historyRecordVoiceFgOver }};
historyRecordVoiceActive: icon {{ "send_control_record", historyRecordVoiceFgActive }}; historyRecordVoiceActive: icon {{ "send_control_record", historyRecordVoiceFgActive }};
@ -315,8 +316,6 @@ historyInlineBotCancel: IconButton(historyReplyCancel) {
} }
reportSpamHide: FlatButton { reportSpamHide: FlatButton {
duration: 200;
color: windowActiveTextFg; color: windowActiveTextFg;
overColor: windowActiveTextFg; overColor: windowActiveTextFg;

View file

@ -38,9 +38,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
DragArea::DragArea(QWidget *parent) : TWidget(parent) DragArea::DragArea(QWidget *parent) : TWidget(parent)
, _hiding(false) , _hiding(false)
, _in(false) , _in(false)
, a_opacity(0)
, a_colorDrop(0)
, _a_appearance(animation(this, &DragArea::step_appearance))
, _shadow(st::boxShadow) { , _shadow(st::boxShadow) {
setMouseTracking(true); setMouseTracking(true);
setAcceptDrops(true); setAcceptDrops(true);
@ -49,28 +46,24 @@ DragArea::DragArea(QWidget *parent) : TWidget(parent)
void DragArea::mouseMoveEvent(QMouseEvent *e) { void DragArea::mouseMoveEvent(QMouseEvent *e) {
if (_hiding) return; if (_hiding) return;
bool newIn = QRect(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()).contains(e->pos()); auto in = QRect(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()).contains(e->pos());
if (newIn != _in) { setIn(in);
_in = newIn;
a_opacity.start(1);
a_colorDrop.start(_in ? 1. : 0.);
_a_appearance.start();
}
} }
void DragArea::dragMoveEvent(QDragMoveEvent *e) { void DragArea::dragMoveEvent(QDragMoveEvent *e) {
QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()); QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom());
bool newIn = r.contains(e->pos()); setIn(r.contains(e->pos()));
if (newIn != _in) {
_in = newIn;
a_opacity.start(1);
a_colorDrop.start(_in ? 1. : 0.);
_a_appearance.start();
}
e->setDropAction(_in ? Qt::CopyAction : Qt::IgnoreAction); e->setDropAction(_in ? Qt::CopyAction : Qt::IgnoreAction);
e->accept(); e->accept();
} }
void DragArea::setIn(bool in) {
if (_in != in) {
_in = in;
_a_in.start([this] { update(); }, _in ? 0. : 1., _in ? 1. : 0., st::defaultDropdownDuration);
}
}
void DragArea::setText(const QString &text, const QString &subtext) { void DragArea::setText(const QString &text, const QString &subtext) {
_text = text; _text = text;
_subtext = subtext; _subtext = subtext;
@ -80,9 +73,12 @@ void DragArea::setText(const QString &text, const QString &subtext) {
void DragArea::paintEvent(QPaintEvent *e) { void DragArea::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
if (_a_appearance.animating()) { auto ms = getms();
p.setOpacity(a_opacity.current()); auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.);
if (!_a_opacity.animating() && _hiding) {
return;
} }
p.setOpacity(opacity);
QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()); QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom());
@ -91,7 +87,7 @@ void DragArea::paintEvent(QPaintEvent *e) {
p.fillRect(r, st::dragBg); p.fillRect(r, st::dragBg);
p.setPen(anim::pen(st::dragColor, st::dragDropColor, a_colorDrop.current())); p.setPen(anim::pen(st::dragColor, st::dragDropColor, _a_in.current(ms, _in ? 1. : 0.)));
p.setFont(st::dragFont); p.setFont(st::dragFont);
p.drawText(QRect(0, (height() - st::dragHeight) / 2, width(), st::dragFont->height), _text, QTextOption(style::al_top)); p.drawText(QRect(0, (height() - st::dragHeight) / 2, width(), st::dragFont->height), _text, QTextOption(style::al_top));
@ -108,10 +104,7 @@ void DragArea::dragEnterEvent(QDragEnterEvent *e) {
void DragArea::dragLeaveEvent(QDragLeaveEvent *e) { void DragArea::dragLeaveEvent(QDragLeaveEvent *e) {
static_cast<HistoryWidget*>(parentWidget())->dragLeaveEvent(e); static_cast<HistoryWidget*>(parentWidget())->dragLeaveEvent(e);
_in = false; setIn(false);
a_opacity.start(_hiding ? 0 : 1);
a_colorDrop.start(_in ? 1. : 0.);
_a_appearance.start();
} }
void DragArea::dropEvent(QDropEvent *e) { void DragArea::dropEvent(QDropEvent *e) {
@ -130,47 +123,33 @@ void DragArea::otherLeave() {
} }
void DragArea::hideFast() { void DragArea::hideFast() {
if (_a_appearance.animating()) { _a_opacity.finish();
_a_appearance.stop();
}
a_opacity = anim::value();
hide(); hide();
} }
void DragArea::hideStart() { void DragArea::hideStart() {
_hiding = true; _hiding = true;
_in = false; setIn(false);
a_opacity.start(0.); _a_opacity.start([this] { opacityAnimationCallback(); }, 1., 0., st::defaultDropdownDuration);
a_colorDrop.start(_in ? 1. : 0.);
_a_appearance.start();
} }
void DragArea::hideFinish() { void DragArea::hideFinish() {
hide(); hide();
_in = false; _in = false;
a_colorDrop = anim::value(); _a_in.finish();
} }
void DragArea::showStart() { void DragArea::showStart() {
_hiding = false; _hiding = false;
show(); show();
a_opacity.start(1); _a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::defaultDropdownDuration);
a_colorDrop.start(_in ? 1. : 0.);
_a_appearance.start();
} }
void DragArea::step_appearance(float64 ms, bool timer) { void DragArea::opacityAnimationCallback() {
float64 dt = ms / st::defaultDropdownDuration; update();
if (dt >= 1) { if (!_a_opacity.animating()) {
a_opacity.finish();
a_colorDrop.finish();
if (_hiding) { if (_hiding) {
hideFinish(); hideFinish();
} }
_a_appearance.stop();
} else {
a_opacity.update(dt, anim::linear);
a_colorDrop.update(dt, anim::linear);
} }
if (timer) update();
} }

View file

@ -34,10 +34,8 @@ public:
void otherEnter(); void otherEnter();
void otherLeave(); void otherLeave();
void step_appearance(float64 ms, bool timer);
bool overlaps(const QRect &globalRect) { bool overlaps(const QRect &globalRect) {
if (isHidden() || _a_appearance.animating()) return false; if (isHidden() || _a_opacity.animating()) return false;
return QRect(st::dragPadding.left(), return QRect(st::dragPadding.left(),
st::dragPadding.top(), st::dragPadding.top(),
@ -67,12 +65,15 @@ public slots:
void showStart(); void showStart();
private: private:
bool _hiding, _in; void setIn(bool in);
void opacityAnimationCallback();
bool _hiding = false;
bool _in = false;
base::lambda<void(const QMimeData *data)> _droppedCallback; base::lambda<void(const QMimeData *data)> _droppedCallback;
anim::value a_opacity; Animation _a_opacity;
anim::value a_colorDrop; Animation _a_in;
Animation _a_appearance;
Ui::RectShadow _shadow; Ui::RectShadow _shadow;

View file

@ -372,7 +372,7 @@ private:
ButtonRows _rows; ButtonRows _rows;
Animations _animations; Animations _animations;
Animation _a_selected; BasicAnimation _a_selected;
StylePtr _st; StylePtr _st;

View file

@ -155,15 +155,18 @@ void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool
if (p == _savel || p == _cancell) { if (p == _savel || p == _cancell) {
if (active && !dataLoaded()) { if (active && !dataLoaded()) {
ensureAnimation(); ensureAnimation();
_animation->a_thumbOver.start(1); _animation->a_thumbOver.start([this] { thumbAnimationCallback(); }, 0., 1., st::msgFileOverDuration);
_animation->_a_thumbOver.start();
} else if (!active && _animation) { } else if (!active && _animation) {
_animation->a_thumbOver.start(0); _animation->a_thumbOver.start([this] { thumbAnimationCallback(); }, 1., 0., st::msgFileOverDuration);
_animation->_a_thumbOver.start();
} }
} }
} }
void HistoryFileMedia::thumbAnimationCallback() {
Ui::repaintHistoryItem(_parent);
checkAnimationFinished();
}
void HistoryFileMedia::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { void HistoryFileMedia::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
Ui::repaintHistoryItem(_parent); Ui::repaintHistoryItem(_parent);
} }
@ -189,20 +192,6 @@ void HistoryFileMedia::setStatusSize(int32 newSize, int32 fullSize, int32 durati
} }
} }
void HistoryFileMedia::step_thumbOver(float64 ms, bool timer) {
float64 dt = ms / st::msgFileOverDuration;
if (dt >= 1) {
_animation->a_thumbOver.finish();
_animation->_a_thumbOver.stop();
checkAnimationFinished();
} else if (!timer) {
_animation->a_thumbOver.update(dt, anim::linear);
}
if (timer) {
Ui::repaintHistoryItem(_parent);
}
}
void HistoryFileMedia::step_radial(TimeMs ms, bool timer) { void HistoryFileMedia::step_radial(TimeMs ms, bool timer) {
if (timer) { if (timer) {
Ui::repaintHistoryItem(_parent); Ui::repaintHistoryItem(_parent);
@ -216,24 +205,19 @@ void HistoryFileMedia::step_radial(TimeMs ms, bool timer) {
void HistoryFileMedia::ensureAnimation() const { void HistoryFileMedia::ensureAnimation() const {
if (!_animation) { if (!_animation) {
_animation = new AnimationData( _animation = std_::make_unique<AnimationData>(animation(const_cast<HistoryFileMedia*>(this), &HistoryFileMedia::step_radial));
animation(const_cast<HistoryFileMedia*>(this), &HistoryFileMedia::step_thumbOver),
animation(const_cast<HistoryFileMedia*>(this), &HistoryFileMedia::step_radial));
} }
} }
void HistoryFileMedia::checkAnimationFinished() { void HistoryFileMedia::checkAnimationFinished() {
if (_animation && !_animation->_a_thumbOver.animating() && !_animation->radial.animating()) { if (_animation && !_animation->a_thumbOver.animating() && !_animation->radial.animating()) {
if (dataLoaded()) { if (dataLoaded()) {
delete _animation; _animation.reset();
_animation = 0;
} }
} }
} }
HistoryFileMedia::~HistoryFileMedia() { HistoryFileMedia::~HistoryFileMedia() = default;
delete base::take(_animation);
}
HistoryPhoto::HistoryPhoto(HistoryItem *parent, PhotoData *photo, const QString &caption) : HistoryFileMedia(parent) HistoryPhoto::HistoryPhoto(HistoryItem *parent, PhotoData *photo, const QString &caption) : HistoryFileMedia(parent)
, _data(photo) , _data(photo)

View file

@ -71,8 +71,8 @@ protected:
// duration = -1 - no duration, duration = -2 - "GIF" duration // duration = -1 - no duration, duration = -2 - "GIF" duration
void setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const; void setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const;
void step_thumbOver(float64 ms, bool timer);
void step_radial(TimeMs ms, bool timer); void step_radial(TimeMs ms, bool timer);
void thumbAnimationCallback();
void ensureAnimation() const; void ensureAnimation() const;
void checkAnimationFinished(); void checkAnimationFinished();
@ -84,10 +84,10 @@ protected:
return _animation && _animation->radial.animating(); return _animation && _animation->radial.animating();
} }
bool isThumbAnimation(TimeMs ms) const { bool isThumbAnimation(TimeMs ms) const {
if (!_animation || !_animation->_a_thumbOver.animating()) return false; if (_animation) {
return _animation->a_thumbOver.animating(ms);
_animation->_a_thumbOver.step(ms); }
return _animation && _animation->_a_thumbOver.animating(); return false;
} }
virtual float64 dataProgress() const = 0; virtual float64 dataProgress() const = 0;
@ -95,16 +95,13 @@ protected:
virtual bool dataLoaded() const = 0; virtual bool dataLoaded() const = 0;
struct AnimationData { struct AnimationData {
AnimationData(AnimationCallbacks &&thumbOverCallbacks, AnimationCallbacks &&radialCallbacks) : a_thumbOver(0, 0) AnimationData(AnimationCallbacks &&radialCallbacks)
, _a_thumbOver(std_::move(thumbOverCallbacks)) : radial(std_::move(radialCallbacks)) {
, radial(std_::move(radialCallbacks)) {
} }
anim::value a_thumbOver; Animation a_thumbOver;
Animation _a_thumbOver;
Ui::RadialAnimation radial; Ui::RadialAnimation radial;
}; };
mutable AnimationData *_animation = nullptr; mutable std_::unique_ptr<AnimationData> _animation;
}; };
@ -307,7 +304,7 @@ struct HistoryDocumentVoicePlayback {
int32 _position; int32 _position;
anim::value a_progress; anim::value a_progress;
Animation _a_progress; BasicAnimation _a_progress;
}; };
struct HistoryDocumentVoice : public RuntimeComponent<HistoryDocumentVoice> { struct HistoryDocumentVoice : public RuntimeComponent<HistoryDocumentVoice> {
HistoryDocumentVoice &operator=(HistoryDocumentVoice &&other) { HistoryDocumentVoice &operator=(HistoryDocumentVoice &&other) {

View file

@ -2676,8 +2676,6 @@ HistoryHider::HistoryHider(MainWidget *parent, bool forwardSelected) : TWidget(p
, _forwardSelected(forwardSelected) , _forwardSelected(forwardSelected)
, _send(this, lang(lng_forward_send), st::defaultBoxButton) , _send(this, lang(lng_forward_send), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, a_opacity(0, 1)
, _a_appearance(animation(this, &HistoryHider::step_appearance))
, _shadow(st::boxShadow) { , _shadow(st::boxShadow) {
init(); init();
} }
@ -2686,8 +2684,6 @@ HistoryHider::HistoryHider(MainWidget *parent, UserData *sharedContact) : TWidge
, _sharedContact(sharedContact) , _sharedContact(sharedContact)
, _send(this, lang(lng_forward_send), st::defaultBoxButton) , _send(this, lang(lng_forward_send), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, a_opacity(0, 1)
, _a_appearance(animation(this, &HistoryHider::step_appearance))
, _shadow(st::boxShadow) { , _shadow(st::boxShadow) {
init(); init();
} }
@ -2696,8 +2692,6 @@ HistoryHider::HistoryHider(MainWidget *parent) : TWidget(parent)
, _sendPath(true) , _sendPath(true)
, _send(this, lang(lng_forward_send), st::defaultBoxButton) , _send(this, lang(lng_forward_send), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, a_opacity(0, 1)
, _a_appearance(animation(this, &HistoryHider::step_appearance))
, _shadow(st::boxShadow) { , _shadow(st::boxShadow) {
init(); init();
} }
@ -2706,8 +2700,6 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &botAndQuery) : TWi
, _botAndQuery(botAndQuery) , _botAndQuery(botAndQuery)
, _send(this, lang(lng_forward_send), st::defaultBoxButton) , _send(this, lang(lng_forward_send), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, a_opacity(0, 1)
, _a_appearance(animation(this, &HistoryHider::step_appearance))
, _shadow(st::boxShadow) { , _shadow(st::boxShadow) {
init(); init();
} }
@ -2717,8 +2709,6 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString
, _shareText(text) , _shareText(text)
, _send(this, lang(lng_forward_send), st::defaultBoxButton) , _send(this, lang(lng_forward_send), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, a_opacity(0, 1)
, _a_appearance(animation(this, &HistoryHider::step_appearance))
, _shadow(st::boxShadow) { , _shadow(st::boxShadow) {
init(); init();
} }
@ -2731,21 +2721,7 @@ void HistoryHider::init() {
_chooseWidth = st::forwardFont->width(lang(_botAndQuery.isEmpty() ? lng_forward_choose : lng_inline_switch_choose)); _chooseWidth = st::forwardFont->width(lang(_botAndQuery.isEmpty() ? lng_forward_choose : lng_inline_switch_choose));
resizeEvent(0); resizeEvent(0);
_a_appearance.start(); _a_opacity.start([this] { update(); }, 0., 1., st::boxDuration);
}
void HistoryHider::step_appearance(float64 ms, bool timer) {
float64 dt = ms / 200;
if (dt >= 1) {
_a_appearance.stop();
a_opacity.finish();
if (_hiding) {
QTimer::singleShot(0, this, SLOT(deleteLater()));
}
} else {
a_opacity.update(dt, anim::linear);
}
if (timer) update();
} }
bool HistoryHider::withConfirm() const { bool HistoryHider::withConfirm() const {
@ -2754,7 +2730,15 @@ bool HistoryHider::withConfirm() const {
void HistoryHider::paintEvent(QPaintEvent *e) { void HistoryHider::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
p.setOpacity(a_opacity.current()); auto opacity = _a_opacity.current(getms(), _hiding ? 0. : 1.);
if (opacity == 0.) {
if (_hiding) {
QTimer::singleShot(0, this, SLOT(deleteLater()));
}
return;
}
p.setOpacity(opacity);
if (!_hiding || !_cacheForAnim.isNull() || !_offered) { if (!_hiding || !_cacheForAnim.isNull() || !_offered) {
p.fillRect(rect(), st::layerBg); p.fillRect(rect(), st::layerBg);
} }
@ -2813,10 +2797,16 @@ void HistoryHider::startHide() {
} else { } else {
if (_offered) _cacheForAnim = myGrab(this, _box); if (_offered) _cacheForAnim = myGrab(this, _box);
if (_forwardRequest) MTP::cancel(_forwardRequest); if (_forwardRequest) MTP::cancel(_forwardRequest);
a_opacity.start(0);
_send->hide(); _send->hide();
_cancel->hide(); _cancel->hide();
_a_appearance.start(); _a_opacity.start([this] { animationCallback(); }, 1., 0., st::boxDuration);
}
}
void HistoryHider::animationCallback() {
update();
if (!_a_opacity.animating() && _hiding) {
QTimer::singleShot(0, this, SLOT(deleteLater()));
} }
} }
@ -3037,7 +3027,7 @@ TextWithTags::Tags textTagsFromEntities(const EntitiesInText &entities) {
HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
, _fieldBarCancel(this, st::historyReplyCancel) , _fieldBarCancel(this, st::historyReplyCancel)
, _scroll(this, st::historyScroll, false) , _scroll(this, st::historyScroll, false)
, _historyToEnd(this, st::historyToDown) , _historyDown(_scroll, st::historyToDown)
, _fieldAutocomplete(this) , _fieldAutocomplete(this)
, _reportSpamPanel(this) , _reportSpamPanel(this)
, _send(this, st::historySend) , _send(this, st::historySend)
@ -3052,8 +3042,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
, _botCommandStart(this, st::historyBotCommandStart) , _botCommandStart(this, st::historyBotCommandStart)
, _silent(this) , _silent(this)
, _field(this, st::historyComposeField, lang(lng_message_ph)) , _field(this, st::historyComposeField, lang(lng_message_ph))
, _a_recording(animation(this, &HistoryWidget::step_recording))
, _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel))) , _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel)))
, _a_recording(animation(this, &HistoryWidget::step_recording))
, _kbScroll(this, st::botKbScroll) , _kbScroll(this, st::botKbScroll)
, _keyboard(this) , _keyboard(this)
, _emojiPan(this) , _emojiPan(this)
@ -3068,7 +3058,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
connect(_reportSpamPanel, SIGNAL(reportClicked()), this, SLOT(onReportSpamClicked())); connect(_reportSpamPanel, SIGNAL(reportClicked()), this, SLOT(onReportSpamClicked()));
connect(_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide())); connect(_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide()));
connect(_reportSpamPanel, SIGNAL(clearClicked()), this, SLOT(onReportSpamClear())); connect(_reportSpamPanel, SIGNAL(clearClicked()), this, SLOT(onReportSpamClear()));
connect(_historyToEnd, SIGNAL(clicked()), this, SLOT(onHistoryToEnd())); connect(_historyDown, SIGNAL(clicked()), this, SLOT(onHistoryToEnd()));
connect(_fieldBarCancel, SIGNAL(clicked()), this, SLOT(onFieldBarCancel())); connect(_fieldBarCancel, SIGNAL(clicked()), this, SLOT(onFieldBarCancel()));
connect(_send, SIGNAL(clicked()), this, SLOT(onSend())); connect(_send, SIGNAL(clicked()), this, SLOT(onSend()));
connect(_unblock, SIGNAL(clicked()), this, SLOT(onUnblock())); connect(_unblock, SIGNAL(clicked()), this, SLOT(onUnblock()));
@ -3138,7 +3128,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
updateScrollColors(); updateScrollColors();
_historyToEnd->installEventFilter(this); _historyDown->installEventFilter(this);
_fieldAutocomplete->hide(); _fieldAutocomplete->hide();
connect(_fieldAutocomplete, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SLOT(onMentionInsert(UserData*))); connect(_fieldAutocomplete, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SLOT(onMentionInsert(UserData*)));
@ -4346,7 +4336,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
if (wasHistory) _peer->asUser()->botInfo->inlineReturnPeerId = wasHistory->peer->id; if (wasHistory) _peer->asUser()->botInfo->inlineReturnPeerId = wasHistory->peer->id;
onBotStart(); onBotStart();
} }
unreadCountChanged(_history); // set _historyToEnd badge. unreadCountChanged(_history); // set _historyDown badge.
} else { } else {
clearFieldText(); clearFieldText();
doneShow(); doneShow();
@ -4523,7 +4513,7 @@ void HistoryWidget::updateControlsVisibility() {
if (!_a_show.animating()) { if (!_a_show.animating()) {
_topShadow->setVisible(_peer ? true : false); _topShadow->setVisible(_peer ? true : false);
} }
updateToEndVisibility(); updateHistoryDownVisibility();
if (!_history || _a_show.animating()) { if (!_history || _a_show.animating()) {
_reportSpamPanel->hide(); _reportSpamPanel->hide();
_scroll->hide(); _scroll->hide();
@ -4540,7 +4530,7 @@ void HistoryWidget::updateControlsVisibility() {
_attachToggle->hide(); _attachToggle->hide();
_attachEmoji->hide(); _attachEmoji->hide();
_silent->hide(); _silent->hide();
_historyToEnd->hide(); _historyDown->hide();
_botKeyboardShow->hide(); _botKeyboardShow->hide();
_botKeyboardHide->hide(); _botKeyboardHide->hide();
_botCommandStart->hide(); _botCommandStart->hide();
@ -4781,9 +4771,9 @@ void HistoryWidget::historyWasRead(ReadServerHistoryChecks checks) {
void HistoryWidget::unreadCountChanged(History *history) { void HistoryWidget::unreadCountChanged(History *history) {
if (history == _history || history == _migrated) { if (history == _history || history == _migrated) {
updateToEndVisibility(); updateHistoryDownVisibility();
if (_historyToEnd) { if (_historyDown) {
_historyToEnd->setUnreadCount(_history->unreadCount() + (_migrated ? _migrated->unreadCount() : 0)); _historyDown->setUnreadCount(_history->unreadCount() + (_migrated ? _migrated->unreadCount() : 0));
} }
} }
} }
@ -5118,7 +5108,7 @@ void HistoryWidget::visibleAreaUpdated() {
void HistoryWidget::preloadHistoryIfNeeded() { void HistoryWidget::preloadHistoryIfNeeded() {
if (_firstLoadRequest || _scroll->isHidden() || !_peer) return; if (_firstLoadRequest || _scroll->isHidden() || !_peer) return;
updateToEndVisibility(); updateHistoryDownVisibility();
int st = _scroll->scrollTop(), stm = _scroll->scrollTopMax(), sh = _scroll->height(); int st = _scroll->scrollTop(), stm = _scroll->scrollTopMax(), sh = _scroll->height();
if (st + PreloadHeightsCount * sh > stm) { if (st + PreloadHeightsCount * sh > stm) {
@ -5456,7 +5446,7 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window:
_cacheUnder = params.oldContentCache; _cacheUnder = params.oldContentCache;
show(); show();
_topShadow->setVisible(params.withTopBarShadow ? false : true); _topShadow->setVisible(params.withTopBarShadow ? false : true);
_historyToEnd->finishAnimation(); historyDownAnimationFinish();
_cacheOver = App::main()->grabForShowAnimation(params); _cacheOver = App::main()->grabForShowAnimation(params);
App::main()->topBar()->startAnim(); App::main()->topBar()->startAnim();
_topShadow->setVisible(params.withTopBarShadow ? true : false); _topShadow->setVisible(params.withTopBarShadow ? true : false);
@ -5464,7 +5454,7 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window:
_scroll->hide(); _scroll->hide();
_kbScroll->hide(); _kbScroll->hide();
_reportSpamPanel->hide(); _reportSpamPanel->hide();
_historyToEnd->hide(); _historyDown->hide();
_attachToggle->hide(); _attachToggle->hide();
_attachEmoji->hide(); _attachEmoji->hide();
_fieldAutocomplete->hide(); _fieldAutocomplete->hide();
@ -5500,7 +5490,7 @@ void HistoryWidget::animationCallback() {
App::main()->topBar()->update(); App::main()->topBar()->update();
if (!_a_show.animating()) { if (!_a_show.animating()) {
_topShadow->setVisible(_peer ? true : false); _topShadow->setVisible(_peer ? true : false);
_historyToEnd->finishAnimation(); historyDownAnimationFinish();
_cacheUnder = _cacheOver = QPixmap(); _cacheUnder = _cacheOver = QPixmap();
App::main()->topBar()->stopAnim(); App::main()->topBar()->stopAnim();
@ -5528,7 +5518,12 @@ void HistoryWidget::finishAnimation() {
if (!_a_show.animating()) return; if (!_a_show.animating()) return;
_a_show.finish(); _a_show.finish();
_topShadow->setVisible(_peer ? true : false); _topShadow->setVisible(_peer ? true : false);
_historyToEnd->finishAnimation(); historyDownAnimationFinish();
}
void HistoryWidget::historyDownAnimationFinish() {
_historyDownShown.finish();
updateHistoryDownPosition();
} }
void HistoryWidget::recordActiveCallback() { void HistoryWidget::recordActiveCallback() {
@ -5634,7 +5629,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) {
} }
if (inField != _inField && _recording) { if (inField != _inField && _recording) {
_inField = inField; _inField = inField;
_a_recordActive.start([this] { recordActiveCallback(); }, _inField ? 0. : 1., _inField ? 1. : 0., st::historyComposeButton.duration); _a_recordActive.start([this] { recordActiveCallback(); }, _inField ? 0. : 1., _inField ? 1. : 0., st::historyRecordVoiceDuration);
} }
_inReplyEdit = inReplyEdit; _inReplyEdit = inReplyEdit;
_inPinnedMsg = inPinnedMsg; _inPinnedMsg = inPinnedMsg;
@ -5680,7 +5675,7 @@ void HistoryWidget::stopRecording(bool send) {
updateField(); updateField();
if (_inField) { if (_inField) {
_a_recordActive.start([this] { recordActiveCallback(); }, 1., 0., st::historyComposeButton.duration); _a_recordActive.start([this] { recordActiveCallback(); }, 1., 0., st::historyRecordVoiceDuration);
} }
if (_recordRipple) { if (_recordRipple) {
@ -5873,7 +5868,7 @@ bool HistoryWidget::insertBotCommand(const QString &cmd, bool specialGif) {
} }
bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) { bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) {
if (obj == _historyToEnd && e->type() == QEvent::Wheel) { if (obj == _historyDown && e->type() == QEvent::Wheel) {
return _scroll->viewportEvent(e); return _scroll->viewportEvent(e);
} }
return TWidget::eventFilter(obj, e); return TWidget::eventFilter(obj, e);
@ -7096,7 +7091,7 @@ void HistoryWidget::updateControlsGeometry() {
updateFieldSize(); updateFieldSize();
_historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll->y() + _scroll->height() - _historyToEnd->height() - st::historyToDownPosition.y()); updateHistoryDownPosition();
_emojiPan->setMaxHeight(height() - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom() - _attachEmoji->height()); _emojiPan->setMaxHeight(height() - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom() - _attachEmoji->height());
if (_membersDropdown) { if (_membersDropdown) {
@ -7196,7 +7191,7 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh
} }
_fieldAutocomplete->setBoundings(_scroll->geometry()); _fieldAutocomplete->setBoundings(_scroll->geometry());
_historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll->y() + _scroll->height() - _historyToEnd->height() - st::historyToDownPosition.y()); _historyDown->moveToRight(st::historyToDownPosition.x(), _scroll->y() + _scroll->height() - _historyDown->height() - st::historyToDownPosition.y());
} }
_list->recountHeight(); _list->recountHeight();
@ -7439,7 +7434,16 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
update(); update();
} }
void HistoryWidget::updateToEndVisibility() { void HistoryWidget::updateHistoryDownPosition() {
auto top = anim::interpolate(0, _historyDown->height() + st::historyToDownPosition.y(), _historyDownShown.current(_historyDownIsShown ? 1. : 0.));
_historyDown->moveToRight(st::historyToDownPosition.x(), _scroll->height() - top);
auto shouldBeHidden = !_historyDownIsShown && !_historyDownShown.animating();
if (shouldBeHidden != _historyDown->isHidden()) {
_historyDown->setVisible(!shouldBeHidden);
}
}
void HistoryWidget::updateHistoryDownVisibility() {
if (_a_show.animating()) return; if (_a_show.animating()) return;
auto haveUnreadBelowBottom = [this](History *history) { auto haveUnreadBelowBottom = [this](History *history) {
@ -7451,7 +7455,7 @@ void HistoryWidget::updateToEndVisibility() {
} }
return (_list->itemTop(history->showFrom) >= _scroll->scrollTop() + _scroll->height()); return (_list->itemTop(history->showFrom) >= _scroll->scrollTop() + _scroll->height());
}; };
auto isToEndVisible = [this, &haveUnreadBelowBottom]() { auto historyDownIsVisible = [this, &haveUnreadBelowBottom]() {
if (!_history || _firstLoadRequest) { if (!_history || _firstLoadRequest) {
return false; return false;
} }
@ -7466,11 +7470,10 @@ void HistoryWidget::updateToEndVisibility() {
} }
return false; return false;
}; };
bool toEndVisible = isToEndVisible(); auto historyDownIsShown = historyDownIsVisible();
if (toEndVisible && _historyToEnd->hidden()) { if (_historyDownIsShown != historyDownIsShown) {
_historyToEnd->showAnimated(); _historyDownIsShown = historyDownIsShown;
} else if (!toEndVisible && !_historyToEnd->hidden()) { _historyDownShown.start([this] { updateHistoryDownPosition(); }, _historyDownIsShown ? 0. : 1., _historyDownIsShown ? 1. : 0., st::historyToDownDuration);
_historyToEnd->hideAnimated();
} }
} }
@ -7487,7 +7490,7 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
updateField(); updateField();
_a_recordActive.start([this] { recordActiveCallback(); }, 0., 1., st::historyComposeButton.duration); _a_recordActive.start([this] { recordActiveCallback(); }, 0., 1., st::historyRecordVoiceDuration);
if (!_recordRipple) { if (!_recordRipple) {
auto mask = Ui::RippleAnimation::ellipseMask(QSize(st::historyAttachEmoji.rippleAreaSize, st::historyAttachEmoji.rippleAreaSize)); auto mask = Ui::RippleAnimation::ellipseMask(QSize(st::historyAttachEmoji.rippleAreaSize, st::historyAttachEmoji.rippleAreaSize));
@ -8797,6 +8800,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
bool hasTopBar = !App::main()->topBar()->isHidden(); bool hasTopBar = !App::main()->topBar()->isHidden();
auto ms = getms(); auto ms = getms();
_historyDownShown.step(ms);
auto progress = _a_show.current(ms, 1.); auto progress = _a_show.current(ms, 1.);
if (_a_show.animating()) { if (_a_show.animating()) {
auto retina = cIntRetinaFactor(); auto retina = cIntRetinaFactor();

View file

@ -274,7 +274,7 @@ private:
int _visibleAreaBottom = 0; int _visibleAreaBottom = 0;
bool _scrollDateShown = false; bool _scrollDateShown = false;
FloatAnimation _scrollDateOpacity; Animation _scrollDateOpacity;
SingleDelayedCall _scrollDateCheck = { this, "onScrollDateCheck" }; SingleDelayedCall _scrollDateCheck = { this, "onScrollDateCheck" };
SingleTimer _scrollDateHideTimer; SingleTimer _scrollDateHideTimer;
HistoryItem *_scrollDateLastItem = nullptr; HistoryItem *_scrollDateLastItem = nullptr;
@ -467,7 +467,6 @@ public:
HistoryHider(MainWidget *parent, const QString &url, const QString &text); // share url HistoryHider(MainWidget *parent, const QString &url, const QString &text); // share url
HistoryHider(MainWidget *parent, const QString &botAndQuery); // inline switch button handler HistoryHider(MainWidget *parent, const QString &botAndQuery); // inline switch button handler
void step_appearance(float64 ms, bool timer);
bool withConfirm() const; bool withConfirm() const;
bool offerPeer(PeerId peer); bool offerPeer(PeerId peer);
@ -496,6 +495,7 @@ signals:
void forwarded(); void forwarded();
private: private:
void animationCallback();
void init(); void init();
MainWidget *parent(); MainWidget *parent();
@ -510,8 +510,7 @@ private:
ChildWidget<Ui::RoundButton> _cancel; ChildWidget<Ui::RoundButton> _cancel;
PeerData *_offered = nullptr; PeerData *_offered = nullptr;
anim::value a_opacity; Animation _a_opacity;
Animation _a_appearance;
QRect _box; QRect _box;
bool _hiding = false; bool _hiding = false;
@ -677,7 +676,8 @@ public:
void applyCloudDraft(History *history); void applyCloudDraft(History *history);
void contactsReceived(); void contactsReceived();
void updateToEndVisibility(); void updateHistoryDownPosition();
void updateHistoryDownVisibility();
void updateAfterDrag(); void updateAfterDrag();
void updateFieldSubmitSettings(); void updateFieldSubmitSettings();
@ -850,6 +850,7 @@ private:
void animationCallback(); void animationCallback();
void recordActiveCallback(); void recordActiveCallback();
void chooseAttach(); void chooseAttach();
void historyDownAnimationFinish();
void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update); void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update);
struct SendingFilesLists { struct SendingFilesLists {
QList<QUrl> nonLocalUrls; QList<QUrl> nonLocalUrls;
@ -1088,7 +1089,9 @@ private:
TimeMs _lastScrolled = 0; TimeMs _lastScrolled = 0;
QTimer _updateHistoryItems; QTimer _updateHistoryItems;
ChildWidget<Ui::HistoryDownButton> _historyToEnd; Animation _historyDownShown;
bool _historyDownIsShown = false;
ChildWidget<Ui::HistoryDownButton> _historyDown;
ChildWidget<FieldAutocomplete> _fieldAutocomplete; ChildWidget<FieldAutocomplete> _fieldAutocomplete;
@ -1122,19 +1125,22 @@ private:
ChildWidget<SilentToggle> _silent; ChildWidget<SilentToggle> _silent;
bool _cmdStartShown = false; bool _cmdStartShown = false;
ChildWidget<MessageField> _field; ChildWidget<MessageField> _field;
Animation _a_recording;
bool _recording = false; bool _recording = false;
bool _inRecord = false; bool _inRecord = false;
bool _inField = false; bool _inField = false;
bool _inReplyEdit = false; bool _inReplyEdit = false;
bool _inPinnedMsg = false; bool _inPinnedMsg = false;
bool _inClickable = false; bool _inClickable = false;
anim::value a_recordingLevel;
int _recordingSamples = 0; int _recordingSamples = 0;
FloatAnimation _a_recordActive; Animation _a_recordActive;
std_::unique_ptr<Ui::RippleAnimation> _recordRipple; std_::unique_ptr<Ui::RippleAnimation> _recordRipple;
int _recordCancelWidth; int _recordCancelWidth;
// This can animate for a very long time (like in music playing),
// so it should be a BasicAnimation, not an Animation.
BasicAnimation _a_recording;
anim::value a_recordingLevel;
FileDialog::QueryId _attachFilesQueryId = 0; FileDialog::QueryId _attachFilesQueryId = 0;
bool kbWasHidden() const; bool kbWasHidden() const;
@ -1163,7 +1169,7 @@ private:
bool _titlePeerTextOnline = false; bool _titlePeerTextOnline = false;
int _titlePeerTextWidth = 0; int _titlePeerTextWidth = 0;
FloatAnimation _a_show; Animation _a_show;
Window::SlideDirection _showDirection; Window::SlideDirection _showDirection;
QPixmap _cacheUnder, _cacheOver; QPixmap _cacheUnder, _cacheOver;

View file

@ -383,7 +383,7 @@ void Sticker::preload() const {
void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context) const { void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
bool loaded = getShownDocument()->loaded(); bool loaded = getShownDocument()->loaded();
auto over = _a_over.current(_active ? 1. : 0.); auto over = _a_over.current(context->ms, _active ? 1. : 0.);
if (over > 0) { if (over > 0) {
p.setOpacity(over); p.setOpacity(over);
App::roundRect(p, QRect(QPoint(0, 0), st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); App::roundRect(p, QRect(QPoint(0, 0), st::stickerPanSize), st::emojiPanHover, StickerHoverCorners);
@ -780,17 +780,8 @@ void File::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, in
void File::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { void File::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (p == _open || p == _cancel) { if (p == _open || p == _cancel) {
if (active) {
ensureAnimation(); ensureAnimation();
_animation->a_thumbOver.start(1); _animation->a_thumbOver.start([this] { thumbAnimationCallback(); }, active ? 0. : 1., active ? 1. : 0., st::msgFileOverDuration);
} else {
if (!_animation) {
ensureAnimation();
_animation->a_thumbOver = anim::value(1, 1);
}
_animation->a_thumbOver.start(0);
}
_animation->_a_thumbOver.start();
} }
} }
@ -798,18 +789,9 @@ File::~File() {
unregDocumentItem(getShownDocument(), this); unregDocumentItem(getShownDocument(), this);
} }
void File::step_thumbOver(float64 ms, bool timer) { void File::thumbAnimationCallback() {
float64 dt = ms / st::msgFileOverDuration;
if (dt >= 1) {
_animation->a_thumbOver.finish();
_animation->_a_thumbOver.stop();
checkAnimationFinished();
} else if (!timer) {
_animation->a_thumbOver.update(dt, anim::linear);
}
if (timer) {
Ui::repaintInlineItem(this); Ui::repaintInlineItem(this);
} checkAnimationFinished();
} }
void File::step_radial(TimeMs ms, bool timer) { void File::step_radial(TimeMs ms, bool timer) {
@ -826,16 +808,14 @@ void File::step_radial(TimeMs ms, bool timer) {
void File::ensureAnimation() const { void File::ensureAnimation() const {
if (!_animation) { if (!_animation) {
_animation.reset(new AnimationData( _animation.reset(new AnimationData(animation(const_cast<File*>(this), &File::step_radial)));
animation(const_cast<File*>(this), &File::step_thumbOver),
animation(const_cast<File*>(this), &File::step_radial)));
} }
} }
void File::checkAnimationFinished() { void File::checkAnimationFinished() {
if (_animation && !_animation->_a_thumbOver.animating() && !_animation->radial.animating()) { if (_animation && !_animation->a_thumbOver.animating() && !_animation->radial.animating()) {
if (getShownDocument()->loaded()) { if (getShownDocument()->loaded()) {
_animation = nullptr; _animation.reset();
} }
} }
} }

View file

@ -107,11 +107,11 @@ private:
, radial(std_::move(callbacks)) { , radial(std_::move(callbacks)) {
} }
bool over; bool over;
FloatAnimation _a_over; Animation _a_over;
Ui::RadialAnimation radial; Ui::RadialAnimation radial;
}; };
mutable std_::unique_ptr<AnimationData> _animation; mutable std_::unique_ptr<AnimationData> _animation;
mutable FloatAnimation _a_deleteOver; mutable Animation _a_deleteOver;
}; };
@ -170,7 +170,7 @@ private:
QSize getThumbSize() const; QSize getThumbSize() const;
mutable FloatAnimation _a_over; mutable Animation _a_over;
mutable bool _active = false; mutable bool _active = false;
mutable QPixmap _thumb; mutable QPixmap _thumb;
@ -242,7 +242,7 @@ public:
~File(); ~File();
private: private:
void step_thumbOver(float64 ms, bool timer); void thumbAnimationCallback();
void step_radial(TimeMs ms, bool timer); void step_radial(TimeMs ms, bool timer);
void ensureAnimation() const; void ensureAnimation() const;
@ -256,20 +256,16 @@ private:
return _animation && _animation->radial.animating(); return _animation && _animation->radial.animating();
} }
bool isThumbAnimation(TimeMs ms) const { bool isThumbAnimation(TimeMs ms) const {
if (!_animation || !_animation->_a_thumbOver.animating()) return false; if (_animation) {
return _animation->a_thumbOver.animating(ms);
_animation->_a_thumbOver.step(ms); }
return _animation && _animation->_a_thumbOver.animating(); return false;
} }
struct AnimationData { struct AnimationData {
AnimationData(AnimationCallbacks &&thumbOverCallbacks, AnimationCallbacks &&radialCallbacks) : a_thumbOver(0, 0) AnimationData(AnimationCallbacks &&radialCallbacks) : radial(std_::move(radialCallbacks)) {
, _a_thumbOver(std_::move(thumbOverCallbacks))
, radial(std_::move(radialCallbacks)) {
} }
anim::value a_thumbOver; Animation a_thumbOver;
Animation _a_thumbOver;
Ui::RadialAnimation radial; Ui::RadialAnimation radial;
}; };
mutable std_::unique_ptr<AnimationData> _animation; mutable std_::unique_ptr<AnimationData> _animation;

View file

@ -198,7 +198,7 @@ public:
QString _errorText; QString _errorText;
ChildWidget<Ui::WidgetFadeWrap<Ui::FlatLabel>> _error = { nullptr }; ChildWidget<Ui::WidgetFadeWrap<Ui::FlatLabel>> _error = { nullptr };
FloatAnimation _a_show; Animation _a_show;
CoverAnimation _coverAnimation; CoverAnimation _coverAnimation;
std_::unique_ptr<Ui::SlideAnimation> _slideAnimation; std_::unique_ptr<Ui::SlideAnimation> _slideAnimation;
QPixmap _coverMask; QPixmap _coverMask;
@ -226,7 +226,7 @@ private:
void resetDone(const MTPBool &result); void resetDone(const MTPBool &result);
bool resetFail(const RPCError &error); bool resetFail(const RPCError &error);
FloatAnimation _a_show; Animation _a_show;
bool _showBack = false; bool _showBack = false;
QPixmap _cacheUnder, _cacheOver; QPixmap _cacheUnder, _cacheOver;
@ -243,7 +243,7 @@ private:
Data _data; Data _data;
FloatAnimation _coverShownAnimation; Animation _coverShownAnimation;
int _nextTopFrom = 0; int _nextTopFrom = 0;
int _controlsTopFrom = 0; int _controlsTopFrom = 0;

View file

@ -79,10 +79,10 @@ private:
bool _wasAnimating = false; bool _wasAnimating = false;
bool _inPaintEvent = false; bool _inPaintEvent = false;
FloatAnimation _a_shown; Animation _a_shown;
FloatAnimation _a_mainMenuShown; Animation _a_mainMenuShown;
FloatAnimation _a_specialLayerShown; Animation _a_specialLayerShown;
FloatAnimation _a_layerShown; Animation _a_layerShown;
Ui::RectShadow _shadow; Ui::RectShadow _shadow;

View file

@ -157,7 +157,7 @@ private:
void fillEmojiString(); void fillEmojiString();
void resetGifAndCache(); void resetGifAndCache();
FloatAnimation _a_shown; Animation _a_shown;
bool _hiding = false; bool _hiding = false;
DocumentData *_document = nullptr; DocumentData *_document = nullptr;
PhotoData *_photo = nullptr; PhotoData *_photo = nullptr;

View file

@ -3139,7 +3139,7 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
h->inboxReadBefore = d.vread_inbox_max_id.v + 1; h->inboxReadBefore = d.vread_inbox_max_id.v + 1;
} }
if (_history->peer() == channel) { if (_history->peer() == channel) {
_history->updateToEndVisibility(); _history->updateHistoryDownVisibility();
_history->preloadHistoryIfNeeded(); _history->preloadHistoryIfNeeded();
} }
h->asChannelHistory()->getRangeDifference(); h->asChannelHistory()->getRangeDifference();

View file

@ -586,7 +586,7 @@ private:
base::Observable<PeerData*> _searchInPeerChanged; base::Observable<PeerData*> _searchInPeerChanged;
base::Observable<PeerData*> _historyPeerChanged; base::Observable<PeerData*> _historyPeerChanged;
FloatAnimation _a_show; Animation _a_show;
bool _showBack = false; bool _showBack = false;
QPixmap _cacheUnder, _cacheOver; QPixmap _cacheUnder, _cacheOver;

View file

@ -45,7 +45,7 @@ QPainterPath interpolatePaths(QPointF (&from)[N], QPointF (&to)[N], float64 k) {
} // namespace } // namespace
PlayButtonLayout::PlayButtonLayout(const style::MediaPlayerButton &st, UpdateCallback &&callback) PlayButtonLayout::PlayButtonLayout(const style::MediaPlayerButton &st, base::lambda<void()> &&callback)
: _st(st) : _st(st)
, _callback(std_::move(callback)) { , _callback(std_::move(callback)) {
} }

View file

@ -33,8 +33,7 @@ public:
Pause, Pause,
Cancel, Cancel,
}; };
using UpdateCallback = FloatAnimation::Callback; PlayButtonLayout(const style::MediaPlayerButton &st, base::lambda<void()> &&callback);
PlayButtonLayout(const style::MediaPlayerButton &st, UpdateCallback &&callback);
void setState(State state); void setState(State state);
void finishTransform(); void finishTransform();
@ -54,10 +53,10 @@ private:
State _state = State::Play; State _state = State::Play;
State _oldState = State::Play; State _oldState = State::Play;
State _nextState = State::Play; State _nextState = State::Play;
FloatAnimation _transformProgress; Animation _transformProgress;
bool _transformBackward = false; bool _transformBackward = false;
UpdateCallback _callback; base::lambda<void()> _callback;
}; };

View file

@ -100,7 +100,7 @@ private:
bool _hiding = false; bool _hiding = false;
QPixmap _cache; QPixmap _cache;
FloatAnimation _a_appearance; Animation _a_appearance;
bool _ignoringEnterEvents = false; bool _ignoringEnterEvents = false;

View file

@ -82,7 +82,7 @@ private:
bool _hiding = false; bool _hiding = false;
QPixmap _cache; QPixmap _cache;
FloatAnimation _a_appearance; Animation _a_appearance;
QTimer _hideTimer, _showTimer; QTimer _hideTimer, _showTimer;

View file

@ -50,7 +50,7 @@ private:
int _downCoord = -1; // < 0 means mouse is not pressed int _downCoord = -1; // < 0 means mouse is not pressed
bool _over = false; bool _over = false;
FloatAnimation _a_over; Animation _a_over;
}; };

View file

@ -296,7 +296,7 @@ private:
QPoint _lastAction, _lastMouseMovePos; QPoint _lastAction, _lastMouseMovePos;
bool _ignoringDropdown = false; bool _ignoringDropdown = false;
Animation _a_state; BasicAnimation _a_state;
enum ControlsState { enum ControlsState {
ControlsShowing, ControlsShowing,

View file

@ -105,20 +105,30 @@ public:
return false; return false;
} }
// check g_b > 2^{2048 - 8} and get the value of g_b
if (BN_is_negative(&bnResult)) {
LOG(("BigNum Error: bad g_b - negative"));
return false;
}
uint32 resultLen = BN_num_bytes(&bnResult); uint32 resultLen = BN_num_bytes(&bnResult);
if (resultLen != 64 * sizeof(uint32)) { if (resultLen != 64 * sizeof(uint32)) {
DEBUG_LOG(("BigNum Error: bad gResult len (%1)").arg(resultLen)); LOG(("BigNum Error: bad g_b len (%1)").arg(resultLen));
return false; return false;
} }
resultLen = BN_bn2bin(&bnResult, (uchar*)gResult); resultLen = BN_bn2bin(&bnResult, (uchar*)gResult);
if (resultLen != 64 * sizeof(uint32)) { if (resultLen != 64 * sizeof(uint32)) {
DEBUG_LOG(("BigNum Error: bad gResult export len (%1)").arg(resultLen)); LOG(("BigNum Error: bad g_b export len (%1)").arg(resultLen));
return false; return false;
} }
BN_add_word(&bnResult, 1); // check g_b < dh_prime - 1 // check g_b < dh_prime - 2^{2048 - 8}
if (BN_cmp(&bnResult, &bnModul) >= 0) { BN_sub(&bnTemp, &bnModul, &bnResult);
DEBUG_LOG(("BigNum Error: bad g_b >= dh_prime - 1")); if (BN_is_negative(&bnTemp)) {
DEBUG_LOG(("BigNum Error: bad g_b > dh_prime"));
return false;
}
if (BN_num_bytes(&bnTemp) != 64 * sizeof(uint32)) {
DEBUG_LOG(("BigNum Error: bad g_b > dh_prime - 2^{2048 - 8}"));
return false; return false;
} }
@ -140,9 +150,25 @@ public:
return false; return false;
} }
BN_add_word(&bn_g_a, 1); // check g_a < dh_prime - 1 // check g_a > 2^{2048 - 8}
if (BN_cmp(&bn_g_a, &bnModul) >= 0) { if (BN_is_negative(&bn_g_a)) {
DEBUG_LOG(("BigNum Error: bad g_a >= dh_prime - 1")); LOG(("BigNum Error: bad g_a - negative"));
return false;
}
resultLen = BN_num_bytes(&bn_g_a);
if (resultLen != 64 * sizeof(uint32)) {
LOG(("BigNum Error: bad g_a len (%1)").arg(resultLen));
return false;
}
// check g_a < dh_prime - 2^{2048 - 8}
BN_sub(&bnTemp, &bnModul, &bn_g_a);
if (BN_is_negative(&bnTemp)) {
LOG(("BigNum Error: bad g_b > dh_prime"));
return false;
}
if (BN_num_bytes(&bnTemp) != 64 * sizeof(uint32)) {
LOG(("BigNum Error: bad g_b > dh_prime - 2^{2048 - 8}"));
return false; return false;
} }
@ -155,6 +181,7 @@ public:
BN_init(&bn_g); BN_init(&bn_g);
BN_init(&bn_g_a); BN_init(&bn_g_a);
BN_init(&bnResult); BN_init(&bnResult);
BN_init(&bnTemp);
} }
~BigNumCounter() { ~BigNumCounter() {
BN_CTX_free(ctx); BN_CTX_free(ctx);
@ -163,11 +190,13 @@ public:
BN_clear_free(&bn_g); BN_clear_free(&bn_g);
BN_clear_free(&bn_g_a); BN_clear_free(&bn_g_a);
BN_clear_free(&bnResult); BN_clear_free(&bnResult);
BN_clear_free(&bnTemp);
} }
private: private:
BIGNUM bnPower, bnModul, bn_g, bn_g_a, bnResult; BIGNUM bnPower, bnModul, bn_g, bn_g_a, bnResult, bnTemp;
BN_CTX *ctx; BN_CTX *ctx;
}; };
// Miller-Rabin primality test // Miller-Rabin primality test
@ -409,29 +438,12 @@ void ConnectionPrivate::destroyConn(AbstractConnection **conn) {
ConnectionPrivate::ConnectionPrivate(QThread *thread, Connection *owner, SessionData *data, uint32 _dc) : QObject(nullptr) ConnectionPrivate::ConnectionPrivate(QThread *thread, Connection *owner, SessionData *data, uint32 _dc) : QObject(nullptr)
, _state(DisconnectedState) , _state(DisconnectedState)
, _needSessionReset(false)
, dc(_dc) , dc(_dc)
, _owner(owner) , _owner(owner)
, _conn(nullptr)
, _conn4(nullptr)
, _conn6(nullptr)
, retryTimeout(1)
, oldConnection(true)
, _waitForReceived(MTPMinReceiveDelay) , _waitForReceived(MTPMinReceiveDelay)
, _waitForConnected(MTPMinConnectDelay) , _waitForConnected(MTPMinConnectDelay)
, firstSentAt(-1)
, _pingId(0)
, _pingIdToSend(0)
, _pingSendAt(0)
, _pingMsgId(0)
, restarted(false)
, _finished(false)
, keyId(0)
// , sessionDataMutex(QReadWriteLock::Recursive) // , sessionDataMutex(QReadWriteLock::Recursive)
, sessionData(data) , sessionData(data) {
, myKeyLock(false)
, authKeyData(0)
, authKeyStrings(0) {
oldConnectionTimer.moveToThread(thread); oldConnectionTimer.moveToThread(thread);
_waitForConnectedTimer.moveToThread(thread); _waitForConnectedTimer.moveToThread(thread);
_waitForReceivedTimer.moveToThread(thread); _waitForReceivedTimer.moveToThread(thread);
@ -1459,7 +1471,7 @@ void ConnectionPrivate::handleReceived() {
if (needAck) ackRequestData.push_back(MTP_long(msgId)); if (needAck) ackRequestData.push_back(MTP_long(msgId));
int32 res = 1; // if no need to handle, then succeed auto res = HandleResult::Success; // if no need to handle, then succeed
end = data + 8 + (msgLen >> 2); end = data + 8 + (msgLen >> 2);
const mtpPrime *sfrom(data + 4); const mtpPrime *sfrom(data + 4);
MTP_LOG(dc, ("Recv: ") + mtpTextSerialize(sfrom, end)); MTP_LOG(dc, ("Recv: ") + mtpTextSerialize(sfrom, end));
@ -1467,19 +1479,14 @@ void ConnectionPrivate::handleReceived() {
bool needToHandle = false; bool needToHandle = false;
{ {
QWriteLocker lock(sessionData->receivedIdsMutex()); QWriteLocker lock(sessionData->receivedIdsMutex());
mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); needToHandle = sessionData->receivedIdsSet().registerMsgId(msgId, needAck);
needToHandle = receivedIds.insert(msgId, needAck);
} }
if (needToHandle) { if (needToHandle) {
res = handleOneReceived(from, end, msgId, serverTime, serverSalt, badTime); res = handleOneReceived(from, end, msgId, serverTime, serverSalt, badTime);
} }
{ {
QWriteLocker lock(sessionData->receivedIdsMutex()); QWriteLocker lock(sessionData->receivedIdsMutex());
mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); sessionData->receivedIdsSet().shrink();
uint32 receivedIdsSize = receivedIds.size();
while (receivedIdsSize-- > MTPIdsBufferSize) {
receivedIds.erase(receivedIds.begin());
}
} }
// send acks // send acks
@ -1502,8 +1509,8 @@ void ConnectionPrivate::handleReceived() {
emit needToReceive(); emit needToReceive();
} }
if (res < 0) { if (res != HandleResult::Success && res != HandleResult::Ignored) {
_needSessionReset = (res < -1); _needSessionReset = (res == HandleResult::ResetSession);
lockFinished.unlock(); lockFinished.unlock();
return restart(); return restart();
@ -1526,7 +1533,7 @@ void ConnectionPrivate::handleReceived() {
} }
} }
int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime *end, uint64 msgId, int32 serverTime, uint64 serverSalt, bool badTime) { ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime *end, uint64 msgId, int32 serverTime, uint64 serverSalt, bool badTime) {
mtpTypeId cons = *from; mtpTypeId cons = *from;
try { try {
@ -1536,7 +1543,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
DEBUG_LOG(("Message Info: gzip container")); DEBUG_LOG(("Message Info: gzip container"));
mtpBuffer response = ungzip(++from, end); mtpBuffer response = ungzip(++from, end);
if (!response.size()) { if (!response.size()) {
return -1; return HandleResult::RestartConnection;
} }
return handleOneReceived(response.data(), response.data() + response.size(), msgId, serverTime, serverSalt, badTime); return handleOneReceived(response.data(), response.data() + response.size(), msgId, serverTime, serverSalt, badTime);
} }
@ -1555,14 +1562,14 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
bool isReply = ((inMsgId.v & 0x03) == 1); bool isReply = ((inMsgId.v & 0x03) == 1);
if (!isReply && ((inMsgId.v & 0x03) != 3)) { if (!isReply && ((inMsgId.v & 0x03) != 3)) {
LOG(("Message Error: bad msg_id %1 in contained message received").arg(inMsgId.v)); LOG(("Message Error: bad msg_id %1 in contained message received").arg(inMsgId.v));
return -1; return HandleResult::RestartConnection;
} }
MTPint inSeqNo(from, otherEnd); MTPint inSeqNo(from, otherEnd);
MTPint bytes(from, otherEnd); MTPint bytes(from, otherEnd);
if ((bytes.v & 0x03) || bytes.v < 4) { if ((bytes.v & 0x03) || bytes.v < 4) {
LOG(("Message Error: bad length %1 of contained message received").arg(bytes.v)); LOG(("Message Error: bad length %1 of contained message received").arg(bytes.v));
return -1; return HandleResult::RestartConnection;
} }
bool needAck = (inSeqNo.v & 0x01); bool needAck = (inSeqNo.v & 0x01);
@ -1576,21 +1583,20 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
bool needToHandle = false; bool needToHandle = false;
{ {
QWriteLocker lock(sessionData->receivedIdsMutex()); QWriteLocker lock(sessionData->receivedIdsMutex());
mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); needToHandle = sessionData->receivedIdsSet().registerMsgId(inMsgId.v, needAck);
needToHandle = receivedIds.insert(inMsgId.v, needAck);
} }
int32 res = 1; // if no need to handle, then succeed auto res = HandleResult::Success; // if no need to handle, then succeed
if (needToHandle) { if (needToHandle) {
res = handleOneReceived(from, otherEnd, inMsgId.v, serverTime, serverSalt, badTime); res = handleOneReceived(from, otherEnd, inMsgId.v, serverTime, serverSalt, badTime);
badTime = false; badTime = false;
} }
if (res <= 0) { if (res != HandleResult::Success) {
return res; return res;
} }
from = otherEnd; from = otherEnd;
} }
} return 1; } return HandleResult::Success;
case mtpc_msgs_ack: { case mtpc_msgs_ack: {
MTPMsgsAck msg(from, end); MTPMsgsAck msg(from, end);
@ -1598,17 +1604,17 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
uint32 idsCount = ids.size(); uint32 idsCount = ids.size();
DEBUG_LOG(("Message Info: acks received, ids: %1").arg(Logs::vector(ids))); DEBUG_LOG(("Message Info: acks received, ids: %1").arg(Logs::vector(ids)));
if (!idsCount) return (badTime ? 0 : 1); if (!idsCount) return (badTime ? HandleResult::Ignored : HandleResult::Success);
if (badTime) { if (badTime) {
if (requestsFixTimeSalt(ids, serverTime, serverSalt)) { if (requestsFixTimeSalt(ids, serverTime, serverSalt)) {
badTime = false; badTime = false;
} else { } else {
return 0; return HandleResult::Ignored;
} }
} }
requestsAcked(ids); requestsAcked(ids);
} return 1; } return HandleResult::Success;
case mtpc_bad_msg_notification: { case mtpc_bad_msg_notification: {
MTPBadMsgNotification msg(from, end); MTPBadMsgNotification msg(from, end);
@ -1654,7 +1660,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
if (!wasSent(resendId)) { if (!wasSent(resendId)) {
DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(resendId)); DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(resendId));
return (badTime ? 0 : 1); return (badTime ? HandleResult::Ignored : HandleResult::Success);
} }
if (needResend) { // bad msg_id if (needResend) { // bad msg_id
@ -1671,7 +1677,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
badTime = false; badTime = false;
} }
LOG(("Message Info: bad message notification received, msgId %1, error_code %2").arg(data.vbad_msg_id.v).arg(errorCode)); LOG(("Message Info: bad message notification received, msgId %1, error_code %2").arg(data.vbad_msg_id.v).arg(errorCode));
return -2; return HandleResult::ResetSession;
} }
} else { // fatal (except 48, but it must not get here) } else { // fatal (except 48, but it must not get here)
mtpMsgId resendId = data.vbad_msg_id.v; mtpMsgId resendId = data.vbad_msg_id.v;
@ -1682,9 +1688,9 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
} else { } else {
DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(resendId)); DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(resendId));
} }
return (badTime ? 0 : 1); return (badTime ? HandleResult::Ignored : HandleResult::Success);
} }
} return 1; } return HandleResult::Success;
case mtpc_bad_server_salt: { case mtpc_bad_server_salt: {
MTPBadMsgNotification msg(from, end); MTPBadMsgNotification msg(from, end);
@ -1696,7 +1702,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
_pingId = 0; _pingId = 0;
} else if (!wasSent(resendId)) { } else if (!wasSent(resendId)) {
DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(resendId)); DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(resendId));
return (badTime ? 0 : 1); return (badTime ? HandleResult::Ignored : HandleResult::Success);
} }
uint64 serverSalt = data.vnew_server_salt.v; uint64 serverSalt = data.vnew_server_salt.v;
@ -1714,25 +1720,25 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
DEBUG_LOG(("Message Info: unixtime updated, now %1, server_salt updated, now %2, resending...").arg(serverTime).arg(serverSalt)); DEBUG_LOG(("Message Info: unixtime updated, now %1, server_salt updated, now %2, resending...").arg(serverTime).arg(serverSalt));
resend(resendId); resend(resendId);
} return 1; } return HandleResult::Success;
case mtpc_msgs_state_req: { case mtpc_msgs_state_req: {
if (badTime) { if (badTime) {
DEBUG_LOG(("Message Info: skipping with bad time...")); DEBUG_LOG(("Message Info: skipping with bad time..."));
return 0; return HandleResult::Ignored;
} }
MTPMsgsStateReq msg(from, end); MTPMsgsStateReq msg(from, end);
const auto &ids(msg.c_msgs_state_req().vmsg_ids.c_vector().v); const auto &ids(msg.c_msgs_state_req().vmsg_ids.c_vector().v);
uint32 idsCount = ids.size(); uint32 idsCount = ids.size();
DEBUG_LOG(("Message Info: msgs_state_req received, ids: %1").arg(Logs::vector(ids))); DEBUG_LOG(("Message Info: msgs_state_req received, ids: %1").arg(Logs::vector(ids)));
if (!idsCount) return 1; if (!idsCount) return HandleResult::Success;
QByteArray info(idsCount, Qt::Uninitialized); QByteArray info(idsCount, Qt::Uninitialized);
{ {
QReadLocker lock(sessionData->receivedIdsMutex()); QReadLocker lock(sessionData->receivedIdsMutex());
const mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); auto &receivedIds = sessionData->receivedIdsSet();
mtpMsgIdsMap::const_iterator receivedIdsEnd(receivedIds.cend()); auto minRecv = receivedIds.min();
uint64 minRecv = receivedIds.min(), maxRecv = receivedIds.max(); auto maxRecv = receivedIds.max();
QReadLocker locker(sessionData->wereAckedMutex()); QReadLocker locker(sessionData->wereAckedMutex());
const mtpRequestIdsMap &wereAcked(sessionData->wereAckedMap()); const mtpRequestIdsMap &wereAcked(sessionData->wereAckedMap());
@ -1746,15 +1752,15 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
} else if (reqMsgId > maxRecv) { } else if (reqMsgId > maxRecv) {
state |= 0x03; state |= 0x03;
} else { } else {
mtpMsgIdsMap::const_iterator recv = receivedIds.constFind(reqMsgId); auto msgIdState = receivedIds.lookup(reqMsgId);
if (recv == receivedIdsEnd) { if (msgIdState == ReceivedMsgIds::State::NotFound) {
state |= 0x02; state |= 0x02;
} else { } else {
state |= 0x04; state |= 0x04;
if (wereAcked.constFind(reqMsgId) != wereAckedEnd) { if (wereAcked.constFind(reqMsgId) != wereAckedEnd) {
state |= 0x80; // we know, that server knows, that we received request state |= 0x80; // we know, that server knows, that we received request
} }
if (recv.value()) { // need ack, so we sent ack if (msgIdState == ReceivedMsgIds::State::NeedsAck) { // need ack, so we sent ack
state |= 0x08; state |= 0x08;
} else { } else {
state |= 0x10; state |= 0x10;
@ -1765,7 +1771,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
} }
} }
emit sendMsgsStateInfoAsync(msgId, info); emit sendMsgsStateInfoAsync(msgId, info);
} return 1; } return HandleResult::Success;
case mtpc_msgs_state_info: { case mtpc_msgs_state_info: {
MTPMsgsStateInfo msg(from, end); MTPMsgsStateInfo msg(from, end);
@ -1782,7 +1788,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
mtpRequestMap::const_iterator replyTo = haveSent.constFind(reqMsgId); mtpRequestMap::const_iterator replyTo = haveSent.constFind(reqMsgId);
if (replyTo == haveSent.cend()) { // do not look in toResend, because we do not resend msgs_state_req requests if (replyTo == haveSent.cend()) { // do not look in toResend, because we do not resend msgs_state_req requests
DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(reqMsgId)); DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(reqMsgId));
return (badTime ? 0 : 1); return (badTime ? HandleResult::Ignored : HandleResult::Success);
} }
if (badTime) { if (badTime) {
if (serverSalt) sessionData->setSalt(serverSalt); // requestsFixTimeSalt with no lookup if (serverSalt) sessionData->setSalt(serverSalt); // requestsFixTimeSalt with no lookup
@ -1799,7 +1805,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
if (requestBuffer->size() < 9) { if (requestBuffer->size() < 9) {
LOG(("Message Error: bad request %1 found in requestMap, size: %2").arg(reqMsgId).arg(requestBuffer->size())); LOG(("Message Error: bad request %1 found in requestMap, size: %2").arg(reqMsgId).arg(requestBuffer->size()));
return -1; return HandleResult::RestartConnection;
} }
try { try {
const mtpPrime *rFrom = requestBuffer->constData() + 8, *rEnd = requestBuffer->constData() + requestBuffer->size(); const mtpPrime *rFrom = requestBuffer->constData() + 8, *rEnd = requestBuffer->constData() + requestBuffer->size();
@ -1816,12 +1822,12 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
} }
requestsAcked(toAck); requestsAcked(toAck);
} return 1; } return HandleResult::Success;
case mtpc_msgs_all_info: { case mtpc_msgs_all_info: {
if (badTime) { if (badTime) {
DEBUG_LOG(("Message Info: skipping with bad time...")); DEBUG_LOG(("Message Info: skipping with bad time..."));
return 0; return HandleResult::Ignored;
} }
MTPMsgsAllInfo msg(from, end); MTPMsgsAllInfo msg(from, end);
@ -1835,7 +1841,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
handleMsgsStates(ids, states, toAck); handleMsgsStates(ids, states, toAck);
requestsAcked(toAck); requestsAcked(toAck);
} return 1; } return HandleResult::Success;
case mtpc_msg_detailed_info: { case mtpc_msg_detailed_info: {
MTPMsgDetailedInfo msg(from, end); MTPMsgDetailedInfo msg(from, end);
@ -1849,7 +1855,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
badTime = false; badTime = false;
} else { } else {
DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(data.vmsg_id.v)); DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(data.vmsg_id.v));
return 0; return HandleResult::Ignored;
} }
} }
requestsAcked(ids); requestsAcked(ids);
@ -1858,8 +1864,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
MTPlong resMsgId = data.vanswer_msg_id; MTPlong resMsgId = data.vanswer_msg_id;
{ {
QReadLocker lock(sessionData->receivedIdsMutex()); QReadLocker lock(sessionData->receivedIdsMutex());
const mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); received = (sessionData->receivedIdsSet().lookup(resMsgId.v) != ReceivedMsgIds::State::NotFound);
received = (receivedIds.find(resMsgId.v) != receivedIds.cend()) && (receivedIds.min() < resMsgId.v);
} }
if (received) { if (received) {
ackRequestData.push_back(resMsgId); ackRequestData.push_back(resMsgId);
@ -1867,12 +1872,12 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
DEBUG_LOG(("Message Info: answer message %1 was not received, requesting...").arg(resMsgId.v)); DEBUG_LOG(("Message Info: answer message %1 was not received, requesting...").arg(resMsgId.v));
resendRequestData.push_back(resMsgId); resendRequestData.push_back(resMsgId);
} }
} return 1; } return HandleResult::Success;
case mtpc_msg_new_detailed_info: { case mtpc_msg_new_detailed_info: {
if (badTime) { if (badTime) {
DEBUG_LOG(("Message Info: skipping msg_new_detailed_info with bad time...")); DEBUG_LOG(("Message Info: skipping msg_new_detailed_info with bad time..."));
return 0; return HandleResult::Ignored;
} }
MTPMsgDetailedInfo msg(from, end); MTPMsgDetailedInfo msg(from, end);
const auto &data(msg.c_msg_new_detailed_info()); const auto &data(msg.c_msg_new_detailed_info());
@ -1883,8 +1888,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
MTPlong resMsgId = data.vanswer_msg_id; MTPlong resMsgId = data.vanswer_msg_id;
{ {
QReadLocker lock(sessionData->receivedIdsMutex()); QReadLocker lock(sessionData->receivedIdsMutex());
const mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); received = (sessionData->receivedIdsSet().lookup(resMsgId.v) != ReceivedMsgIds::State::NotFound);
received = (receivedIds.find(resMsgId.v) != receivedIds.cend()) && (receivedIds.min() < resMsgId.v);
} }
if (received) { if (received) {
ackRequestData.push_back(resMsgId); ackRequestData.push_back(resMsgId);
@ -1892,7 +1896,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
DEBUG_LOG(("Message Info: answer message %1 was not received, requesting...").arg(resMsgId.v)); DEBUG_LOG(("Message Info: answer message %1 was not received, requesting...").arg(resMsgId.v));
resendRequestData.push_back(resMsgId); resendRequestData.push_back(resMsgId);
} }
} return 1; } return HandleResult::Success;
case mtpc_msg_resend_req: { case mtpc_msg_resend_req: {
MTPMsgResendReq msg(from, end); MTPMsgResendReq msg(from, end);
@ -1900,14 +1904,14 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
uint32 idsCount = ids.size(); uint32 idsCount = ids.size();
DEBUG_LOG(("Message Info: resend of msgs requested, ids: %1").arg(Logs::vector(ids))); DEBUG_LOG(("Message Info: resend of msgs requested, ids: %1").arg(Logs::vector(ids)));
if (!idsCount) return (badTime ? 0 : 1); if (!idsCount) return (badTime ? HandleResult::Ignored : HandleResult::Success);
QVector<quint64> toResend(ids.size()); QVector<quint64> toResend(ids.size());
for (int32 i = 0, l = ids.size(); i < l; ++i) { for (int32 i = 0, l = ids.size(); i < l; ++i) {
toResend[i] = ids.at(i).v; toResend[i] = ids.at(i).v;
} }
resendMany(toResend, 0, false, true); resendMany(toResend, 0, false, true);
} return 1; } return HandleResult::Success;
case mtpc_rpc_result: { case mtpc_rpc_result: {
if (from + 3 > end) throw mtpErrorInsufficient(); if (from + 3 > end) throw mtpErrorInsufficient();
@ -1924,7 +1928,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
badTime = false; badTime = false;
} else { } else {
DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(reqMsgId.v)); DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(reqMsgId.v));
return 0; return HandleResult::Ignored;
} }
} }
requestsAcked(ids, true); requestsAcked(ids, true);
@ -1933,7 +1937,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
DEBUG_LOG(("RPC Info: gzip container")); DEBUG_LOG(("RPC Info: gzip container"));
response = ungzip(++from, end); response = ungzip(++from, end);
if (!response.size()) { if (!response.size()) {
return -1; return HandleResult::RestartConnection;
} }
typeId = response[0]; typeId = response[0];
} else { } else {
@ -1952,7 +1956,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
} else { } else {
DEBUG_LOG(("RPC Info: requestId not found for msgId %1").arg(reqMsgId.v)); DEBUG_LOG(("RPC Info: requestId not found for msgId %1").arg(reqMsgId.v));
} }
} return 1; } return HandleResult::Success;
case mtpc_new_session_created: { case mtpc_new_session_created: {
const mtpPrime *start = from; const mtpPrime *start = from;
@ -1964,7 +1968,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
badTime = false; badTime = false;
} else { } else {
DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(data.vfirst_msg_id.v)); DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(data.vfirst_msg_id.v));
return 0; return HandleResult::Ignored;
} }
} }
@ -1991,16 +1995,16 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
mtpResponseMap &haveReceived(sessionData->haveReceivedMap()); mtpResponseMap &haveReceived(sessionData->haveReceivedMap());
mtpRequestId fakeRequestId = sessionData->nextFakeRequestId(); mtpRequestId fakeRequestId = sessionData->nextFakeRequestId();
haveReceived.insert(fakeRequestId, mtpResponse(update)); // notify main process about new session - need to get difference haveReceived.insert(fakeRequestId, mtpResponse(update)); // notify main process about new session - need to get difference
} return 1; } return HandleResult::Success;
case mtpc_ping: { case mtpc_ping: {
if (badTime) return 0; if (badTime) return HandleResult::Ignored;
MTPPing msg(from, end); MTPPing msg(from, end);
DEBUG_LOG(("Message Info: ping received, ping_id: %1, sending pong...").arg(msg.vping_id.v)); DEBUG_LOG(("Message Info: ping received, ping_id: %1, sending pong...").arg(msg.vping_id.v));
emit sendPongAsync(msgId, msg.vping_id.v); emit sendPongAsync(msgId, msg.vping_id.v);
} return 1; } return HandleResult::Success;
case mtpc_pong: { case mtpc_pong: {
MTPPong msg(from, end); MTPPong msg(from, end);
@ -2009,7 +2013,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
if (!wasSent(data.vmsg_id.v)) { if (!wasSent(data.vmsg_id.v)) {
DEBUG_LOG(("Message Error: such msg_id %1 ping_id %2 was not sent recently").arg(data.vmsg_id.v).arg(data.vping_id.v)); DEBUG_LOG(("Message Error: such msg_id %1 ping_id %2 was not sent recently").arg(data.vmsg_id.v).arg(data.vping_id.v));
return 0; return HandleResult::Ignored;
} }
if (data.vping_id.v == _pingId) { if (data.vping_id.v == _pingId) {
_pingId = 0; _pingId = 0;
@ -2022,21 +2026,21 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
if (requestsFixTimeSalt(ids, serverTime, serverSalt)) { if (requestsFixTimeSalt(ids, serverTime, serverSalt)) {
badTime = false; badTime = false;
} else { } else {
return 0; return HandleResult::Ignored;
} }
} }
requestsAcked(ids, true); requestsAcked(ids, true);
} return 1; } return HandleResult::Success;
} }
} catch (Exception &) { } catch (Exception &) {
return -1; return HandleResult::RestartConnection;
} }
if (badTime) { if (badTime) {
DEBUG_LOG(("Message Error: bad time in updates cons, must create new session")); DEBUG_LOG(("Message Error: bad time in updates cons, must create new session"));
return -2; return HandleResult::ResetSession;
} }
mtpBuffer update(end - from); mtpBuffer update(end - from);
@ -2051,7 +2055,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
LOG(("Message Error: unknown constructor %1").arg(cons)); // maybe new api?.. LOG(("Message Error: unknown constructor %1").arg(cons)); // maybe new api?..
} }
return 1; return HandleResult::Success;
} }
mtpBuffer ConnectionPrivate::ungzip(const mtpPrime *from, const mtpPrime *end) const { mtpBuffer ConnectionPrivate::ungzip(const mtpPrime *from, const mtpPrime *end) const {
@ -2377,13 +2381,13 @@ void ConnectionPrivate::updateAuthKey() {
return authKeyCreated(); return authKeyCreated();
} }
authKeyData = new ConnectionPrivate::AuthKeyCreateData(); _authKeyData = std_::make_unique<ConnectionPrivate::AuthKeyCreateData>();
authKeyStrings = new ConnectionPrivate::AuthKeyCreateStrings(); _authKeyStrings = std_::make_unique<ConnectionPrivate::AuthKeyCreateStrings>();
authKeyData->req_num = 0; _authKeyData->req_num = 0;
authKeyData->nonce = rand_value<MTPint128>(); _authKeyData->nonce = rand_value<MTPint128>();
MTPReq_pq req_pq; MTPReq_pq req_pq;
req_pq.vnonce = authKeyData->nonce; req_pq.vnonce = _authKeyData->nonce;
connect(_conn, SIGNAL(receivedData()), this, SLOT(pqAnswered())); connect(_conn, SIGNAL(receivedData()), this, SLOT(pqAnswered()));
@ -2408,9 +2412,9 @@ void ConnectionPrivate::pqAnswered() {
} }
const auto &res_pq_data(res_pq.c_resPQ()); const auto &res_pq_data(res_pq.c_resPQ());
if (res_pq_data.vnonce != authKeyData->nonce) { if (res_pq_data.vnonce != _authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in res_pq)!")); LOG(("AuthKey Error: received nonce <> sent nonce (in res_pq)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&res_pq_data.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&res_pq_data.vnonce, 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str()));
return restart(); return restart();
} }
@ -2436,12 +2440,12 @@ void ConnectionPrivate::pqAnswered() {
return restart(); return restart();
} }
authKeyData->server_nonce = res_pq_data.vserver_nonce; _authKeyData->server_nonce = res_pq_data.vserver_nonce;
MTPP_Q_inner_data p_q_inner; MTPP_Q_inner_data p_q_inner;
MTPDp_q_inner_data &p_q_inner_data(p_q_inner._p_q_inner_data()); MTPDp_q_inner_data &p_q_inner_data(p_q_inner._p_q_inner_data());
p_q_inner_data.vnonce = authKeyData->nonce; p_q_inner_data.vnonce = _authKeyData->nonce;
p_q_inner_data.vserver_nonce = authKeyData->server_nonce; p_q_inner_data.vserver_nonce = _authKeyData->server_nonce;
p_q_inner_data.vpq = res_pq_data.vpq; p_q_inner_data.vpq = res_pq_data.vpq;
const string &pq(res_pq_data.vpq.c_string().v); const string &pq(res_pq_data.vpq.c_string().v);
@ -2453,12 +2457,12 @@ void ConnectionPrivate::pqAnswered() {
return restart(); return restart();
} }
authKeyData->new_nonce = rand_value<MTPint256>(); _authKeyData->new_nonce = rand_value<MTPint256>();
p_q_inner_data.vnew_nonce = authKeyData->new_nonce; p_q_inner_data.vnew_nonce = _authKeyData->new_nonce;
MTPReq_DH_params req_DH_params; MTPReq_DH_params req_DH_params;
req_DH_params.vnonce = authKeyData->nonce; req_DH_params.vnonce = _authKeyData->nonce;
req_DH_params.vserver_nonce = authKeyData->server_nonce; req_DH_params.vserver_nonce = _authKeyData->server_nonce;
req_DH_params.vpublic_key_fingerprint = MTP_long(rsaKey->getFingerPrint()); req_DH_params.vpublic_key_fingerprint = MTP_long(rsaKey->getFingerPrint());
req_DH_params.vp = p_q_inner_data.vp; req_DH_params.vp = p_q_inner_data.vp;
req_DH_params.vq = p_q_inner_data.vq; req_DH_params.vq = p_q_inner_data.vq;
@ -2508,14 +2512,14 @@ void ConnectionPrivate::dhParamsAnswered() {
switch (res_DH_params.type()) { switch (res_DH_params.type()) {
case mtpc_server_DH_params_ok: { case mtpc_server_DH_params_ok: {
const auto &encDH(res_DH_params.c_server_DH_params_ok()); const auto &encDH(res_DH_params.c_server_DH_params_ok());
if (encDH.vnonce != authKeyData->nonce) { if (encDH.vnonce != _authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_ok)!")); LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_ok)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce, 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str()));
return restart(); return restart();
} }
if (encDH.vserver_nonce != authKeyData->server_nonce) { if (encDH.vserver_nonce != _authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_ok)!")); LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_ok)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce, 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str()));
return restart(); return restart();
} }
@ -2527,12 +2531,12 @@ void ConnectionPrivate::dhParamsAnswered() {
return restart(); return restart();
} }
uint32 nlen = authKeyData->new_nonce.innerLength(), slen = authKeyData->server_nonce.innerLength(); uint32 nlen = _authKeyData->new_nonce.innerLength(), slen = _authKeyData->server_nonce.innerLength();
uchar tmp_aes[1024], sha1ns[20], sha1sn[20], sha1nn[20]; uchar tmp_aes[1024], sha1ns[20], sha1sn[20], sha1nn[20];
memcpy(tmp_aes, &authKeyData->new_nonce, nlen); memcpy(tmp_aes, &_authKeyData->new_nonce, nlen);
memcpy(tmp_aes + nlen, &authKeyData->server_nonce, slen); memcpy(tmp_aes + nlen, &_authKeyData->server_nonce, slen);
memcpy(tmp_aes + nlen + slen, &authKeyData->new_nonce, nlen); memcpy(tmp_aes + nlen + slen, &_authKeyData->new_nonce, nlen);
memcpy(tmp_aes + nlen + slen + nlen, &authKeyData->new_nonce, nlen); memcpy(tmp_aes + nlen + slen + nlen, &_authKeyData->new_nonce, nlen);
hashSha1(tmp_aes, nlen + slen, sha1ns); hashSha1(tmp_aes, nlen + slen, sha1ns);
hashSha1(tmp_aes + nlen, nlen + slen, sha1sn); hashSha1(tmp_aes + nlen, nlen + slen, sha1sn);
hashSha1(tmp_aes + nlen + slen, nlen + nlen, sha1nn); hashSha1(tmp_aes + nlen + slen, nlen + nlen, sha1nn);
@ -2540,31 +2544,31 @@ void ConnectionPrivate::dhParamsAnswered() {
mtpBuffer decBuffer; mtpBuffer decBuffer;
decBuffer.resize(encDHBufLen); decBuffer.resize(encDHBufLen);
memcpy(authKeyData->aesKey, sha1ns, 20); memcpy(_authKeyData->aesKey, sha1ns, 20);
memcpy(authKeyData->aesKey + 20, sha1sn, 12); memcpy(_authKeyData->aesKey + 20, sha1sn, 12);
memcpy(authKeyData->aesIV, sha1sn + 12, 8); memcpy(_authKeyData->aesIV, sha1sn + 12, 8);
memcpy(authKeyData->aesIV + 8, sha1nn, 20); memcpy(_authKeyData->aesIV + 8, sha1nn, 20);
memcpy(authKeyData->aesIV + 28, &authKeyData->new_nonce, 4); memcpy(_authKeyData->aesIV + 28, &_authKeyData->new_nonce, 4);
aesIgeDecrypt(&encDHStr[0], &decBuffer[0], encDHLen, authKeyData->aesKey, authKeyData->aesIV); aesIgeDecrypt(&encDHStr[0], &decBuffer[0], encDHLen, _authKeyData->aesKey, _authKeyData->aesIV);
const mtpPrime *from(&decBuffer[5]), *to(from), *end(from + (encDHBufLen - 5)); const mtpPrime *from(&decBuffer[5]), *to(from), *end(from + (encDHBufLen - 5));
MTPServer_DH_inner_data dh_inner(to, end); MTPServer_DH_inner_data dh_inner(to, end);
const auto &dh_inner_data(dh_inner.c_server_DH_inner_data()); const auto &dh_inner_data(dh_inner.c_server_DH_inner_data());
if (dh_inner_data.vnonce != authKeyData->nonce) { if (dh_inner_data.vnonce != _authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_inner_data)!")); LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_inner_data)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&dh_inner_data.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&dh_inner_data.vnonce, 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str()));
return restart(); return restart();
} }
if (dh_inner_data.vserver_nonce != authKeyData->server_nonce) { if (dh_inner_data.vserver_nonce != _authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_inner_data)!")); LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_inner_data)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&dh_inner_data.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&dh_inner_data.vserver_nonce, 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str()));
return restart(); return restart();
} }
uchar sha1Buffer[20]; uchar sha1Buffer[20];
if (memcmp(&decBuffer[0], hashSha1(&decBuffer[5], (to - from) * sizeof(mtpPrime), sha1Buffer), 20)) { if (memcmp(&decBuffer[0], hashSha1(&decBuffer[5], (to - from) * sizeof(mtpPrime), sha1Buffer), 20)) {
LOG(("AuthKey Error: sha1 hash of encrypted part did not match!")); LOG(("AuthKey Error: sha1 hash of encrypted part did not match!"));
DEBUG_LOG(("AuthKey Error: sha1 did not match, server_nonce: %1, new_nonce %2, encrypted data %3").arg(Logs::mb(&authKeyData->server_nonce, 16).str()).arg(Logs::mb(&authKeyData->new_nonce, 16).str()).arg(Logs::mb(&encDHStr[0], encDHLen).str())); DEBUG_LOG(("AuthKey Error: sha1 did not match, server_nonce: %1, new_nonce %2, encrypted data %3").arg(Logs::mb(&_authKeyData->server_nonce, 16).str()).arg(Logs::mb(&_authKeyData->new_nonce, 16).str()).arg(Logs::mb(&encDHStr[0], encDHLen).str()));
return restart(); return restart();
} }
unixtimeSet(dh_inner_data.vserver_time.v); unixtimeSet(dh_inner_data.vserver_time.v);
@ -2584,29 +2588,29 @@ void ConnectionPrivate::dhParamsAnswered() {
return restart(); return restart();
} }
authKeyStrings->dh_prime = QByteArray(dhPrime.data(), dhPrime.size()); _authKeyStrings->dh_prime = QByteArray(dhPrime.data(), dhPrime.size());
authKeyData->g = dh_inner_data.vg.v; _authKeyData->g = dh_inner_data.vg.v;
authKeyStrings->g_a = QByteArray(g_a.data(), g_a.size()); _authKeyStrings->g_a = QByteArray(g_a.data(), g_a.size());
authKeyData->retry_id = MTP_long(0); _authKeyData->retry_id = MTP_long(0);
authKeyData->retries = 0; _authKeyData->retries = 0;
} return dhClientParamsSend(); } return dhClientParamsSend();
case mtpc_server_DH_params_fail: { case mtpc_server_DH_params_fail: {
const auto &encDH(res_DH_params.c_server_DH_params_fail()); const auto &encDH(res_DH_params.c_server_DH_params_fail());
if (encDH.vnonce != authKeyData->nonce) { if (encDH.vnonce != _authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_fail)!")); LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_fail)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce, 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str()));
return restart(); return restart();
} }
if (encDH.vserver_nonce != authKeyData->server_nonce) { if (encDH.vserver_nonce != _authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_fail)!")); LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_fail)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce, 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str()));
return restart(); return restart();
} }
uchar sha1Buffer[20]; uchar sha1Buffer[20];
if (encDH.vnew_nonce_hash != *(MTPint128*)(hashSha1(&authKeyData->new_nonce, 32, sha1Buffer) + 1)) { if (encDH.vnew_nonce_hash != *(MTPint128*)(hashSha1(&_authKeyData->new_nonce, 32, sha1Buffer) + 1)) {
LOG(("AuthKey Error: received new_nonce_hash did not match!")); LOG(("AuthKey Error: received new_nonce_hash did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash: %1, new_nonce: %2").arg(Logs::mb(&encDH.vnew_nonce_hash, 16).str()).arg(Logs::mb(&authKeyData->new_nonce, 32).str())); DEBUG_LOG(("AuthKey Error: received new_nonce_hash: %1, new_nonce: %2").arg(Logs::mb(&encDH.vnew_nonce_hash, 16).str()).arg(Logs::mb(&_authKeyData->new_nonce, 32).str()));
return restart(); return restart();
} }
LOG(("AuthKey Error: server_DH_params_fail received!")); LOG(("AuthKey Error: server_DH_params_fail received!"));
@ -2618,16 +2622,16 @@ void ConnectionPrivate::dhParamsAnswered() {
} }
void ConnectionPrivate::dhClientParamsSend() { void ConnectionPrivate::dhClientParamsSend() {
if (++authKeyData->retries > 5) { if (++_authKeyData->retries > 5) {
LOG(("AuthKey Error: could not create auth_key for %1 retries").arg(authKeyData->retries - 1)); LOG(("AuthKey Error: could not create auth_key for %1 retries").arg(_authKeyData->retries - 1));
return restart(); return restart();
} }
MTPClient_DH_Inner_Data client_dh_inner; MTPClient_DH_Inner_Data client_dh_inner;
MTPDclient_DH_inner_data &client_dh_inner_data(client_dh_inner._client_DH_inner_data()); MTPDclient_DH_inner_data &client_dh_inner_data(client_dh_inner._client_DH_inner_data());
client_dh_inner_data.vnonce = authKeyData->nonce; client_dh_inner_data.vnonce = _authKeyData->nonce;
client_dh_inner_data.vserver_nonce = authKeyData->server_nonce; client_dh_inner_data.vserver_nonce = _authKeyData->server_nonce;
client_dh_inner_data.vretry_id = authKeyData->retry_id; client_dh_inner_data.vretry_id = _authKeyData->retry_id;
client_dh_inner_data.vg_b._string().v.resize(256); client_dh_inner_data.vg_b._string().v.resize(256);
// gen rand 'b' // gen rand 'b'
@ -2636,19 +2640,19 @@ void ConnectionPrivate::dhClientParamsSend() {
// count g_b and auth_key using openssl BIGNUM methods // count g_b and auth_key using openssl BIGNUM methods
MTP::internal::BigNumCounter bnCounter; MTP::internal::BigNumCounter bnCounter;
if (!bnCounter.count(b, authKeyStrings->dh_prime.constData(), authKeyData->g, g_b, authKeyStrings->g_a.constData(), authKeyData->auth_key)) { if (!bnCounter.count(b, _authKeyStrings->dh_prime.constData(), _authKeyData->g, g_b, _authKeyStrings->g_a.constData(), _authKeyData->auth_key)) {
return dhClientParamsSend(); return dhClientParamsSend();
} }
// count auth_key hashes - parts of sha1(auth_key) // count auth_key hashes - parts of sha1(auth_key)
uchar sha1Buffer[20]; uchar sha1Buffer[20];
int32 *auth_key_sha = hashSha1(authKeyData->auth_key, 256, sha1Buffer); int32 *auth_key_sha = hashSha1(_authKeyData->auth_key, 256, sha1Buffer);
memcpy(&authKeyData->auth_key_aux_hash, auth_key_sha, 8); memcpy(&_authKeyData->auth_key_aux_hash, auth_key_sha, 8);
memcpy(&authKeyData->auth_key_hash, auth_key_sha + 3, 8); memcpy(&_authKeyData->auth_key_hash, auth_key_sha + 3, 8);
MTPSet_client_DH_params req_client_DH_params; MTPSet_client_DH_params req_client_DH_params;
req_client_DH_params.vnonce = authKeyData->nonce; req_client_DH_params.vnonce = _authKeyData->nonce;
req_client_DH_params.vserver_nonce = authKeyData->server_nonce; req_client_DH_params.vserver_nonce = _authKeyData->server_nonce;
string &sdhEncString(req_client_DH_params.vencrypted_data._string().v); string &sdhEncString(req_client_DH_params.vencrypted_data._string().v);
@ -2670,7 +2674,7 @@ void ConnectionPrivate::dhClientParamsSend() {
sdhEncString.resize(encFullSize * 4); sdhEncString.resize(encFullSize * 4);
aesIgeEncrypt(&encBuffer[0], &sdhEncString[0], encFullSize * sizeof(mtpPrime), authKeyData->aesKey, authKeyData->aesIV); aesIgeEncrypt(&encBuffer[0], &sdhEncString[0], encFullSize * sizeof(mtpPrime), _authKeyData->aesKey, _authKeyData->aesIV);
connect(_conn, SIGNAL(receivedData()), this, SLOT(dhClientParamsAnswered())); connect(_conn, SIGNAL(receivedData()), this, SLOT(dhClientParamsAnswered()));
@ -2694,38 +2698,38 @@ void ConnectionPrivate::dhClientParamsAnswered() {
switch (res_client_DH_params.type()) { switch (res_client_DH_params.type()) {
case mtpc_dh_gen_ok: { case mtpc_dh_gen_ok: {
const auto &resDH(res_client_DH_params.c_dh_gen_ok()); const auto &resDH(res_client_DH_params.c_dh_gen_ok());
if (resDH.vnonce != authKeyData->nonce) { if (resDH.vnonce != _authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_ok)!")); LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_ok)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce, 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str()));
lockFinished.unlock(); lockFinished.unlock();
return restart(); return restart();
} }
if (resDH.vserver_nonce != authKeyData->server_nonce) { if (resDH.vserver_nonce != _authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_ok)!")); LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_ok)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce, 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str()));
lockFinished.unlock(); lockFinished.unlock();
return restart(); return restart();
} }
authKeyData->new_nonce_buf[32] = 1; _authKeyData->new_nonce_buf[32] = 1;
uchar sha1Buffer[20]; uchar sha1Buffer[20];
if (resDH.vnew_nonce_hash1 != *(MTPint128*)(hashSha1(authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) { if (resDH.vnew_nonce_hash1 != *(MTPint128*)(hashSha1(_authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) {
LOG(("AuthKey Error: received new_nonce_hash1 did not match!")); LOG(("AuthKey Error: received new_nonce_hash1 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash1: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash1, 16).str()).arg(Logs::mb(authKeyData->new_nonce_buf, 41).str())); DEBUG_LOG(("AuthKey Error: received new_nonce_hash1: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash1, 16).str()).arg(Logs::mb(_authKeyData->new_nonce_buf, 41).str()));
lockFinished.unlock(); lockFinished.unlock();
return restart(); return restart();
} }
uint64 salt1 = authKeyData->new_nonce.l.l, salt2 = authKeyData->server_nonce.l, serverSalt = salt1 ^ salt2; uint64 salt1 = _authKeyData->new_nonce.l.l, salt2 = _authKeyData->server_nonce.l, serverSalt = salt1 ^ salt2;
sessionData->setSalt(serverSalt); sessionData->setSalt(serverSalt);
AuthKeyPtr authKey(new AuthKey()); AuthKeyPtr authKey(new AuthKey());
authKey->setKey(authKeyData->auth_key); authKey->setKey(_authKeyData->auth_key);
authKey->setDC(bareDcId(dc)); authKey->setDC(bareDcId(dc));
DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2, auth key: %3").arg(authKey->keyId()).arg(serverSalt).arg(Logs::mb(authKeyData->auth_key, 256).str())); DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2, auth key: %3").arg(authKey->keyId()).arg(serverSalt).arg(Logs::mb(_authKeyData->auth_key, 256).str()));
sessionData->owner()->notifyKeyCreated(authKey); // slot will call authKeyCreated() sessionData->owner()->notifyKeyCreated(authKey); // slot will call authKeyCreated()
sessionData->clear(); sessionData->clear();
@ -2734,53 +2738,53 @@ void ConnectionPrivate::dhClientParamsAnswered() {
case mtpc_dh_gen_retry: { case mtpc_dh_gen_retry: {
const auto &resDH(res_client_DH_params.c_dh_gen_retry()); const auto &resDH(res_client_DH_params.c_dh_gen_retry());
if (resDH.vnonce != authKeyData->nonce) { if (resDH.vnonce != _authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_retry)!")); LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_retry)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce, 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str()));
lockFinished.unlock(); lockFinished.unlock();
return restart(); return restart();
} }
if (resDH.vserver_nonce != authKeyData->server_nonce) { if (resDH.vserver_nonce != _authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_retry)!")); LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_retry)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce, 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str()));
lockFinished.unlock(); lockFinished.unlock();
return restart(); return restart();
} }
authKeyData->new_nonce_buf[32] = 2; _authKeyData->new_nonce_buf[32] = 2;
uchar sha1Buffer[20]; uchar sha1Buffer[20];
if (resDH.vnew_nonce_hash2 != *(MTPint128*)(hashSha1(authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) { if (resDH.vnew_nonce_hash2 != *(MTPint128*)(hashSha1(_authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) {
LOG(("AuthKey Error: received new_nonce_hash2 did not match!")); LOG(("AuthKey Error: received new_nonce_hash2 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash2: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash2, 16).str()).arg(Logs::mb(authKeyData->new_nonce_buf, 41).str())); DEBUG_LOG(("AuthKey Error: received new_nonce_hash2: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash2, 16).str()).arg(Logs::mb(_authKeyData->new_nonce_buf, 41).str()));
lockFinished.unlock(); lockFinished.unlock();
return restart(); return restart();
} }
authKeyData->retry_id = authKeyData->auth_key_aux_hash; _authKeyData->retry_id = _authKeyData->auth_key_aux_hash;
} return dhClientParamsSend(); } return dhClientParamsSend();
case mtpc_dh_gen_fail: { case mtpc_dh_gen_fail: {
const auto &resDH(res_client_DH_params.c_dh_gen_fail()); const auto &resDH(res_client_DH_params.c_dh_gen_fail());
if (resDH.vnonce != authKeyData->nonce) { if (resDH.vnonce != _authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_fail)!")); LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_fail)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce, 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str()));
lockFinished.unlock(); lockFinished.unlock();
return restart(); return restart();
} }
if (resDH.vserver_nonce != authKeyData->server_nonce) { if (resDH.vserver_nonce != _authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_fail)!")); LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_fail)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str())); DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce, 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str()));
lockFinished.unlock(); lockFinished.unlock();
return restart(); return restart();
} }
authKeyData->new_nonce_buf[32] = 3; _authKeyData->new_nonce_buf[32] = 3;
uchar sha1Buffer[20]; uchar sha1Buffer[20];
if (resDH.vnew_nonce_hash3 != *(MTPint128*)(hashSha1(authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) { if (resDH.vnew_nonce_hash3 != *(MTPint128*)(hashSha1(_authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) {
LOG(("AuthKey Error: received new_nonce_hash3 did not match!")); LOG(("AuthKey Error: received new_nonce_hash3 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash3: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash3, 16).str()).arg(Logs::mb(authKeyData->new_nonce_buf, 41).str())); DEBUG_LOG(("AuthKey Error: received new_nonce_hash3: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash3, 16).str()).arg(Logs::mb(_authKeyData->new_nonce_buf, 41).str()));
lockFinished.unlock(); lockFinished.unlock();
return restart(); return restart();
@ -2817,20 +2821,18 @@ void ConnectionPrivate::authKeyCreated() {
} }
void ConnectionPrivate::clearAuthKeyData() { void ConnectionPrivate::clearAuthKeyData() {
if (authKeyData) { if (_authKeyData) {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
SecureZeroMemory(authKeyData, sizeof(AuthKeyCreateData)); SecureZeroMemory(_authKeyData.get(), sizeof(AuthKeyCreateData));
if (!authKeyStrings->dh_prime.isEmpty()) SecureZeroMemory(authKeyStrings->dh_prime.data(), authKeyStrings->dh_prime.size()); if (!_authKeyStrings->dh_prime.isEmpty()) SecureZeroMemory(_authKeyStrings->dh_prime.data(), _authKeyStrings->dh_prime.size());
if (!authKeyStrings->g_a.isEmpty()) SecureZeroMemory(authKeyStrings->g_a.data(), authKeyStrings->g_a.size()); if (!_authKeyStrings->g_a.isEmpty()) SecureZeroMemory(_authKeyStrings->g_a.data(), _authKeyStrings->g_a.size());
#else #else
memset(authKeyData, 0, sizeof(AuthKeyCreateData)); memset(authKeyData, 0, sizeof(AuthKeyCreateData));
if (!authKeyStrings->dh_prime.isEmpty()) memset(authKeyStrings->dh_prime.data(), 0, authKeyStrings->dh_prime.size()); if (!_authKeyStrings->dh_prime.isEmpty()) memset(_authKeyStrings->dh_prime.data(), 0, _authKeyStrings->dh_prime.size());
if (!authKeyStrings->g_a.isEmpty()) memset(authKeyStrings->g_a.data(), 0, authKeyStrings->g_a.size()); if (!_authKeyStrings->g_a.isEmpty()) memset(_authKeyStrings->g_a.data(), 0, _authKeyStrings->g_a.size());
#endif #endif
delete authKeyData; _authKeyData.reset();
authKeyData = 0; _authKeyStrings.reset();
delete authKeyStrings;
authKeyStrings = 0;
} }
} }
@ -2877,14 +2879,14 @@ void ConnectionPrivate::sendRequestNotSecure(const TRequest &request) {
buffer.push_back(0); // tcp packet num buffer.push_back(0); // tcp packet num
buffer.push_back(0); buffer.push_back(0);
buffer.push_back(0); buffer.push_back(0);
buffer.push_back(authKeyData->req_num); buffer.push_back(_authKeyData->req_num);
buffer.push_back(unixtime()); buffer.push_back(unixtime());
buffer.push_back(requestSize * 4); buffer.push_back(requestSize * 4);
request.write(buffer); request.write(buffer);
buffer.push_back(0); // tcp crc32 hash buffer.push_back(0); // tcp crc32 hash
++authKeyData->msgs_sent; ++_authKeyData->msgs_sent;
DEBUG_LOG(("AuthKey Info: sending request, size: %1, num: %2, time: %3").arg(requestSize).arg(authKeyData->req_num).arg(buffer[5])); DEBUG_LOG(("AuthKey Info: sending request, size: %1, num: %2, time: %3").arg(requestSize).arg(_authKeyData->req_num).arg(buffer[5]));
_conn->sendData(buffer); _conn->sendData(buffer);

View file

@ -164,7 +164,13 @@ private:
bool sendRequest(mtpRequest &request, bool needAnyResponse, QReadLocker &lockFinished); bool sendRequest(mtpRequest &request, bool needAnyResponse, QReadLocker &lockFinished);
mtpRequestId wasSent(mtpMsgId msgId) const; mtpRequestId wasSent(mtpMsgId msgId) const;
int32 handleOneReceived(const mtpPrime *from, const mtpPrime *end, uint64 msgId, int32 serverTime, uint64 serverSalt, bool badTime); enum class HandleResult {
Success,
Ignored,
RestartConnection,
ResetSession,
};
HandleResult handleOneReceived(const mtpPrime *from, const mtpPrime *end, uint64 msgId, int32 serverTime, uint64 serverSalt, bool badTime);
mtpBuffer ungzip(const mtpPrime *from, const mtpPrime *end) const; mtpBuffer ungzip(const mtpPrime *from, const mtpPrime *end) const;
void handleMsgsStates(const QVector<MTPlong> &ids, const std::string &states, QVector<MTPlong> &acked); void handleMsgsStates(const QVector<MTPlong> &ids, const std::string &states, QVector<MTPlong> &acked);
@ -174,23 +180,25 @@ private:
mutable QReadWriteLock stateConnMutex; mutable QReadWriteLock stateConnMutex;
int32 _state; int32 _state;
bool _needSessionReset; bool _needSessionReset = false;
void resetSession(); void resetSession();
ShiftedDcId dc; ShiftedDcId dc = 0;
Connection *_owner; Connection *_owner = nullptr;
AbstractConnection *_conn, *_conn4, *_conn6; AbstractConnection *_conn = nullptr;
AbstractConnection *_conn4 = nullptr;
AbstractConnection *_conn6 = nullptr;;
SingleTimer retryTimer; // exp retry timer SingleTimer retryTimer; // exp retry timer
int retryTimeout; int retryTimeout = 1;
qint64 retryWillFinish; qint64 retryWillFinish;
SingleTimer oldConnectionTimer; SingleTimer oldConnectionTimer;
bool oldConnection; bool oldConnection = true;
SingleTimer _waitForConnectedTimer, _waitForReceivedTimer, _waitForIPv4Timer; SingleTimer _waitForConnectedTimer, _waitForReceivedTimer, _waitForIPv4Timer;
uint32 _waitForReceived, _waitForConnected; uint32 _waitForReceived, _waitForConnected;
TimeMs firstSentAt; TimeMs firstSentAt = -1;
QVector<MTPlong> ackRequestData, resendRequestData; QVector<MTPlong> ackRequestData, resendRequestData;
@ -200,9 +208,10 @@ private:
// remove msgs with such ids from sessionData->haveSent, add to sessionData->wereAcked // remove msgs with such ids from sessionData->haveSent, add to sessionData->wereAcked
void requestsAcked(const QVector<MTPlong> &ids, bool byResponse = false); void requestsAcked(const QVector<MTPlong> &ids, bool byResponse = false);
mtpPingId _pingId, _pingIdToSend; mtpPingId _pingId = 0;
TimeMs _pingSendAt; mtpPingId _pingIdToSend = 0;
mtpMsgId _pingMsgId; TimeMs _pingSendAt = 0;
mtpMsgId _pingMsgId = 0;
SingleTimer _pingSender; SingleTimer _pingSender;
void resend(quint64 msgId, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false); void resend(quint64 msgId, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
@ -214,13 +223,14 @@ private:
template <typename TResponse> template <typename TResponse>
bool readResponseNotSecure(TResponse &response); bool readResponseNotSecure(TResponse &response);
bool restarted, _finished; bool restarted = false;
bool _finished = false;
uint64 keyId; uint64 keyId = 0;
QReadWriteLock sessionDataMutex; QReadWriteLock sessionDataMutex;
SessionData *sessionData; SessionData *sessionData = nullptr;
bool myKeyLock; bool myKeyLock = false;
void lockKey(); void lockKey();
void unlockKey(); void unlockKey();
@ -228,39 +238,32 @@ private:
struct AuthKeyCreateData { struct AuthKeyCreateData {
AuthKeyCreateData() AuthKeyCreateData()
: new_nonce(*(MTPint256*)((uchar*)new_nonce_buf)) : new_nonce(*(MTPint256*)((uchar*)new_nonce_buf))
, auth_key_aux_hash(*(MTPlong*)((uchar*)new_nonce_buf + 33)) , auth_key_aux_hash(*(MTPlong*)((uchar*)new_nonce_buf + 33)) {
, retries(0)
, g(0)
, req_num(0)
, msgs_sent(0) {
memset(new_nonce_buf, 0, sizeof(new_nonce_buf));
memset(aesKey, 0, sizeof(aesKey));
memset(aesIV, 0, sizeof(aesIV));
memset(auth_key, 0, sizeof(auth_key));
} }
MTPint128 nonce, server_nonce; MTPint128 nonce, server_nonce;
uchar new_nonce_buf[41]; // 32 bytes new_nonce + 1 check byte + 8 bytes of auth_key_aux_hash uchar new_nonce_buf[41] = { 0 }; // 32 bytes new_nonce + 1 check byte + 8 bytes of auth_key_aux_hash
MTPint256 &new_nonce; MTPint256 &new_nonce;
MTPlong &auth_key_aux_hash; MTPlong &auth_key_aux_hash;
uint32 retries; uint32 retries = 0;
MTPlong retry_id; MTPlong retry_id;
int32 g; int32 g = 0;
uchar aesKey[32], aesIV[32]; uchar aesKey[32] = { 0 };
uint32 auth_key[64]; uchar aesIV[32] = { 0 };
uint32 auth_key[64] = { 0 };
MTPlong auth_key_hash; MTPlong auth_key_hash;
uint32 req_num; // sent not encrypted request number uint32 req_num = 0; // sent not encrypted request number
uint32 msgs_sent; uint32 msgs_sent = 0;
}; };
struct AuthKeyCreateStrings { struct AuthKeyCreateStrings {
QByteArray dh_prime; QByteArray dh_prime;
QByteArray g_a; QByteArray g_a;
}; };
AuthKeyCreateData *authKeyData; std_::unique_ptr<AuthKeyCreateData> _authKeyData;
AuthKeyCreateStrings *authKeyStrings; std_::unique_ptr<AuthKeyCreateStrings> _authKeyStrings;
void dhClientParamsSend(); void dhClientParamsSend();
void authKeyCreated(); void authKeyCreated();

View file

@ -141,35 +141,6 @@ public:
typedef QMap<mtpRequestId, mtpRequest> mtpPreRequestMap; typedef QMap<mtpRequestId, mtpRequest> mtpPreRequestMap;
typedef QMap<mtpMsgId, mtpRequest> mtpRequestMap; typedef QMap<mtpMsgId, mtpRequest> mtpRequestMap;
typedef QMap<mtpMsgId, bool> mtpMsgIdsSet; typedef QMap<mtpMsgId, bool> mtpMsgIdsSet;
class mtpMsgIdsMap : public QMap<mtpMsgId, bool> {
public:
typedef QMap<mtpMsgId, bool> ParentType;
bool insert(const mtpMsgId &k, bool v) {
ParentType::const_iterator i = constFind(k);
if (i == cend()) {
if (size() >= MTPIdsBufferSize && k < min()) {
MTP_LOG(-1, ("No need to handle - %1 < min = %2").arg(k).arg(min()));
return false;
} else {
ParentType::insert(k, v);
return true;
}
} else {
MTP_LOG(-1, ("No need to handle - %1 already is in map").arg(k));
return false;
}
}
mtpMsgId min() const {
return isEmpty() ? 0 : cbegin().key();
}
mtpMsgId max() const {
ParentType::const_iterator e(cend());
return isEmpty() ? 0 : (--e).key();
}
};
class mtpRequestIdsMap : public QMap<mtpMsgId, mtpRequestId> { class mtpRequestIdsMap : public QMap<mtpMsgId, mtpRequestId> {
public: public:

View file

@ -28,18 +28,64 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace MTP { namespace MTP {
namespace internal { namespace internal {
class Session; class ReceivedMsgIds {
public:
bool registerMsgId(mtpMsgId msgId, bool needAck) {
auto i = _idsNeedAck.constFind(msgId);
if (i == _idsNeedAck.cend()) {
if (_idsNeedAck.size() < MTPIdsBufferSize || msgId > min()) {
_idsNeedAck.insert(msgId, needAck);
return true;
}
MTP_LOG(-1, ("No need to handle - %1 < min = %2").arg(msgId).arg(min()));
} else {
MTP_LOG(-1, ("No need to handle - %1 already is in map").arg(msgId));
}
return false;
}
mtpMsgId min() const {
return _idsNeedAck.isEmpty() ? 0 : _idsNeedAck.cbegin().key();
}
mtpMsgId max() const {
auto end = _idsNeedAck.cend();
return _idsNeedAck.isEmpty() ? 0 : (--end).key();
}
void shrink() {
auto size = _idsNeedAck.size();
while (size-- > MTPIdsBufferSize) {
_idsNeedAck.erase(_idsNeedAck.begin());
}
}
enum class State {
NotFound,
NeedsAck,
NoAckNeeded,
};
State lookup(mtpMsgId msgId) const {
auto i = _idsNeedAck.constFind(msgId);
if (i == _idsNeedAck.cend()) {
return State::NotFound;
}
return i.value() ? State::NeedsAck : State::NoAckNeeded;
}
void clear() {
_idsNeedAck.clear();
}
private:
QMap<mtpMsgId, bool> _idsNeedAck;
};
class Session;
class SessionData { class SessionData {
public: public:
SessionData(Session *creator) SessionData(Session *creator) : _owner(creator) {
: _session(0)
, _salt(0)
, _messagesSent(0)
, _fakeRequestId(-2000000000)
, _owner(creator)
, _keyChecked(false)
, _layerInited(false) {
} }
void setSession(uint64 session) { void setSession(uint64 session) {
@ -142,10 +188,10 @@ public:
const mtpRequestIdsMap &toResendMap() const { const mtpRequestIdsMap &toResendMap() const {
return toResend; return toResend;
} }
mtpMsgIdsMap &receivedIdsSet() { ReceivedMsgIds &receivedIdsSet() {
return receivedIds; return receivedIds;
} }
const mtpMsgIdsMap &receivedIdsSet() const { const ReceivedMsgIds &receivedIdsSet() const {
return receivedIds; return receivedIds;
} }
mtpRequestIdsMap &wereAckedMap() { mtpRequestIdsMap &wereAckedMap() {
@ -193,20 +239,22 @@ public:
void clear(); void clear();
private: private:
uint64 _session, _salt; uint64 _session = 0;
uint64 _salt = 0;
uint32 _messagesSent; uint32 _messagesSent = 0;
mtpRequestId _fakeRequestId; mtpRequestId _fakeRequestId = -2000000000;
Session *_owner; Session *_owner = nullptr;
AuthKeyPtr _authKey; AuthKeyPtr _authKey;
bool _keyChecked, _layerInited; bool _keyChecked = false;
bool _layerInited = false;
mtpPreRequestMap toSend; // map of request_id -> request, that is waiting to be sent mtpPreRequestMap toSend; // map of request_id -> request, that is waiting to be sent
mtpRequestMap haveSent; // map of msg_id -> request, that was sent, msDate = 0 for msgs_state_req (no resend / state req), msDate = 0, seqNo = 0 for containers mtpRequestMap haveSent; // map of msg_id -> request, that was sent, msDate = 0 for msgs_state_req (no resend / state req), msDate = 0, seqNo = 0 for containers
mtpRequestIdsMap toResend; // map of msg_id -> request_id, that request_id -> request lies in toSend and is waiting to be resent mtpRequestIdsMap toResend; // map of msg_id -> request_id, that request_id -> request lies in toSend and is waiting to be resent
mtpMsgIdsMap receivedIds; // set of received msg_id's, for checking new msg_ids ReceivedMsgIds receivedIds; // set of received msg_id's, for checking new msg_ids
mtpRequestIdsMap wereAcked; // map of msg_id -> request_id, this msg_ids already were acked or do not need ack mtpRequestIdsMap wereAcked; // map of msg_id -> request_id, this msg_ids already were acked or do not need ack
mtpResponseMap haveReceived; // map of request_id -> response, that should be processed in other thread mtpResponseMap haveReceived; // map of request_id -> response, that should be processed in other thread
mtpMsgIdsSet stateRequest; // set of msg_id's, whose state should be requested mtpMsgIdsSet stateRequest; // set of msg_id's, whose state should be requested

View file

@ -62,8 +62,9 @@ void ItemBase::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed
void RadialProgressItem::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { void RadialProgressItem::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (p == _openl || p == _savel || p == _cancell) { if (p == _openl || p == _savel || p == _cancell) {
a_iconOver.start(active ? 1 : 0); if (iconAnimated()) {
_a_iconOver.start(); _a_iconOver.start([this] { Ui::repaintHistoryItem(_parent); }, active ? 0. : 1., active ? 1. : 0., st::msgFileOverDuration);
}
} }
ItemBase::clickHandlerActiveChanged(p, active); ItemBase::clickHandlerActiveChanged(p, active);
} }
@ -78,19 +79,6 @@ void RadialProgressItem::setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&sav
_cancell = std_::move(cancell); _cancell = std_::move(cancell);
} }
void RadialProgressItem::step_iconOver(float64 ms, bool timer) {
float64 dt = ms / st::msgFileOverDuration;
if (dt >= 1) {
a_iconOver.finish();
_a_iconOver.stop();
} else if (!timer) {
a_iconOver.update(dt, anim::linear);
}
if (timer && iconAnimated()) {
Ui::repaintHistoryItem(_parent);
}
}
void RadialProgressItem::step_radial(TimeMs ms, bool timer) { void RadialProgressItem::step_radial(TimeMs ms, bool timer) {
if (timer) { if (timer) {
Ui::repaintHistoryItem(_parent); Ui::repaintHistoryItem(_parent);
@ -104,20 +92,17 @@ void RadialProgressItem::step_radial(TimeMs ms, bool timer) {
void RadialProgressItem::ensureRadial() { void RadialProgressItem::ensureRadial() {
if (!_radial) { if (!_radial) {
_radial = new Ui::RadialAnimation(animation(const_cast<RadialProgressItem*>(this), &RadialProgressItem::step_radial)); _radial = std_::make_unique<Ui::RadialAnimation>(animation(const_cast<RadialProgressItem*>(this), &RadialProgressItem::step_radial));
} }
} }
void RadialProgressItem::checkRadialFinished() { void RadialProgressItem::checkRadialFinished() {
if (_radial && !_radial->animating() && dataLoaded()) { if (_radial && !_radial->animating() && dataLoaded()) {
delete _radial; _radial.reset();
_radial = nullptr;
} }
} }
RadialProgressItem::~RadialProgressItem() { RadialProgressItem::~RadialProgressItem() = default;
delete base::take(_radial);
}
void StatusText::update(int newSize, int fullSize, int duration, TimeMs realDuration) { void StatusText::update(int newSize, int fullSize, int duration, TimeMs realDuration) {
setSize(newSize); setSize(newSize);
@ -174,7 +159,7 @@ private:
base::lambda_copy<void()> _updateCallback; base::lambda_copy<void()> _updateCallback;
Ui::RoundCheckbox _check; Ui::RoundCheckbox _check;
FloatAnimation _pression; Animation _pression;
bool _active = false; bool _active = false;
bool _pressed = false; bool _pressed = false;
@ -393,13 +378,9 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
if (selected) { if (selected) {
p.setBrush(st::msgDateImgBgSelected); p.setBrush(st::msgDateImgBgSelected);
} else if (_a_iconOver.animating()) {
_a_iconOver.step(context->ms);
auto over = a_iconOver.current();
p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over));
} else { } else {
auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _savel)); auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _savel));
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, _a_iconOver.current(context->ms, over ? 1. : 0.)));
} }
{ {
@ -541,13 +522,9 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
if (selected) { if (selected) {
p.setBrush(st::msgFileInBgSelected); p.setBrush(st::msgFileInBgSelected);
} else if (_a_iconOver.animating()) {
_a_iconOver.step(context->ms);
auto over = a_iconOver.current();
p.setBrush(anim::brush(st::msgFileInBg, st::msgFileInBgOver, over));
} else { } else {
auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl)); auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl));
p.setBrush(over ? st::msgFileInBgOver : st::msgFileInBg); p.setBrush(anim::brush(st::msgFileInBg, st::msgFileInBgOver, _a_iconOver.current(context->ms, over ? 1. : 0.)));
} }
{ {
@ -752,13 +729,9 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
if (selected) { if (selected) {
p.setBrush(st::msgFileInBgSelected); p.setBrush(st::msgFileInBgSelected);
} else if (_a_iconOver.animating()) {
_a_iconOver.step(context->ms);
auto over = a_iconOver.current();
p.setBrush(anim::brush(_st.songIconBg, _st.songOverBg, over));
} else { } else {
auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl)); auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl));
p.setBrush(over ? _st.songOverBg : _st.songIconBg); p.setBrush(anim::brush(_st.songIconBg, _st.songOverBg, _a_iconOver.current(context->ms, over ? 1. : 0.)));
} }
{ {
@ -828,13 +801,9 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
if (selected) { if (selected) {
p.setBrush(wthumb ? st::msgDateImgBgSelected : documentSelectedColor(_colorIndex)); p.setBrush(wthumb ? st::msgDateImgBgSelected : documentSelectedColor(_colorIndex));
} else if (_a_iconOver.animating()) {
_a_iconOver.step(context->ms);
auto over = a_iconOver.current();
p.setBrush(anim::brush(wthumb ? st::msgDateImgBg : documentDarkColor(_colorIndex), wthumb ? st::msgDateImgBgOver : documentOverColor(_colorIndex), over));
} else { } else {
auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel);
p.setBrush(over ? (wthumb ? st::msgDateImgBgOver : documentOverColor(_colorIndex)) : (wthumb ? st::msgDateImgBg : documentDarkColor(_colorIndex))); p.setBrush(anim::brush(wthumb ? st::msgDateImgBg : documentDarkColor(_colorIndex), wthumb ? st::msgDateImgBgOver : documentOverColor(_colorIndex), _a_iconOver.current(context->ms, over ? 1. : 0.)));
} }
p.setOpacity(radialOpacity * p.opacity()); p.setOpacity(radialOpacity * p.opacity());

View file

@ -86,10 +86,7 @@ protected:
class RadialProgressItem : public ItemBase { class RadialProgressItem : public ItemBase {
public: public:
RadialProgressItem(HistoryItem *parent) : ItemBase(parent) RadialProgressItem(HistoryItem *parent) : ItemBase(parent) {
, _radial(0)
, a_iconOver(0, 0)
, _a_iconOver(animation(this, &RadialProgressItem::step_iconOver)) {
} }
RadialProgressItem(const RadialProgressItem &other) = delete; RadialProgressItem(const RadialProgressItem &other) = delete;
@ -111,7 +108,6 @@ protected:
setLinks(MakeShared<DocumentOpenClickHandler>(document), std_::move(save), MakeShared<DocumentCancelClickHandler>(document)); setLinks(MakeShared<DocumentOpenClickHandler>(document), std_::move(save), MakeShared<DocumentCancelClickHandler>(document));
} }
void step_iconOver(float64 ms, bool timer);
void step_radial(TimeMs ms, bool timer); void step_radial(TimeMs ms, bool timer);
void ensureRadial(); void ensureRadial();
@ -131,8 +127,7 @@ protected:
return false; return false;
} }
Ui::RadialAnimation *_radial; std_::unique_ptr<Ui::RadialAnimation> _radial;
anim::value a_iconOver;
Animation _a_iconOver; Animation _a_iconOver;
}; };

View file

@ -362,7 +362,7 @@ private:
QString _header; QString _header;
FloatAnimation _a_show; Animation _a_show;
Window::SlideDirection _showDirection; Window::SlideDirection _showDirection;
QPixmap _cacheUnder, _cacheOver; QPixmap _cacheUnder, _cacheOver;

View file

@ -51,7 +51,7 @@ private:
void showAll(); void showAll();
void hideAll(); void hideAll();
FloatAnimation _a_show; Animation _a_show;
bool _showBack = false; bool _showBack = false;
QPixmap _cacheUnder, _cacheOver; QPixmap _cacheUnder, _cacheOver;

View file

@ -65,7 +65,7 @@ void ChannelMembersWidget::addButton(const QString &text, ChildWidget<Ui::LeftOu
} else if (*button) { } else if (*button) {
(*button)->setText(text); (*button)->setText(text);
} else { } else {
(*button) = new Ui::LeftOutlineButton(this, text, st::defaultLeftOutlineButton); button->create(this, text, st::defaultLeftOutlineButton);
(*button)->show(); (*button)->show();
connect(*button, SIGNAL(clicked()), this, slot); connect(*button, SIGNAL(clicked()), this, slot);
} }

View file

@ -65,7 +65,7 @@ private:
Item *computeItem(PeerData *group); Item *computeItem(PeerData *group);
QMap<PeerData*, Item*> _dataMap; QMap<PeerData*, Item*> _dataMap;
FloatAnimation _height; Animation _height;
int32 _preloadGroupId = 0; int32 _preloadGroupId = 0;
mtpRequestId _preloadRequestId = 0; mtpRequestId _preloadRequestId = 0;

View file

@ -84,7 +84,7 @@ private:
ChildWidget<Ui::FlatLabel> _username = { nullptr }; ChildWidget<Ui::FlatLabel> _username = { nullptr };
ChildWidget<Ui::LeftOutlineButton> _commonGroups = { nullptr }; ChildWidget<Ui::LeftOutlineButton> _commonGroups = { nullptr };
FloatAnimation _height; Animation _height;
bool _showFinished = false; bool _showFinished = false;
bool _forceHiddenCommonGroups = false; bool _forceHiddenCommonGroups = false;

View file

@ -23,8 +23,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_profile.h" #include "styles/style_profile.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
#include "ui/toast/toast.h"
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
#include "observer_peer.h" #include "observer_peer.h"
#include "mainwindow.h"
#include "lang.h" #include "lang.h"
namespace Profile { namespace Profile {
@ -108,7 +110,9 @@ void InviteLinkWidget::refreshLink() {
} }
QApplication::clipboard()->setText(link); QApplication::clipboard()->setText(link);
Ui::showLayer(new InformBox(lang(lng_group_invite_copied))); Ui::Toast::Config toast;
toast.text = lang(lng_group_invite_copied);
Ui::Toast::Show(App::wnd(), toast);
return false; return false;
}); });
} }

View file

@ -45,7 +45,7 @@ private:
int _titleWidth, _subtitleWidth; int _titleWidth, _subtitleWidth;
QPixmap _cache; QPixmap _cache;
FloatAnimation _a_appearance; Animation _a_appearance;
bool _hiding = false; bool _hiding = false;
HideFinishCallback _hideFinishCallback; HideFinishCallback _hideFinishCallback;

View file

@ -54,7 +54,7 @@ private:
PeerData *_peer; PeerData *_peer;
bool _waiting = false; bool _waiting = false;
QPixmap _userpic, _oldUserpic; QPixmap _userpic, _oldUserpic;
FloatAnimation _a_appearance; Animation _a_appearance;
}; };

View file

@ -80,7 +80,7 @@ private:
void createChildRow(ChildWidget<Ui::WidgetSlideWrap<Widget>> &child, style::margins &margin, const style::margins &padding, Args&&... args) { void createChildRow(ChildWidget<Ui::WidgetSlideWrap<Widget>> &child, style::margins &margin, const style::margins &padding, Args&&... args) {
ChildWidget<Widget> plainChild = { nullptr }; ChildWidget<Widget> plainChild = { nullptr };
createChildRow(plainChild, margin, std_::forward<Args>(args)...); createChildRow(plainChild, margin, std_::forward<Args>(args)...);
child = new Ui::WidgetSlideWrap<Widget>(this, plainChild, padding, [this]() { child.create(this, plainChild, padding, [this]() {
rowHeightUpdated(); rowHeightUpdated();
}); });
margin.setLeft(margin.left() - padding.left()); margin.setLeft(margin.left() - padding.left());

View file

@ -41,9 +41,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace internal { namespace internal {
EmojiColorPicker::EmojiColorPicker() : TWidget() EmojiColorPicker::EmojiColorPicker(QWidget *parent) : TWidget(parent)
, a_opacity(0)
, _a_appearance(animation(this, &EmojiColorPicker::step_appearance))
, _shadow(st::defaultDropdownShadow) { , _shadow(st::defaultDropdownShadow) {
memset(_variants, 0, sizeof(_variants)); memset(_variants, 0, sizeof(_variants));
@ -78,15 +76,21 @@ void EmojiColorPicker::showEmoji(uint32 code) {
void EmojiColorPicker::paintEvent(QPaintEvent *e) { void EmojiColorPicker::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
if (!_cache.isNull()) { auto opacity = _a_opacity.current(getms(), _hiding ? 0. : 1.);
p.setOpacity(a_opacity.current()); if (opacity < 1.) {
if (opacity > 0.) {
p.setOpacity(opacity);
} else {
return;
}
} }
if (e->rect() != rect()) { if (e->rect() != rect()) {
p.setClipRect(e->rect()); p.setClipRect(e->rect());
} }
int32 w = st::defaultDropdownShadow.width(), h = st::defaultDropdownShadow.height(); auto w = st::defaultDropdownShadow.width();
QRect r = QRect(w, h, width() - 2 * w, height() - 2 * h); auto h = st::defaultDropdownShadow.height();
auto r = QRect(w, h, width() - 2 * w, height() - 2 * h);
_shadow.paint(p, r, st::defaultDropdownShadowShift); _shadow.paint(p, r, st::defaultDropdownShadowShift);
if (_cache.isNull()) { if (_cache.isNull()) {
@ -151,14 +155,9 @@ void EmojiColorPicker::mouseMoveEvent(QMouseEvent *e) {
handleMouseMove(e->globalPos()); handleMouseMove(e->globalPos());
} }
void EmojiColorPicker::step_appearance(float64 ms, bool timer) { void EmojiColorPicker::animationCallback() {
if (_cache.isNull()) { update();
_a_appearance.stop(); if (!_a_opacity.animating()) {
return;
}
float64 dt = ms / st::defaultDropdownDuration;
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap(); _cache = QPixmap();
if (_hiding) { if (_hiding) {
hide(); hide();
@ -167,17 +166,12 @@ void EmojiColorPicker::step_appearance(float64 ms, bool timer) {
_lastMousePos = QCursor::pos(); _lastMousePos = QCursor::pos();
updateSelected(); updateSelected();
} }
_a_appearance.stop();
} else {
a_opacity.update(dt, anim::linear);
} }
if (timer) update();
} }
void EmojiColorPicker::hideFast() { void EmojiColorPicker::hideFast() {
clearSelection(); clearSelection();
if (_a_appearance.animating()) _a_appearance.stop(); _a_opacity.finish();
a_opacity = anim::value();
_cache = QPixmap(); _cache = QPixmap();
hide(); hide();
emit hidden(); emit hidden();
@ -190,29 +184,23 @@ void EmojiColorPicker::hideAnimated() {
clearSelection(); clearSelection();
} }
_hiding = true; _hiding = true;
a_opacity.start(0); _a_opacity.start([this] { animationCallback(); }, 1., 0., st::defaultDropdownDuration);
_a_appearance.start();
} }
void EmojiColorPicker::showAnimated() { void EmojiColorPicker::showAnimated() {
if (_ignoreShow) return; if (_ignoreShow) return;
_hiding = false; if (!isHidden() && !_hiding) {
if (!isHidden() && a_opacity.current() == 1) {
if (_a_appearance.animating()) {
_a_appearance.stop();
_cache = QPixmap();
}
return; return;
} }
_hiding = false;
if (_cache.isNull()) { if (_cache.isNull()) {
auto w = st::defaultDropdownShadow.width(), h = st::defaultDropdownShadow.height(); auto w = st::defaultDropdownShadow.width(), h = st::defaultDropdownShadow.height();
_cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h)); _cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h));
clearSelection(); clearSelection();
} }
show(); show();
a_opacity.start(1); _a_opacity.start([this] { animationCallback(); }, 0., 1., st::defaultDropdownDuration);
_a_appearance.start();
} }
void EmojiColorPicker::clearSelection() { void EmojiColorPicker::clearSelection() {
@ -266,13 +254,14 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) {
} }
EmojiPanInner::EmojiPanInner(QWidget *parent) : TWidget(parent) EmojiPanInner::EmojiPanInner(QWidget *parent) : TWidget(parent)
, _maxHeight(int(st::emojiPanMaxHeight) - st::emojiCategory.height) { , _maxHeight(int(st::emojiPanMaxHeight) - st::emojiCategory.height)
, _picker(this) {
resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, countHeight()); resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, countHeight());
setMouseTracking(true); setMouseTracking(true);
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
_picker.hide(); _picker->hide();
_esize = EmojiSizes[EIndex + 1]; _esize = EmojiSizes[EIndex + 1];
@ -282,8 +271,8 @@ EmojiPanInner::EmojiPanInner(QWidget *parent) : TWidget(parent)
_showPickerTimer.setSingleShot(true); _showPickerTimer.setSingleShot(true);
connect(&_showPickerTimer, SIGNAL(timeout()), this, SLOT(onShowPicker())); connect(&_showPickerTimer, SIGNAL(timeout()), this, SLOT(onShowPicker()));
connect(&_picker, SIGNAL(emojiSelected(EmojiPtr)), this, SLOT(onColorSelected(EmojiPtr))); connect(_picker, SIGNAL(emojiSelected(EmojiPtr)), this, SLOT(onColorSelected(EmojiPtr)));
connect(&_picker, SIGNAL(hidden()), this, SLOT(onPickerHidden())); connect(_picker, SIGNAL(hidden()), this, SLOT(onPickerHidden()));
} }
void EmojiPanInner::setMaxHeight(int32 h) { void EmojiPanInner::setMaxHeight(int32 h) {
@ -357,7 +346,7 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) {
int32 index = i * EmojiPanPerRow + j; int32 index = i * EmojiPanPerRow + j;
if (index >= size) break; if (index >= size) break;
auto selected = (!_picker.isHidden() && c * MatrixRowShift + index == _pickerSel) || (c * MatrixRowShift + index == _selected); auto selected = (!_picker->isHidden() && c * MatrixRowShift + index == _pickerSel) || (c * MatrixRowShift + index == _selected);
QPoint w(st::emojiPanPadding + j * st::emojiPanSize.width(), y + i * st::emojiPanSize.height()); QPoint w(st::emojiPanPadding + j * st::emojiPanSize.width(), y + i * st::emojiPanSize.height());
if (selected) { if (selected) {
@ -372,8 +361,8 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) {
} }
bool EmojiPanInner::checkPickerHide() { bool EmojiPanInner::checkPickerHide() {
if (!_picker.isHidden() && _pickerSel >= 0) { if (!_picker->isHidden() && _pickerSel >= 0) {
_picker.hideAnimated(); _picker->hideAnimated();
_pickerSel = -1; _pickerSel = -1;
updateSelected(); updateSelected();
return true; return true;
@ -408,14 +397,14 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) {
_pressedSel = -1; _pressedSel = -1;
_lastMousePos = e->globalPos(); _lastMousePos = e->globalPos();
if (!_picker.isHidden()) { if (!_picker->isHidden()) {
if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) { if (_picker->rect().contains(_picker->mapFromGlobal(_lastMousePos))) {
return _picker.handleMouseRelease(QCursor::pos()); return _picker->handleMouseRelease(QCursor::pos());
} else if (_pickerSel >= 0) { } else if (_pickerSel >= 0) {
int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift;
if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) {
if (cEmojiVariants().constFind(_emojis[tab][sel]->code) != cEmojiVariants().cend()) { if (cEmojiVariants().constFind(_emojis[tab][sel]->code) != cEmojiVariants().cend()) {
_picker.hideAnimated(); _picker->hideAnimated();
_pickerSel = -1; _pickerSel = -1;
} }
} }
@ -426,7 +415,7 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) {
if (_showPickerTimer.isActive()) { if (_showPickerTimer.isActive()) {
_showPickerTimer.stop(); _showPickerTimer.stop();
_pickerSel = -1; _pickerSel = -1;
_picker.hide(); _picker->hide();
} }
if (_selected < 0 || _selected != pressed) return; if (_selected < 0 || _selected != pressed) return;
@ -438,7 +427,7 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) {
int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift;
if (sel < _emojis[tab].size()) { if (sel < _emojis[tab].size()) {
EmojiPtr emoji(_emojis[tab][sel]); EmojiPtr emoji(_emojis[tab][sel]);
if (emoji->color && !_picker.isHidden()) return; if (emoji->color && !_picker->isHidden()) return;
selectEmoji(emoji); selectEmoji(emoji);
} }
@ -493,16 +482,16 @@ void EmojiPanInner::onShowPicker() {
int32 size = (c == tab) ? (sel - (sel % EmojiPanPerRow)) : _counts[c], rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0); int32 size = (c == tab) ? (sel - (sel % EmojiPanPerRow)) : _counts[c], rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0);
y += st::emojiPanHeader + (rows * st::emojiPanSize.height()); y += st::emojiPanHeader + (rows * st::emojiPanSize.height());
} }
y -= _picker.height() - st::buttonRadius + _visibleTop; y -= _picker->height() - st::buttonRadius + _visibleTop;
if (y < 0) { if (y < 0) {
y += _picker.height() - st::buttonRadius + st::emojiPanSize.height() - st::buttonRadius; y += _picker->height() - st::buttonRadius + st::emojiPanSize.height() - st::buttonRadius;
} }
int xmax = width() - _picker.width(); int xmax = width() - _picker->width();
float64 coef = float64(sel % EmojiPanPerRow) / float64(EmojiPanPerRow - 1); float64 coef = float64(sel % EmojiPanPerRow) / float64(EmojiPanPerRow - 1);
if (rtl()) coef = 1. - coef; if (rtl()) coef = 1. - coef;
_picker.move(qRound(xmax * coef), y); _picker->move(qRound(xmax * coef), y);
_picker.showEmoji(_emojis[tab][sel]->code); _picker->showEmoji(_emojis[tab][sel]->code);
emit disableScroll(true); emit disableScroll(true);
} }
} }
@ -545,16 +534,16 @@ void EmojiPanInner::onColorSelected(EmojiPtr emoji) {
} }
} }
selectEmoji(emoji); selectEmoji(emoji);
_picker.hideAnimated(); _picker->hideAnimated();
} }
void EmojiPanInner::mouseMoveEvent(QMouseEvent *e) { void EmojiPanInner::mouseMoveEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos(); _lastMousePos = e->globalPos();
if (!_picker.isHidden()) { if (!_picker->isHidden()) {
if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) { if (_picker->rect().contains(_picker->mapFromGlobal(_lastMousePos))) {
return _picker.handleMouseMove(QCursor::pos()); return _picker->handleMouseMove(QCursor::pos());
} else { } else {
_picker.clearSelection(); _picker->clearSelection();
} }
} }
updateSelected(); updateSelected();
@ -593,8 +582,8 @@ DBIEmojiTab EmojiPanInner::currentTab(int yOffset) const {
} }
void EmojiPanInner::hideFinish() { void EmojiPanInner::hideFinish() {
if (!_picker.isHidden()) { if (!_picker->isHidden()) {
_picker.hideFast(); _picker->hideFast();
_pickerSel = -1; _pickerSel = -1;
clearSelection(); clearSelection();
} }
@ -612,8 +601,8 @@ void EmojiPanInner::refreshRecent() {
} }
void EmojiPanInner::fillPanels(QVector<EmojiPanel*> &panels) { void EmojiPanInner::fillPanels(QVector<EmojiPanel*> &panels) {
if (_picker.parentWidget() != parentWidget()) { if (_picker->parentWidget() != parentWidget()) {
_picker.setParent(parentWidget()); _picker->setParent(parentWidget());
} }
for (int32 i = 0; i < panels.size(); ++i) { for (int32 i = 0; i < panels.size(); ++i) {
panels.at(i)->hide(); panels.at(i)->hide();
@ -630,7 +619,7 @@ void EmojiPanInner::fillPanels(QVector<EmojiPanel*> &panels) {
panels.back()->show(); panels.back()->show();
y += st::emojiPanHeader + rows * st::emojiPanSize.height(); y += st::emojiPanHeader + rows * st::emojiPanSize.height();
} }
_picker.raise(); _picker->raise();
} }
void EmojiPanInner::refreshPanels(QVector<EmojiPanel*> &panels) { void EmojiPanInner::refreshPanels(QVector<EmojiPanel*> &panels) {
@ -684,11 +673,11 @@ void EmojiPanInner::setSelected(int newSelected) {
updateSelected(); updateSelected();
setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default); setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default);
if (_selected >= 0 && !_picker.isHidden()) { if (_selected >= 0 && !_picker->isHidden()) {
if (_selected != _pickerSel) { if (_selected != _pickerSel) {
_picker.hideAnimated(); _picker->hideAnimated();
} else { } else {
_picker.showAnimated(); _picker->showAnimated();
} }
} }
} }
@ -1633,14 +1622,15 @@ void StickerPanInner::clearInlineRowsPanel() {
void StickerPanInner::refreshSwitchPmButton(const InlineCacheEntry *entry) { void StickerPanInner::refreshSwitchPmButton(const InlineCacheEntry *entry) {
if (!entry || entry->switchPmText.isEmpty()) { if (!entry || entry->switchPmText.isEmpty()) {
_switchPmButton.reset(); _switchPmButton.destroy();
_switchPmStartToken.clear(); _switchPmStartToken.clear();
} else { } else {
if (!_switchPmButton) { if (!_switchPmButton) {
_switchPmButton = std_::make_unique<Ui::RoundButton>(this, QString(), st::switchPmButton); _switchPmButton.create(this, QString(), st::switchPmButton);
_switchPmButton->show(); _switchPmButton->show();
_switchPmButton->move(st::inlineResultsLeft - st::buttonRadius, st::emojiPanHeader); _switchPmButton->move(st::inlineResultsLeft - st::buttonRadius, st::emojiPanHeader);
connect(_switchPmButton.get(), SIGNAL(clicked()), this, SLOT(onSwitchPm())); _switchPmButton->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
connect(_switchPmButton, SIGNAL(clicked()), this, SLOT(onSwitchPm()));
} }
_switchPmButton->setText(entry->switchPmText); // doesn't perform text.toUpper() _switchPmButton->setText(entry->switchPmText); // doesn't perform text.toUpper()
_switchPmStartToken = entry->switchPmStartToken; _switchPmStartToken = entry->switchPmStartToken;

View file

@ -63,7 +63,7 @@ class EmojiColorPicker : public TWidget {
Q_OBJECT Q_OBJECT
public: public:
EmojiColorPicker(); EmojiColorPicker(QWidget *parent);
void showEmoji(uint32 code); void showEmoji(uint32 code);
@ -90,7 +90,7 @@ protected:
void mouseMoveEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override;
private: private:
void step_appearance(float64 ms, bool timer); void animationCallback();
void drawVariant(Painter &p, int variant); void drawVariant(Painter &p, int variant);
@ -107,9 +107,7 @@ private:
bool _hiding = false; bool _hiding = false;
QPixmap _cache; QPixmap _cache;
Animation _a_opacity;
anim::value a_opacity;
Animation _a_appearance;
QTimer _hideTimer; QTimer _hideTimer;
@ -192,8 +190,9 @@ private:
int _pickerSel = -1; int _pickerSel = -1;
QPoint _lastMousePos; QPoint _lastMousePos;
EmojiColorPicker _picker; ChildWidget<EmojiColorPicker> _picker;
QTimer _showPickerTimer; QTimer _showPickerTimer;
}; };
struct StickerIcon { struct StickerIcon {
@ -368,14 +367,12 @@ private:
QTimer _updateInlineItems; QTimer _updateInlineItems;
bool _inlineWithThumb = false; bool _inlineWithThumb = false;
std_::unique_ptr<Ui::RoundButton> _switchPmButton; ChildWidget<Ui::RoundButton> _switchPmButton = { nullptr };
QString _switchPmStartToken; QString _switchPmStartToken;
typedef QVector<InlineItem*> InlineItems; typedef QVector<InlineItem*> InlineItems;
struct InlineRow { struct InlineRow {
InlineRow() : height(0) { int height = 0;
}
int32 height;
InlineItems items; InlineItems items;
}; };
typedef QVector<InlineRow> InlineRows; typedef QVector<InlineRow> InlineRows;
@ -629,17 +626,17 @@ private:
Ui::PanelAnimation::Origin _origin = Ui::PanelAnimation::Origin::BottomRight; Ui::PanelAnimation::Origin _origin = Ui::PanelAnimation::Origin::BottomRight;
std_::unique_ptr<Ui::PanelAnimation> _showAnimation; std_::unique_ptr<Ui::PanelAnimation> _showAnimation;
FloatAnimation _a_show; Animation _a_show;
bool _hiding = false; bool _hiding = false;
QPixmap _cache; QPixmap _cache;
FloatAnimation _a_opacity; Animation _a_opacity;
QTimer _hideTimer; QTimer _hideTimer;
bool _inPanelGrab = false; bool _inPanelGrab = false;
class SlideAnimation; class SlideAnimation;
std_::unique_ptr<SlideAnimation> _slideAnimation; std_::unique_ptr<SlideAnimation> _slideAnimation;
FloatAnimation _a_slide; Animation _a_slide;
ChildWidget<Ui::IconButton> _recent; ChildWidget<Ui::IconButton> _recent;
ChildWidget<Ui::IconButton> _people; ChildWidget<Ui::IconButton> _people;
@ -655,7 +652,7 @@ private:
int _iconSel = 0; int _iconSel = 0;
int _iconDown = -1; int _iconDown = -1;
bool _iconsDragging = false; bool _iconsDragging = false;
Animation _a_icons; BasicAnimation _a_icons;
QPoint _iconsMousePos, _iconsMouseDown; QPoint _iconsMousePos, _iconsMouseDown;
int _iconsLeft = 0; int _iconsLeft = 0;
int _iconsTop = 0; int _iconsTop = 0;

View file

@ -112,7 +112,7 @@ void registerClipManager(Media::Clip::Manager *manager) {
} // anim } // anim
void Animation::start() { void BasicAnimation::start() {
if (!_manager) return; if (!_manager) return;
_callbacks.start(); _callbacks.start();
@ -120,7 +120,7 @@ void Animation::start() {
_animating = true; _animating = true;
} }
void Animation::stop() { void BasicAnimation::stop() {
if (!_manager) return; if (!_manager) return;
_animating = false; _animating = false;
@ -132,7 +132,7 @@ AnimationManager::AnimationManager() : _timer(this), _iterating(false) {
connect(&_timer, SIGNAL(timeout()), this, SLOT(timeout())); connect(&_timer, SIGNAL(timeout()), this, SLOT(timeout()));
} }
void AnimationManager::start(Animation *obj) { void AnimationManager::start(BasicAnimation *obj) {
if (_iterating) { if (_iterating) {
_starting.insert(obj); _starting.insert(obj);
if (!_stopping.isEmpty()) { if (!_stopping.isEmpty()) {
@ -146,7 +146,7 @@ void AnimationManager::start(Animation *obj) {
} }
} }
void AnimationManager::stop(Animation *obj) { void AnimationManager::stop(BasicAnimation *obj) {
if (_iterating) { if (_iterating) {
_stopping.insert(obj); _stopping.insert(obj);
if (!_starting.isEmpty()) { if (!_starting.isEmpty()) {

View file

@ -383,12 +383,12 @@ FORCE_INLINE QBrush brush(const style::color &a, const style::color &b, float64
}; };
class Animation; class BasicAnimation;
class AnimationImplementation { class AnimationImplementation {
public: public:
virtual void start() {} virtual void start() {}
virtual void step(Animation *a, TimeMs ms, bool timer) = 0; virtual void step(BasicAnimation *a, TimeMs ms, bool timer) = 0;
virtual ~AnimationImplementation() {} virtual ~AnimationImplementation() {}
}; };
@ -407,7 +407,7 @@ public:
} }
void start() { _implementation->start(); } void start() { _implementation->start(); }
void step(Animation *a, TimeMs ms, bool timer) { _implementation->step(a, ms, timer); } void step(BasicAnimation *a, TimeMs ms, bool timer) { _implementation->step(a, ms, timer); }
~AnimationCallbacks() { delete base::take(_implementation); } ~AnimationCallbacks() { delete base::take(_implementation); }
private: private:
@ -415,9 +415,9 @@ private:
}; };
class Animation { class BasicAnimation {
public: public:
Animation(AnimationCallbacks &&callbacks) BasicAnimation(AnimationCallbacks &&callbacks)
: _callbacks(std_::move(callbacks)) : _callbacks(std_::move(callbacks))
, _animating(false) { , _animating(false) {
} }
@ -437,7 +437,7 @@ public:
return _animating; return _animating;
} }
~Animation() { ~BasicAnimation() {
if (_animating) stop(); if (_animating) stop();
} }
@ -459,7 +459,7 @@ public:
_started = float64(getms()); _started = float64(getms());
} }
void step(Animation *a, TimeMs ms, bool timer) { void step(BasicAnimation *a, TimeMs ms, bool timer) {
(_obj->*_method)(ms - _started, timer); (_obj->*_method)(ms - _started, timer);
} }
@ -482,7 +482,7 @@ public:
AnimationCallbacksAbsolute(Type *obj, Method method) : _obj(obj), _method(method) { AnimationCallbacksAbsolute(Type *obj, Method method) : _obj(obj), _method(method) {
} }
void step(Animation *a, TimeMs ms, bool timer) { void step(BasicAnimation *a, TimeMs ms, bool timer) {
(_obj->*_method)(ms, timer); (_obj->*_method)(ms, timer);
} }
@ -508,7 +508,7 @@ public:
_started = float64(getms()); _started = float64(getms());
} }
void step(Animation *a, TimeMs ms, bool timer) { void step(BasicAnimation *a, TimeMs ms, bool timer) {
(_obj->*_method)(_param, ms - _started, timer); (_obj->*_method)(_param, ms - _started, timer);
} }
@ -532,7 +532,7 @@ public:
AnimationCallbacksAbsoluteWithParam(Param param, Type *obj, Method method) : _param(param), _obj(obj), _method(method) { AnimationCallbacksAbsoluteWithParam(Param param, Type *obj, Method method) : _param(param), _obj(obj), _method(method) {
} }
void step(Animation *a, TimeMs ms, bool timer) { void step(BasicAnimation *a, TimeMs ms, bool timer) {
(_obj->*_method)(_param, ms, timer); (_obj->*_method)(_param, ms, timer);
} }
@ -547,12 +547,8 @@ AnimationCallbacks animation(Param param, Type *obj, typename AnimationCallbacks
return AnimationCallbacks(new AnimationCallbacksAbsoluteWithParam<Type, Param>(param, obj, method)); return AnimationCallbacks(new AnimationCallbacksAbsoluteWithParam<Type, Param>(param, obj, method));
} }
template <typename AnimType> class Animation {
class SimpleAnimation {
public: public:
using ValueType = typename AnimType::ValueType;
using Callback = base::lambda<void()>;
void step(TimeMs ms) { void step(TimeMs ms) {
if (_data) { if (_data) {
_data->a_animation.step(ms); _data->a_animation.step(ms);
@ -576,24 +572,32 @@ public:
return animating(); return animating();
} }
ValueType current() const { float64 current() const {
t_assert(_data != nullptr); t_assert(_data != nullptr);
return _data->value.current(); return _data->value.current();
} }
ValueType current(const ValueType &def) const { float64 current(float64 def) const {
return _data ? current() : def; return animating() ? current() : def;
} }
ValueType current(TimeMs ms, const ValueType &def) { float64 current(TimeMs ms, float64 def) {
return animating(ms) ? current() : def; return animating(ms) ? current() : def;
} }
static constexpr auto kLongAnimationDuration = 1000;
template <typename Lambda> template <typename Lambda>
void start(Lambda &&updateCallback, const ValueType &from, const ValueType &to, float64 duration, const anim::transition &transition = anim::linear) { void start(Lambda &&updateCallback, float64 from, float64 to, float64 duration, const anim::transition &transition = anim::linear) {
auto isLong = (duration >= kLongAnimationDuration);
if (_data) { if (_data) {
if (!isLong) {
_data->pause.restart(); _data->pause.restart();
}
} else { } else {
_data = std_::make_unique<Data>(from, std_::forward<Lambda>(updateCallback)); _data = std_::make_unique<Data>(from, std_::forward<Lambda>(updateCallback));
} }
if (isLong) {
_data->pause.release();
}
_data->value.start(to); _data->value.start(to);
_data->duration = duration; _data->duration = duration;
_data->transition = transition; _data->transition = transition;
@ -611,12 +615,12 @@ public:
private: private:
struct Data { struct Data {
template <typename Lambda, typename = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::value>> template <typename Lambda, typename = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::value>>
Data(const ValueType &from, Lambda &&updateCallback) Data(float64 from, Lambda &&updateCallback)
: value(from, from) : value(from, from)
, a_animation(animation(this, &Data::step)) , a_animation(animation(this, &Data::step))
, updateCallback(std_::move(updateCallback)) { , updateCallback(std_::move(updateCallback)) {
} }
Data(const ValueType &from, const base::lambda_copy<void()> &updateCallback) Data(float64 from, const base::lambda_copy<void()> &updateCallback)
: value(from, from) : value(from, from)
, a_animation(animation(this, &Data::step)) , a_animation(animation(this, &Data::step))
, updateCallback(base::lambda_copy<void()>(updateCallback)) { , updateCallback(base::lambda_copy<void()>(updateCallback)) {
@ -633,9 +637,9 @@ private:
updateCallback(); updateCallback();
} }
AnimType value; anim::value value;
Animation a_animation; BasicAnimation a_animation;
Callback updateCallback; base::lambda<void()> updateCallback;
float64 duration = 0.; float64 duration = 0.;
anim::transition transition = anim::linear; anim::transition transition = anim::linear;
MTP::PauseHolder pause; MTP::PauseHolder pause;
@ -644,16 +648,14 @@ private:
}; };
using FloatAnimation = SimpleAnimation<anim::value>;
class AnimationManager : public QObject { class AnimationManager : public QObject {
Q_OBJECT Q_OBJECT
public: public:
AnimationManager(); AnimationManager();
void start(Animation *obj); void start(BasicAnimation *obj);
void stop(Animation *obj); void stop(BasicAnimation *obj);
public slots: public slots:
void timeout(); void timeout();
@ -661,7 +663,7 @@ public slots:
void clipCallback(Media::Clip::Reader *reader, qint32 threadIndex, qint32 notification); void clipCallback(Media::Clip::Reader *reader, qint32 threadIndex, qint32 notification);
private: private:
using AnimatingObjects = OrderedSet<Animation*>; using AnimatingObjects = OrderedSet<BasicAnimation*>;
AnimatingObjects _objects, _starting, _stopping; AnimatingObjects _objects, _starting, _stopping;
QTimer _timer; QTimer _timer;
bool _iterating; bool _iterating;

View file

@ -28,9 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui { namespace Ui {
HistoryDownButton::HistoryDownButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple) HistoryDownButton::HistoryDownButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple)
, _st(st) , _st(st) {
//, a_arrowOpacity(st::historyAttachEmoji.opacity, st::historyAttachEmoji.opacity)
, _a_arrowOver(animation(this, &HistoryDownButton::step_arrowOver)) {
resize(_st.width, _st.height); resize(_st.width, _st.height);
setCursor(style::cur_pointer); setCursor(style::cur_pointer);
@ -49,12 +47,6 @@ void HistoryDownButton::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
auto ms = getms(); auto ms = getms();
auto opacity = _a_show.current(ms, _shown ? 1. : 0.);
if (opacity == 0.) {
if (!_shown) hide();
return;
}
p.setOpacity(opacity);
auto over = isOver(); auto over = isOver();
auto down = isDown(); auto down = isDown();
((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width()); ((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width());
@ -80,44 +72,6 @@ void HistoryDownButton::setUnreadCount(int unreadCount) {
update(); update();
} }
bool HistoryDownButton::hidden() const {
return !_shown;
}
void HistoryDownButton::showAnimated() {
if (_shown) return;
if (isHidden()) show();
toggleAnimated();
}
void HistoryDownButton::hideAnimated() {
if (!_shown) return;
toggleAnimated();
}
void HistoryDownButton::toggleAnimated() {
_shown = !_shown;
float64 from = _shown ? 0. : 1., to = _shown ? 1. : 0.;
_a_show.start([this] { update(); }, from, to, st::historyToDownDuration);
}
void HistoryDownButton::finishAnimation() {
_a_show.finish();
setVisible(_shown);
}
void HistoryDownButton::step_arrowOver(float64 ms, bool timer) {
float64 dt = ms / st::historyAttachEmoji.duration;
if (dt >= 1) {
_a_arrowOver.stop();
a_arrowOpacity.finish();
} else {
a_arrowOpacity.update(dt, anim::linear);
}
if (timer) update();
}
EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : RippleButton(parent, st.ripple) EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : RippleButton(parent, st.ripple)
, _st(st) , _st(st)
, _a_loading(animation(this, &EmojiButton::step_loading)) { , _a_loading(animation(this, &EmojiButton::step_loading)) {

View file

@ -34,13 +34,6 @@ public:
return _unreadCount; return _unreadCount;
} }
bool hidden() const;
void showAnimated();
void hideAnimated();
void finishAnimation();
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
@ -48,18 +41,8 @@ protected:
QPoint prepareRippleStartPosition() const override; QPoint prepareRippleStartPosition() const override;
private: private:
void toggleAnimated();
void step_arrowOver(float64 ms, bool timer);
const style::TwoIconButton &_st; const style::TwoIconButton &_st;
bool _shown = false;
anim::value a_arrowOpacity;
Animation _a_arrowOver;
FloatAnimation _a_show;
int _unreadCount = 0; int _unreadCount = 0;
}; };
@ -81,8 +64,8 @@ private:
const style::IconButton &_st; const style::IconButton &_st;
bool _loading = false; bool _loading = false;
FloatAnimation a_loading; Animation a_loading;
Animation _a_loading; BasicAnimation _a_loading;
void step_loading(TimeMs ms, bool timer) { void step_loading(TimeMs ms, bool timer) {
if (timer) { if (timer) {

View file

@ -51,7 +51,7 @@ private:
float64 _opacity = 0.; float64 _opacity = 0.;
anim::value a_arcEnd; anim::value a_arcEnd;
anim::value a_arcStart; anim::value a_arcStart;
Animation _animation; BasicAnimation _animation;
}; };

View file

@ -46,8 +46,8 @@ private:
int _radiusTo = 0; int _radiusTo = 0;
bool _hiding = false; bool _hiding = false;
FloatAnimation _show; Animation _show;
FloatAnimation _hide; Animation _hide;
QPixmap _cache; QPixmap _cache;
QImage _frame; QImage _frame;

View file

@ -42,8 +42,8 @@ public:
private: private:
struct Icon { struct Icon {
FloatAnimation fadeIn; Animation fadeIn;
FloatAnimation fadeOut; Animation fadeOut;
QPixmap wideCheckCache; QPixmap wideCheckCache;
}; };
void removeFadeOutedIcons(); void removeFadeOutedIcons();
@ -87,7 +87,7 @@ private:
PaintRoundImage _paintRoundImage; PaintRoundImage _paintRoundImage;
QPixmap _wideCache; QPixmap _wideCache;
FloatAnimation _selection; Animation _selection;
RoundCheckbox _check; RoundCheckbox _check;

View file

@ -40,7 +40,7 @@ public:
} }
private: private:
FloatAnimation _animation; Animation _animation;
QPixmap _leftSnapshot; QPixmap _leftSnapshot;
QPixmap _rightSnapshot; QPixmap _rightSnapshot;
bool _slideLeft = false; bool _slideLeft = false;

View file

@ -29,7 +29,7 @@ FadeAnimation::FadeAnimation(TWidget *widget) : _widget(widget) {
bool FadeAnimation::paint(Painter &p) { bool FadeAnimation::paint(Painter &p) {
if (_cache.isNull()) return false; if (_cache.isNull()) return false;
p.setOpacity(_animation.current(_visible ? 1. : 0.)); p.setOpacity(_animation.current(getms(), _visible ? 1. : 0.));
p.drawPixmap(0, 0, _cache); p.drawPixmap(0, 0, _cache);
return true; return true;
} }

View file

@ -52,7 +52,7 @@ private:
void updateCallback(); void updateCallback();
TWidget *_widget; TWidget *_widget;
FloatAnimation _animation; Animation _animation;
QPixmap _cache; QPixmap _cache;
bool _visible = false; bool _visible = false;

View file

@ -31,8 +31,7 @@ WidgetSlideWrap<TWidget>::WidgetSlideWrap(QWidget *parent
, _entity(entity) , _entity(entity)
, _padding(entityPadding) , _padding(entityPadding)
, _duration(duration) , _duration(duration)
, _updateCallback(std_::move(updateCallback)) , _updateCallback(std_::move(updateCallback)) {
, _a_height(animation(this, &WidgetSlideWrap<TWidget>::step_height)) {
_entity->setParent(this); _entity->setParent(this);
auto margins = getMargins(); auto margins = getMargins();
_entity->moveToLeft(margins.left() + _padding.left(), margins.top() + _padding.top()); _entity->moveToLeft(margins.left() + _padding.left(), margins.top() + _padding.top());
@ -50,12 +49,9 @@ void WidgetSlideWrap<TWidget>::slideUp() {
} }
if (_a_height.animating()) { if (_a_height.animating()) {
if (_hiding) return; if (_hiding) return;
} else {
a_height = anim::value(_realSize.height());
} }
a_height.start(0.);
_hiding = true; _hiding = true;
_a_height.start(); _a_height.start([this] { animationCallback(); }, _realSize.height(), 0., _duration);
} }
void WidgetSlideWrap<TWidget>::slideDown() { void WidgetSlideWrap<TWidget>::slideDown() {
@ -69,15 +65,14 @@ void WidgetSlideWrap<TWidget>::slideDown() {
if (_a_height.animating()) { if (_a_height.animating()) {
if (!_hiding) return; if (!_hiding) return;
} }
a_height.start(_realSize.height());
_forceHeight = qRound(a_height.current());
_hiding = false; _hiding = false;
_a_height.start(); _forceHeight = qRound(_a_height.current(0.));
_a_height.start([this] { animationCallback(); }, 0., _realSize.height(), _duration);
} }
void WidgetSlideWrap<TWidget>::showFast() { void WidgetSlideWrap<TWidget>::showFast() {
show(); show();
_a_height.stop(); _a_height.finish();
_forceHeight = -1; _forceHeight = -1;
resizeToWidth(_realSize.width()); resizeToWidth(_realSize.width());
if (_updateCallback) { if (_updateCallback) {
@ -86,8 +81,7 @@ void WidgetSlideWrap<TWidget>::showFast() {
} }
void WidgetSlideWrap<TWidget>::hideFast() { void WidgetSlideWrap<TWidget>::hideFast() {
_a_height.stop(); _a_height.finish();
a_height = anim::value();
_forceHeight = 0; _forceHeight = 0;
resizeToWidth(_realSize.width()); resizeToWidth(_realSize.width());
hide(); hide();
@ -135,18 +129,13 @@ int WidgetSlideWrap<TWidget>::resizeGetHeight(int newWidth) {
return _realSize.height(); return _realSize.height();
} }
void WidgetSlideWrap<TWidget>::step_height(float64 ms, bool timer) { void WidgetSlideWrap<TWidget>::animationCallback() {
auto dt = ms / _duration; _forceHeight = qRound(_a_height.current(_hiding ? 0 : -1));
if (dt >= 1) { resizeToWidth(_realSize.width());
a_height.finish(); if (!_a_height.animating()) {
_a_height.stop();
_forceHeight = _hiding ? 0 : -1; _forceHeight = _hiding ? 0 : -1;
if (_hiding) hide(); if (_hiding) hide();
} else {
a_height.update(dt, anim::linear);
_forceHeight = qRound(a_height.current());
} }
resizeToWidth(_realSize.width());
if (_updateCallback) { if (_updateCallback) {
_updateCallback(); _updateCallback();
} }

View file

@ -56,7 +56,7 @@ protected:
int resizeGetHeight(int newWidth) override; int resizeGetHeight(int newWidth) override;
private: private:
void step_height(float64 ms, bool timer); void animationCallback();
TWidget *_entity; TWidget *_entity;
bool _inResizeToWidth = false; bool _inResizeToWidth = false;
@ -66,7 +66,6 @@ private:
style::size _realSize; style::size _realSize;
int _forceHeight = -1; int _forceHeight = -1;
anim::value a_height;
Animation _a_height; Animation _a_height;
bool _hiding = false; bool _hiding = false;

View file

@ -28,10 +28,9 @@ namespace Ui {
namespace Toast { namespace Toast {
Instance::Instance(const Config &config, QWidget *widgetParent, const Private &) Instance::Instance(const Config &config, QWidget *widgetParent, const Private &)
: _a_fade(animation(this, &Instance::step_fade)) : _hideAtMs(getms(true) + config.durationMs) {
, _hideAtMs(getms(true) + config.durationMs) {
_widget = std_::make_unique<internal::Widget>(widgetParent, config); _widget = std_::make_unique<internal::Widget>(widgetParent, config);
_a_fade.start(); _a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::toastFadeInDuration);
} }
void Show(QWidget *parent, const Config &config) { void Show(QWidget *parent, const Config &config) {
@ -41,9 +40,19 @@ void Show(QWidget *parent, const Config &config) {
} }
} }
void Instance::opacityAnimationCallback() {
_widget->setShownLevel(_a_opacity.current(_hiding ? 0. : 1.));
_widget->update();
if (!_a_opacity.animating()) {
if (_hiding) {
hide();
}
}
}
void Instance::fadeOut() { void Instance::fadeOut() {
_fadingOut = true; _hiding = true;
_a_fade.start(); _a_opacity.start([this] { opacityAnimationCallback(); }, 1., 0., st::toastFadeOutDuration);
} }
void Instance::hide() { void Instance::hide() {
@ -51,27 +60,5 @@ void Instance::hide() {
_widget->deleteLater(); _widget->deleteLater();
} }
void Instance::step_fade(float64 ms, bool timer) {
if (timer) {
_widget->update();
}
if (_fadingOut) {
if (ms >= st::toastFadeOutDuration) {
hide();
} else {
float64 dt = ms / st::toastFadeOutDuration;
_widget->setShownLevel(1. - dt);
}
} else {
if (ms >= st::toastFadeInDuration) {
_widget->setShownLevel(1.);
_a_fade.stop();
} else {
float64 dt = ms / st::toastFadeInDuration;
_widget->setShownLevel(dt);
}
}
}
} // namespace Toast } // namespace Toast
} // namespace Ui } // namespace Ui

View file

@ -49,9 +49,10 @@ public:
void hide(); void hide();
private: private:
void step_fade(float64 ms, bool timer); void opacityAnimationCallback();
bool _fadingOut = false;
Animation _a_fade; bool _hiding = false;
Animation _a_opacity;
const TimeMs _hideAtMs; const TimeMs _hideAtMs;

View file

@ -135,9 +135,7 @@ RippleButton::~RippleButton() = default;
FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st) : RippleButton(parent, st.ripple) FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st) : RippleButton(parent, st.ripple)
, _text(text) , _text(text)
, _st(st) , _st(st) {
, a_over(0)
, _a_appearance(animation(this, &FlatButton::step_appearance)) {
if (_st.width < 0) { if (_st.width < 0) {
_width = textWidth() - _st.width; _width = textWidth() - _st.width;
} else if (!_st.width) { } else if (!_st.width) {
@ -148,15 +146,6 @@ FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatBu
resize(_width, _st.height); resize(_width, _st.height);
} }
void FlatButton::setOpacity(float64 o) {
_opacity = o;
update();
}
float64 FlatButton::opacity() const {
return _opacity;
}
void FlatButton::setText(const QString &text) { void FlatButton::setText(const QString &text) {
_text = text; _text = text;
update(); update();
@ -176,44 +165,22 @@ int32 FlatButton::textWidth() const {
return _st.font->width(_text); return _st.font->width(_text);
} }
void FlatButton::step_appearance(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
if (dt >= 1) {
_a_appearance.stop();
a_over.finish();
} else {
a_over.update(dt, anim::linear);
}
if (timer) update();
}
void FlatButton::onStateChanged(State was, StateChangeSource source) { void FlatButton::onStateChanged(State was, StateChangeSource source) {
RippleButton::onStateChanged(was, source); RippleButton::onStateChanged(was, source);
a_over.start(isOver() ? 1. : 0.);
if (source == StateChangeSource::ByUser || source == StateChangeSource::ByPress) {
_a_appearance.stop();
a_over.finish();
update(); update();
} else {
_a_appearance.start();
}
} }
void FlatButton::paintEvent(QPaintEvent *e) { void FlatButton::paintEvent(QPaintEvent *e) {
QPainter p(this); QPainter p(this);
QRect r(0, height() - _st.height, width(), _st.height); QRect r(0, height() - _st.height, width(), _st.height);
p.fillRect(r, isOver() ? _st.overBgColor : _st.bgColor);
p.setOpacity(_opacity); paintRipple(p, 0, 0, getms());
p.fillRect(r, anim::brush(_st.bgColor, _st.overBgColor, a_over.current()));
auto ms = getms();
paintRipple(p, 0, 0, ms);
p.setFont(isOver() ? _st.overFont : _st.font); p.setFont(isOver() ? _st.overFont : _st.font);
p.setRenderHint(QPainter::TextAntialiasing); p.setRenderHint(QPainter::TextAntialiasing);
p.setPen(anim::pen(_st.color, _st.overColor, a_over.current())); p.setPen(isOver() ? _st.overColor : _st.color);
r.setTop(_st.textTop); r.setTop(_st.textTop);
p.drawText(r, _text, style::al_top); p.drawText(r, _text, style::al_top);
@ -269,8 +236,12 @@ void RoundButton::resizeToText() {
resize(innerWidth - _st.width + _st.padding.left() + _st.padding.right(), _st.height + _st.padding.top() + _st.padding.bottom()); resize(innerWidth - _st.width + _st.padding.left() + _st.padding.right(), _st.height + _st.padding.top() + _st.padding.bottom());
} else { } else {
if (_st.width < innerWidth + (_st.height - _st.font->height)) { if (_st.width < innerWidth + (_st.height - _st.font->height)) {
_text = _st.font->elided(_fullText, qMax(_st.width - (_st.height - _st.font->height), 1)); auto fullText = _fullText;
innerWidth = _st.font->width(_text); if (_transform == TextTransform::ToUpper) {
fullText = std_::move(fullText).toUpper();
}
_text = _st.font->elided(fullText, qMax(_st.width - (_st.height - _st.font->height), 1));
_textWidth = _st.font->width(_text);
} }
resize(_st.width + _st.padding.left() + _st.padding.right(), _st.height + _st.padding.top() + _st.padding.bottom()); resize(_st.width + _st.padding.left() + _st.padding.right(), _st.height + _st.padding.top() + _st.padding.bottom());
} }

View file

@ -90,8 +90,6 @@ class FlatButton : public RippleButton {
public: public:
FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st); FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st);
void step_appearance(float64 ms, bool timer);
void setText(const QString &text); void setText(const QString &text);
void setWidth(int32 w); void setWidth(int32 w);
@ -103,19 +101,11 @@ protected:
void onStateChanged(State was, StateChangeSource source) override; void onStateChanged(State was, StateChangeSource source) override;
private: private:
void setOpacity(float64 o);
float64 opacity() const;
QString _text, _textForAutoSize; QString _text, _textForAutoSize;
int _width; int _width;
const style::FlatButton &_st; const style::FlatButton &_st;
anim::value a_over;
Animation _a_appearance;
float64 _opacity = 1.;
}; };
class RoundButton : public RippleButton { class RoundButton : public RippleButton {
@ -181,7 +171,7 @@ private:
const style::icon *_iconOverrideOver = nullptr; const style::icon *_iconOverrideOver = nullptr;
const style::color *_rippleColorOverride = nullptr; const style::color *_rippleColorOverride = nullptr;
FloatAnimation _a_over; Animation _a_over;
}; };
@ -231,7 +221,7 @@ private:
const style::CrossButton &_st; const style::CrossButton &_st;
bool _shown = false; bool _shown = false;
FloatAnimation _a_show; Animation _a_show;
}; };

View file

@ -68,7 +68,7 @@ private:
QRect _checkRect; QRect _checkRect;
bool _checked; bool _checked;
FloatAnimation _a_checked; Animation _a_checked;
}; };
@ -117,7 +117,7 @@ private:
QRect _checkRect; QRect _checkRect;
bool _checked; bool _checked;
FloatAnimation _a_checked; Animation _a_checked;
void *_group; void *_group;
int32 _value; int32 _value;

View file

@ -105,10 +105,12 @@ private:
Callback _changeFinishedCallback; Callback _changeFinishedCallback;
bool _over = false; bool _over = false;
FloatAnimation _a_over; Animation _a_over;
// This can animate for a very long time (like in music playing),
// so it should be a BasicAnimation, not an Animation.
anim::value a_value; anim::value a_value;
Animation _a_value; BasicAnimation _a_value;
bool _mouseDown = false; bool _mouseDown = false;
float64 _downValue = 0.; float64 _downValue = 0.;

View file

@ -92,7 +92,7 @@ private:
int _pressed = -1; int _pressed = -1;
int _selected = 0; int _selected = 0;
FloatAnimation _a_left; Animation _a_left;
int _timerId = -1; int _timerId = -1;
TimeMs _callbackAfterMs = 0; TimeMs _callbackAfterMs = 0;

View file

@ -108,11 +108,11 @@ private:
PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft; PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft;
std_::unique_ptr<PanelAnimation> _showAnimation; std_::unique_ptr<PanelAnimation> _showAnimation;
FloatAnimation _a_show; Animation _a_show;
bool _hiding = false; bool _hiding = false;
QPixmap _cache; QPixmap _cache;
FloatAnimation _a_opacity; Animation _a_opacity;
QTimer _hideTimer; QTimer _hideTimer;
bool _ignoreShowEvents = false; bool _ignoreShowEvents = false;

View file

@ -1717,9 +1717,6 @@ InputArea::InputArea(QWidget *parent, const style::InputArea &st, const QString
, _placeholderFull(ph) , _placeholderFull(ph)
, _placeholderVisible(val.isEmpty()) , _placeholderVisible(val.isEmpty())
, a_borderOpacityActive(0)
, a_borderFgActive(0)
, a_borderFgError(0)
, _a_border(animation(this, &InputArea::step_border)) , _a_border(animation(this, &InputArea::step_border))
, _focused(false) , _focused(false)
@ -2412,9 +2409,6 @@ InputField::InputField(QWidget *parent, const style::InputField &st, const QStri
, _placeholderFull(ph) , _placeholderFull(ph)
, _placeholderVisible(val.isEmpty()) , _placeholderVisible(val.isEmpty())
, a_borderOpacityActive(0)
, a_borderFgActive(0)
, a_borderFgError(0)
, _a_border(animation(this, &InputField::step_border)) , _a_border(animation(this, &InputField::step_border))
, _focused(false) , _focused(false)
@ -3125,9 +3119,6 @@ MaskedInputField::MaskedInputField(QWidget *parent, const style::InputField &st,
, _placeholderVisible(val.isEmpty()) , _placeholderVisible(val.isEmpty())
, _placeholderFast(false) , _placeholderFast(false)
, a_borderOpacityActive(0)
, a_borderFgActive(0)
, a_borderFgError(0)
, _a_border(animation(this, &MaskedInputField::step_border)) , _a_border(animation(this, &MaskedInputField::step_border))
, _focused(false) , _focused(false)

View file

@ -190,8 +190,8 @@ private:
QString _ph, _phelided; QString _ph, _phelided;
int _phAfter = 0; int _phAfter = 0;
bool _placeholderVisible = true; bool _placeholderVisible = true;
FloatAnimation _a_placeholderFocused; Animation _a_placeholderFocused;
FloatAnimation _a_placeholderVisible; Animation _a_placeholderVisible;
TextWithTags _lastTextWithTags; TextWithTags _lastTextWithTags;
@ -314,8 +314,8 @@ private:
bool _customUpDown = false; bool _customUpDown = false;
bool _placeholderVisible = true; bool _placeholderVisible = true;
FloatAnimation _a_placeholderFocused; Animation _a_placeholderFocused;
FloatAnimation _a_placeholderVisible; Animation _a_placeholderVisible;
const style::FlatInput &_st; const style::FlatInput &_st;
@ -468,13 +468,13 @@ private:
QString _placeholder, _placeholderFull; QString _placeholder, _placeholderFull;
bool _placeholderVisible; bool _placeholderVisible;
FloatAnimation _a_placeholderFocused; Animation _a_placeholderFocused;
FloatAnimation _a_placeholderVisible; Animation _a_placeholderVisible;
anim::value a_borderOpacityActive; anim::value a_borderOpacityActive;
anim::value a_borderFgActive; anim::value a_borderFgActive;
anim::value a_borderFgError; anim::value a_borderFgError;
Animation _a_border; BasicAnimation _a_border;
bool _focused, _error; bool _focused, _error;
@ -634,13 +634,13 @@ private:
QString _placeholder, _placeholderFull; QString _placeholder, _placeholderFull;
bool _placeholderVisible; bool _placeholderVisible;
FloatAnimation _a_placeholderFocused; Animation _a_placeholderFocused;
FloatAnimation _a_placeholderVisible; Animation _a_placeholderVisible;
anim::value a_borderOpacityActive; anim::value a_borderOpacityActive;
anim::value a_borderFgActive; anim::value a_borderFgActive;
anim::value a_borderFgError; anim::value a_borderFgError;
Animation _a_border; BasicAnimation _a_border;
bool _focused, _error; bool _focused, _error;
@ -754,13 +754,13 @@ private:
QString _placeholder, _placeholderFull; QString _placeholder, _placeholderFull;
bool _placeholderVisible, _placeholderFast; bool _placeholderVisible, _placeholderFast;
FloatAnimation _a_placeholderFocused; Animation _a_placeholderFocused;
FloatAnimation _a_placeholderVisible; Animation _a_placeholderVisible;
anim::value a_borderOpacityActive; anim::value a_borderOpacityActive;
anim::value a_borderFgActive; anim::value a_borderFgActive;
anim::value a_borderFgError; anim::value a_borderFgError;
Animation _a_border; BasicAnimation _a_border;
bool _focused, _error; bool _focused, _error;

View file

@ -95,7 +95,7 @@ private:
, y(y) { , y(y) {
x.start(updateCallback, fromX, toX, duration); x.start(updateCallback, fromX, toX, duration);
} }
FloatAnimation x; Animation x;
int fromX, toX; int fromX, toX;
int y; int y;
}; };
@ -107,8 +107,8 @@ private:
const style::color &_color; const style::color &_color;
bool _over = false; bool _over = false;
QPixmap _cache; QPixmap _cache;
FloatAnimation _visibility; Animation _visibility;
FloatAnimation _overOpacity; Animation _overOpacity;
bool _overDelete = false; bool _overDelete = false;
bool _active = false; bool _active = false;
PaintRoundImage _paintRoundImage; PaintRoundImage _paintRoundImage;

View file

@ -137,7 +137,7 @@ private:
QMargins itemPaintMargins() const; QMargins itemPaintMargins() const;
const style::MultiSelect &_st; const style::MultiSelect &_st;
FloatAnimation _iconOpacity; Animation _iconOpacity;
ScrollCallback _scrollCallback; ScrollCallback _scrollCallback;
@ -157,7 +157,7 @@ private:
ChildWidget<Ui::CrossButton> _cancel; ChildWidget<Ui::CrossButton> _cancel;
int _newHeight = 0; int _newHeight = 0;
FloatAnimation _height; Animation _height;
base::lambda<void(const QString &query)> _queryChangedCallback; base::lambda<void(const QString &query)> _queryChangedCallback;
base::lambda<void(bool ctrlShiftEnter)> _submittedCallback; base::lambda<void(bool ctrlShiftEnter)> _submittedCallback;

View file

@ -119,12 +119,12 @@ private:
PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft; PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft;
std_::unique_ptr<PanelAnimation> _showAnimation; std_::unique_ptr<PanelAnimation> _showAnimation;
FloatAnimation _a_show; Animation _a_show;
bool _useTransparency = true; bool _useTransparency = true;
bool _hiding = false; bool _hiding = false;
QPixmap _cache; QPixmap _cache;
FloatAnimation _a_opacity; Animation _a_opacity;
bool _deleteOnHide = true; bool _deleteOnHide = true;
bool _triggering = false; bool _triggering = false;

View file

@ -41,18 +41,9 @@ void ScrollShadow::changeVisibility(bool shown) {
ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::FlatScroll *st) : QWidget(parent) ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::FlatScroll *st) : QWidget(parent)
, _st(st) , _st(st)
, _vertical(vert) , _vertical(vert)
, _over(false) , _hiding(_st->hiding != 0)
, _overbar(false)
, _moving(false)
, _topSh(false)
, _bottomSh(false)
, _connected(vert ? parent->verticalScrollBar() : parent->horizontalScrollBar()) , _connected(vert ? parent->verticalScrollBar() : parent->horizontalScrollBar())
, _scrollMax(_connected->maximum()) , _scrollMax(_connected->maximum()) {
, _hideIn(-1)
, a_bgOver(0.)
, a_barOver(0.)
, a_fullOpacity(_st->hiding ? 0. : 1.)
, _a_appearance(animation(this, &ScrollBar::step_appearance)) {
recountSize(); recountSize();
_hideTimer.setSingleShot(true); _hideTimer.setSingleShot(true);
@ -121,32 +112,78 @@ void ScrollBar::updateBar(bool force) {
} }
void ScrollBar::onHideTimer() { void ScrollBar::onHideTimer() {
_hideIn = -1; if (!_hiding) {
a_fullOpacity.start(0.); _hiding = true;
a_bgOver.restart(); _a_opacity.start([this] { update(); }, 1., 0., _st->duration);
a_barOver.restart(); }
_a_appearance.start();
} }
ScrollArea *ScrollBar::area() { ScrollArea *ScrollBar::area() {
return static_cast<ScrollArea*>(parentWidget()); return static_cast<ScrollArea*>(parentWidget());
} }
void ScrollBar::setOver(bool over) {
if (_over != over) {
auto wasOver = (_over || _moving);
_over = over;
auto nowOver = (_over || _moving);
if (wasOver != nowOver) {
_a_over.start([this] { update(); }, nowOver ? 0. : 1., nowOver ? 1. : 0., _st->duration);
}
if (nowOver && _hiding) {
_hiding = false;
_a_opacity.start([this] { update(); }, 0., 1., _st->duration);
}
}
}
void ScrollBar::setOverBar(bool overbar) {
if (_overbar != overbar) {
auto wasBarOver = (_overbar || _moving);
_overbar = overbar;
auto nowBarOver = (_overbar || _moving);
if (wasBarOver != nowBarOver) {
_a_barOver.start([this] { update(); }, nowBarOver ? 0. : 1., nowBarOver ? 1. : 0., _st->duration);
}
}
}
void ScrollBar::setMoving(bool moving) {
if (_moving != moving) {
auto wasOver = (_over || _moving);
auto wasBarOver = (_overbar || _moving);
_moving = moving;
auto nowBarOver = (_overbar || _moving);
if (wasBarOver != nowBarOver) {
_a_barOver.start([this] { update(); }, nowBarOver ? 0. : 1., nowBarOver ? 1. : 0., _st->duration);
}
auto nowOver = (_over || _moving);
if (wasOver != nowOver) {
_a_over.start([this] { update(); }, nowOver ? 0. : 1., nowOver ? 1. : 0., _st->duration);
}
if (!nowOver && _st->hiding && !_hiding) {
_hideTimer.start(_hideIn);
}
}
}
void ScrollBar::paintEvent(QPaintEvent *e) { void ScrollBar::paintEvent(QPaintEvent *e) {
if (!_bar.width() && !_bar.height()) { if (!_bar.width() && !_bar.height()) {
hide(); hide();
return; return;
} }
if (a_fullOpacity.current() == 0.) return; auto ms = getms();
auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.);
if (opacity == 0.) return;
Painter p(this); Painter p(this);
auto deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0; auto deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0;
auto deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax; auto deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax;
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
auto bg = anim::color(_st->bgColor, _st->bgOverColor, a_bgOver.current()); auto bg = anim::color(_st->bgColor, _st->bgOverColor, _a_over.current(ms, (_over || _moving) ? 1. : 0.));
bg.setAlpha(anim::interpolate(0, bg.alpha(), a_fullOpacity.current())); bg.setAlpha(anim::interpolate(0, bg.alpha(), opacity));
auto bar = anim::color(_st->barColor, _st->barOverColor, a_barOver.current()); auto bar = anim::color(_st->barColor, _st->barOverColor, _a_barOver.current(ms, (_overbar || _moving) ? 1. : 0.));
bar.setAlpha(anim::interpolate(0, bar.alpha(), a_fullOpacity.current())); bar.setAlpha(anim::interpolate(0, bar.alpha(), opacity));
if (_st->round) { if (_st->round) {
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);
p.setBrush(bg); p.setBrush(bg);
@ -159,30 +196,13 @@ void ScrollBar::paintEvent(QPaintEvent *e) {
} }
} }
void ScrollBar::step_appearance(float64 ms, bool timer) {
float64 dt = ms / _st->duration;
if (dt >= 1) {
_a_appearance.stop();
a_bgOver.finish();
a_barOver.finish();
a_fullOpacity.finish();
} else {
a_bgOver.update(dt, anim::linear);
a_barOver.update(dt, anim::linear);
a_fullOpacity.update(dt, anim::linear);
}
if (timer) update();
}
void ScrollBar::hideTimeout(TimeMs dt) { void ScrollBar::hideTimeout(TimeMs dt) {
if (_hideIn < 0) { if (_hiding && dt > 0) {
a_bgOver.start(_over ? 1. : 0.); _hiding = false;
a_barOver.start(_over ? 1. : 0.); _a_opacity.start([this] { update(); }, 0., 1., _st->duration);
a_fullOpacity.start(1.);
_a_appearance.start();
} }
_hideIn = dt; _hideIn = dt;
if (!_moving && _hideIn >= 0) { if (!_moving) {
_hideTimer.start(_hideIn); _hideTimer.start(_hideIn);
} }
} }
@ -190,40 +210,22 @@ void ScrollBar::hideTimeout(TimeMs dt) {
void ScrollBar::enterEvent(QEvent *e) { void ScrollBar::enterEvent(QEvent *e) {
_hideTimer.stop(); _hideTimer.stop();
setMouseTracking(true); setMouseTracking(true);
_over = true; setOver(true);
a_bgOver.start(1.);
a_barOver.start(1.);
a_fullOpacity.start(1.);
_a_appearance.start();
} }
void ScrollBar::leaveEvent(QEvent *e) { void ScrollBar::leaveEvent(QEvent *e) {
if (!_moving) { if (!_moving) {
setMouseTracking(false); setMouseTracking(false);
a_bgOver.start(0.); }
a_barOver.start(0.); setOver(false);
a_fullOpacity.start(1.); setOverBar(false);
_a_appearance.start(); if (_st->hiding && !_hiding) {
if (_hideIn >= 0) {
_hideTimer.start(_hideIn); _hideTimer.start(_hideIn);
} else if (_st->hiding) {
hideTimeout(_st->hiding);
} }
} }
_over = _overbar = false;
}
void ScrollBar::mouseMoveEvent(QMouseEvent *e) { void ScrollBar::mouseMoveEvent(QMouseEvent *e) {
bool newOverBar = _bar.contains(e->pos()); setOverBar(_bar.contains(e->pos()));
if (_overbar != newOverBar) {
_overbar = newOverBar;
if (!_moving) {
a_barOver.start(newOverBar ? 1. : 0.);
a_bgOver.start(1.);
a_fullOpacity.start(1.);
_a_appearance.start();
}
}
if (_moving) { if (_moving) {
int delta = 0, barDelta = _vertical ? (area()->height() - _bar.height()) : (area()->width() - _bar.width()); int delta = 0, barDelta = _vertical ? (area()->height() - _bar.height()) : (area()->width() - _bar.width());
if (barDelta > 0) { if (barDelta > 0) {
@ -238,7 +240,7 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) {
if (!width() || !height()) return; if (!width() || !height()) return;
_dragStart = e->globalPos(); _dragStart = e->globalPos();
_moving = true; setMoving(true);
if (_overbar) { if (_overbar) {
_startFrom = _connected->value(); _startFrom = _connected->value();
} else { } else {
@ -247,13 +249,7 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) {
div = (div <= _st->deltat + _st->deltab) ? 1 : (div - _st->deltat - _st->deltab); div = (div <= _st->deltat + _st->deltab) ? 1 : (div - _st->deltat - _st->deltab);
_startFrom = _vertical ? int32((val * int64(area()->scrollTopMax())) / div) : ((val * int64(area()->scrollLeftMax())) / div); _startFrom = _vertical ? int32((val * int64(area()->scrollTopMax())) / div) : ((val * int64(area()->scrollLeftMax())) / div);
_connected->setValue(_startFrom); _connected->setValue(_startFrom);
if (!_overbar) { setOverBar(true);
_overbar = true;
a_barOver.start(1.);
a_bgOver.start(1.);
a_fullOpacity.start(1.);
_a_appearance.start();
}
} }
area()->setMovingByScrollBar(true); area()->setMovingByScrollBar(true);
@ -262,28 +258,7 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) {
void ScrollBar::mouseReleaseEvent(QMouseEvent *e) { void ScrollBar::mouseReleaseEvent(QMouseEvent *e) {
if (_moving) { if (_moving) {
_moving = false; setMoving(false);
bool a = false;
if (!_overbar) {
if (!_over || _hideIn) {
a_bgOver.restart();
a_barOver.start(0.);
a_fullOpacity.start(1.);
a = true;
}
}
if (!_over) {
if (_hideIn) {
a_barOver.restart();
a_bgOver.start(0.);
a_fullOpacity.start(1.);
a = true;
}
if (_hideIn >= 0) {
_hideTimer.start(_hideIn);
}
}
if (a) _a_appearance.start();
area()->setMovingByScrollBar(false); area()->setMovingByScrollBar(false);
emit area()->scrollFinished(); emit area()->scrollFinished();

View file

@ -55,53 +55,58 @@ class ScrollBar : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
ScrollBar(ScrollArea *parent, bool vertical, const style::FlatScroll *st); ScrollBar(ScrollArea *parent, bool vertical, const style::FlatScroll *st);
void recountSize(); void recountSize();
void paintEvent(QPaintEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
void step_appearance(float64 ms, bool timer);
void hideTimeout(TimeMs dt); void hideTimeout(TimeMs dt);
public slots: public slots:
void onValueChanged(); void onValueChanged();
void updateBar(bool force = false); void updateBar(bool force = false);
void onHideTimer(); void onHideTimer();
signals: signals:
void topShadowVisibility(bool); void topShadowVisibility(bool);
void bottomShadowVisibility(bool); void bottomShadowVisibility(bool);
private: protected:
void paintEvent(QPaintEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
ScrollArea *area(); ScrollArea *area();
void setOver(bool over);
void setOverBar(bool overbar);
void setMoving(bool moving);
const style::FlatScroll *_st; const style::FlatScroll *_st;
bool _vertical; bool _vertical = true;
bool _over, _overbar, _moving; bool _hiding = false;
bool _topSh, _bottomSh; bool _over = false;
bool _overbar = false;
bool _moving = false;
bool _topSh = false;
bool _bottomSh = false;
QPoint _dragStart; QPoint _dragStart;
QScrollBar *_connected; QScrollBar *_connected;
int32 _startFrom, _scrollMax; int32 _startFrom, _scrollMax;
TimeMs _hideIn; TimeMs _hideIn = 0;
QTimer _hideTimer; QTimer _hideTimer;
anim::value a_bgOver, a_barOver, a_fullOpacity; Animation _a_over;
Animation _a_appearance; Animation _a_barOver;
Animation _a_opacity;
QRect _bar; QRect _bar;
}; };

View file

@ -58,7 +58,7 @@ protected:
private: private:
const style::color &_color; const style::color &_color;
FloatAnimation _a_opacity; Animation _a_opacity;
bool _shown = true; bool _shown = true;
}; };

View file

@ -64,7 +64,6 @@ FlatButton {
font: font; font: font;
overFont: font; overFont: font;
duration: int;
ripple: RippleAnimation; ripple: RippleAnimation;
} }

View file

@ -346,10 +346,6 @@ Manager::~Manager() {
namespace internal { namespace internal {
Widget::Widget(QPoint startPosition, int shift, Direction shiftDirection) : TWidget(nullptr) Widget::Widget(QPoint startPosition, int shift, Direction shiftDirection) : TWidget(nullptr)
, _opacityDuration(st::notifyFastAnim)
, a_opacity(0, 1)
, a_func(anim::linear)
, _a_opacity(animation(this, &Widget::step_opacity))
, _startPosition(startPosition) , _startPosition(startPosition)
, _direction(shiftDirection) , _direction(shiftDirection)
, a_shift(shift) , a_shift(shift)
@ -360,7 +356,7 @@ Widget::Widget(QPoint startPosition, int shift, Direction shiftDirection) : TWid
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
setAttribute(Qt::WA_MacAlwaysShowToolWindow); setAttribute(Qt::WA_MacAlwaysShowToolWindow);
_a_opacity.start(); _a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::notifyFastAnim);
} }
void Widget::destroyDelayed() { void Widget::destroyDelayed() {
@ -376,19 +372,12 @@ void Widget::destroyDelayed() {
#endif // Q_OS_LINUX32 || Q_OS_LINUX64 #endif // Q_OS_LINUX32 || Q_OS_LINUX64
} }
void Widget::step_opacity(float64 ms, bool timer) { void Widget::opacityAnimationCallback() {
float64 dt = ms / float64(_opacityDuration);
if (dt >= 1) {
a_opacity.finish();
_a_opacity.stop();
if (_hiding) {
destroyDelayed();
}
} else {
a_opacity.update(dt, a_func);
}
updateOpacity(); updateOpacity();
update(); update();
if (!_a_opacity.animating() && _hiding) {
destroyDelayed();
}
} }
void Widget::step_shift(float64 ms, bool timer) { void Widget::step_shift(float64 ms, bool timer) {
@ -411,25 +400,19 @@ void Widget::hideFast() {
void Widget::hideStop() { void Widget::hideStop() {
if (_hiding) { if (_hiding) {
_opacityDuration = st::notifyFastAnim;
a_func = anim::linear;
a_opacity.start(1);
_hiding = false; _hiding = false;
_a_opacity.start(); _a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::notifyFastAnim);
} }
} }
void Widget::hideAnimated(float64 duration, const anim::transition &func) { void Widget::hideAnimated(float64 duration, const anim::transition &func) {
_opacityDuration = duration;
a_func = func;
a_opacity.start(0);
_hiding = true; _hiding = true;
_a_opacity.start(); _a_opacity.start([this] { opacityAnimationCallback(); }, 1., 0., duration, func);
} }
void Widget::updateOpacity() { void Widget::updateOpacity() {
if (auto manager = ManagerInstance.data()) { if (auto manager = ManagerInstance.data()) {
setWindowOpacity(a_opacity.current() * manager->demoMasterOpacity()); setWindowOpacity(_a_opacity.current(_hiding ? 1. : 0.) * manager->demoMasterOpacity());
} }
} }
@ -724,6 +707,7 @@ void Notification::toggleActionButtons(bool visible) {
if (_actionsVisible != visible) { if (_actionsVisible != visible) {
_actionsVisible = visible; _actionsVisible = visible;
a_actionsOpacity.start([this] { actionsOpacityCallback(); }, _actionsVisible ? 0. : 1., _actionsVisible ? 1. : 0., st::notifyActionsDuration); a_actionsOpacity.start([this] { actionsOpacityCallback(); }, _actionsVisible ? 0. : 1., _actionsVisible ? 1. : 0., st::notifyActionsDuration);
_reply->clearState();
_reply->hide(); _reply->hide();
} }
} }
@ -755,7 +739,7 @@ void Notification::showReplyField() {
connect(_replyArea, SIGNAL(submitted(bool)), this, SLOT(onReplySubmit(bool))); connect(_replyArea, SIGNAL(submitted(bool)), this, SLOT(onReplySubmit(bool)));
connect(_replyArea, SIGNAL(cancelled()), this, SLOT(onReplyCancel())); connect(_replyArea, SIGNAL(cancelled()), this, SLOT(onReplyCancel()));
_replySend = new Ui::IconButton(this, st::notifySendReply); _replySend.create(this, st::notifySendReply);
_replySend->moveToRight(st::notifyBorderWidth, st::notifyMinHeight); _replySend->moveToRight(st::notifyBorderWidth, st::notifyMinHeight);
_replySend->show(); _replySend->show();
_replySend->setClickedCallback([this] { sendReply(); }); _replySend->setClickedCallback([this] { sendReply(); });

View file

@ -114,7 +114,7 @@ private:
using QueuedNotifications = QList<QueuedNotification>; using QueuedNotifications = QList<QueuedNotification>;
QueuedNotifications _queuedNotifications; QueuedNotifications _queuedNotifications;
FloatAnimation _demoMasterOpacity; Animation _demoMasterOpacity;
}; };
@ -150,23 +150,20 @@ protected:
virtual void updateGeometry(int x, int y, int width, int height); virtual void updateGeometry(int x, int y, int width, int height);
private: private:
void opacityAnimationCallback();
void destroyDelayed(); void destroyDelayed();
void moveByShift(); void moveByShift();
void hideAnimated(float64 duration, const anim::transition &func); void hideAnimated(float64 duration, const anim::transition &func);
void step_opacity(float64 ms, bool timer);
void step_shift(float64 ms, bool timer); void step_shift(float64 ms, bool timer);
bool _hiding = false; bool _hiding = false;
bool _deleted = false; bool _deleted = false;
float64 _opacityDuration;
anim::value a_opacity;
anim::transition a_func;
Animation _a_opacity; Animation _a_opacity;
QPoint _startPosition; QPoint _startPosition;
Direction _direction; Direction _direction;
anim::value a_shift; anim::value a_shift;
Animation _a_shift; BasicAnimation _a_shift;
}; };
@ -234,7 +231,7 @@ private:
bool _hideReplyButton = false; bool _hideReplyButton = false;
bool _actionsVisible = false; bool _actionsVisible = false;
FloatAnimation a_actionsOpacity; Animation a_actionsOpacity;
QPixmap _buttonsCache; QPixmap _buttonsCache;
#if defined Q_OS_WIN && !defined Q_OS_WINRT #if defined Q_OS_WIN && !defined Q_OS_WINRT

View file

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
using "basic.style"; using "basic.style";
using "ui/widgets/widgets.style"; using "ui/widgets/widgets.style";
using "history/history.style";
windowMinWidth: 380px; windowMinWidth: 380px;
windowMinHeight: 480px; windowMinHeight: 480px;
@ -76,12 +77,10 @@ notifyReplyArea: InputArea(defaultInputArea) {
borderActive: 0px; borderActive: 0px;
borderError: 0px; borderError: 0px;
} }
notifySendReply: IconButton { notifySendReply: IconButton(historySend) {
width: 36px; width: 36px;
height: 36px; height: 36px;
iconPosition: point(6px, 6px);
icon: icon {{ "notification_send", lightButtonFg, point(3px, 9px) }};
iconPosition: point(0px, 0px);
} }
titleUnreadCounterTop: 5px; titleUnreadCounterTop: 5px;

View file

@ -28,7 +28,8 @@ namespace Window {
void SlideAnimation::paintContents(Painter &p, const QRect &update) const { void SlideAnimation::paintContents(Painter &p, const QRect &update) const {
int retina = cIntRetinaFactor(); int retina = cIntRetinaFactor();
auto progress = _animation.current(getms()); // Animation callback can destroy "this", so we don't pass "ms".
auto progress = _animation.current((_direction == SlideDirection::FromLeft) ? 0. : 1.);
auto coordUnder = anim::interpolate(0, -st::slideShift, progress); auto coordUnder = anim::interpolate(0, -st::slideShift, progress);
auto coordOver = anim::interpolate(_cacheOver.width() / cIntRetinaFactor(), 0, progress); auto coordOver = anim::interpolate(_cacheOver.width() / cIntRetinaFactor(), 0, progress);
if (coordOver) { if (coordOver) {

View file

@ -53,7 +53,7 @@ private:
SlideDirection _direction = SlideDirection::FromRight; SlideDirection _direction = SlideDirection::FromRight;
bool _topBarShadowEnabled = false; bool _topBarShadowEnabled = false;
mutable FloatAnimation _animation; mutable Animation _animation;
QPixmap _cacheUnder, _cacheOver; QPixmap _cacheUnder, _cacheOver;
RepaintCallback _repaintCallback; RepaintCallback _repaintCallback;

View file

@ -52,7 +52,7 @@ private:
void handleTimer(); void handleTimer();
bool _hiding = false; bool _hiding = false;
FloatAnimation _animation; Animation _animation;
QPixmap _cache; QPixmap _cache;
QRect _inner, _outer; QRect _inner, _outer;