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)
, _linkOver(false)
, _save(this, lang(lng_settings_save), st::defaultBoxButton)
, _skip(this, lang(existing ? lng_cancel : lng_create_group_skip), st::cancelBoxButton)
, a_goodOpacity(0, 0)
, _a_goodFade(animation(this, &SetupChannelBox::step_goodFade)) {
, _skip(this, lang(existing ? lng_cancel : lng_create_group_skip), st::cancelBoxButton) {
setMouseTracking(true);
_checkRequestId = MTP::send(MTPchannels_CheckUsername(_channel->inputChannel, MTP_string("preston")), RPCDoneHandlerPtr(), rpcFail(&SetupChannelBox::onFirstCheckFail));
@ -515,12 +513,15 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) {
p.setFont(_linkOver ? st::boxTextFont->underline() : st::boxTextFont);
p.setPen(st::defaultLinkButton.color);
p.drawText(_invitationLink, _channel->inviteLink(), option);
if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) {
p.setOpacity(a_goodOpacity.current());
p.setPen(st::boxTextFgGood);
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.setOpacity(1);
if (!_goodTextLink.isEmpty()) {
auto opacity = _a_goodOpacity.current(getms(), 0.);
if (opacity > 0.) {
p.setOpacity(opacity);
p.setPen(st::boxTextFgGood);
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.setOpacity(1);
}
}
}
} else {
@ -557,8 +558,8 @@ void SetupChannelBox::mousePressEvent(QMouseEvent *e) {
if (_linkOver) {
Application::clipboard()->setText(_channel->inviteLink());
_goodTextLink = lang(lng_create_channel_link_copied);
a_goodOpacity = anim::value(1, 0);
_a_goodFade.start();
_a_goodOpacity.finish();
_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() {
if (!_existing) {
Ui::showLayer(new ContactsBox(_channel));

View File

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

View File

@ -204,10 +204,7 @@ void ConfirmBotGameBox::onOpenLink() {
MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth)
, _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())
, _link(link)
, _linkOver(false)
, a_goodOpacity(0, 0)
, _a_good(animation(this, &MaxInviteBox::step_good)) {
, _link(link) {
setMouseTracking(true);
_textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right();
@ -226,8 +223,8 @@ void MaxInviteBox::mousePressEvent(QMouseEvent *e) {
if (_linkOver) {
Application::clipboard()->setText(_link);
_goodTextLink = lang(lng_create_channel_link_copied);
a_goodOpacity = anim::value(1, 0);
_a_good.start();
_a_goodOpacity.finish();
_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) {
AbstractBox::paintEvent(e);
@ -271,12 +257,15 @@ void MaxInviteBox::paintEvent(QPaintEvent *e) {
p.setFont(_linkOver ? st::defaultInputField.font->underline() : st::defaultInputField.font);
p.setPen(st::defaultLinkButton.color);
p.drawText(_invitationLink, _link, option);
if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) {
p.setOpacity(a_goodOpacity.current());
p.setPen(st::boxTextFgGood);
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.setOpacity(1);
if (!_goodTextLink.isEmpty()) {
auto opacity = _a_goodOpacity.current(getms(), 0.);
if (opacity > 0.) {
p.setOpacity(opacity);
p.setPen(st::boxTextFgGood);
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.setOpacity(1);
}
}
}

View File

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

View File

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

View File

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

View File

@ -273,12 +273,15 @@ void ShareBox::onMustScrollTo(int top, int bottom) {
to = bottom - (scrollBottom - scrollTop);
}
if (from != to) {
_scrollAnimation.start([this]() {
scrollArea()->scrollToY(qRound(_scrollAnimation.current(scrollArea()->scrollTop())));
}, from, to, st::shareScrollDuration, anim::sineInOut);
_scrollAnimation.start([this]() { scrollAnimationCallback(); }, from, to, st::shareScrollDuration, anim::sineInOut);
}
}
void ShareBox::scrollAnimationCallback() {
auto scrollTop = qRound(_scrollAnimation.current(scrollArea()->scrollTop()));
scrollArea()->scrollToY(scrollTop);
}
void ShareBox::onScroll() {
auto scroll = scrollArea();
auto scrollTop = scroll->scrollTop();
@ -505,7 +508,8 @@ void ShareBox::Inner::paintChat(Painter &p, TimeMs ms, Chat *chat, int index) {
auto photoTop = st::sharePhotoTop;
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 nameLeft = st::shareColumnSkip / 2;

View File

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

View File

@ -246,7 +246,7 @@ private:
QList<TimeMs> _animStartTimes;
TimeMs _aboveShadowFadeStart = 0;
anim::value _aboveShadowFadeOpacity;
Animation _a_shifting;
BasicAnimation _a_shifting;
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;
_pack.push_back(doc);
_packOvers.push_back(FloatAnimation());
_packOvers.push_back(Animation());
}
auto &packs(d.vpacks.c_vector().v);
for (int i = 0, l = packs.size(); i < l; ++i) {
@ -389,6 +389,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
if (_pack.isEmpty()) return;
auto ms = getms();
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;
@ -401,7 +402,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
DocumentData *doc = _pack.at(index);
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);
QPoint tl(pos);
if (rtl()) tl.setX(width() - tl.x() - st::stickersSize.width());

View File

@ -113,7 +113,7 @@ private:
return (_setFlags & MTPDstickerSet::Flag::f_masks);
}
std_::vector_of_moveable<FloatAnimation> _packOvers;
std_::vector_of_moveable<Animation> _packOvers;
StickerPack _pack;
StickersByEmojiMap _emoji;
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) }};
dialogsUpdateButton: FlatButton {
duration: 0;
color: activeButtonFg;
overColor: activeButtonFgOver;

View File

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

View File

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

View File

@ -61,7 +61,7 @@ public:
typedef QMap<History*, TimeMs> TypingHistories; // when typing in this history started
TypingHistories typing;
Animation _a_typings;
BasicAnimation _a_typings;
int unreadBadge() 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)
, _scroll(this, st::mentionScroll)
, _inner(this, &_mrows, &_hrows, &_brows, &_srows)
, a_opacity(0)
, _a_appearance(animation(this, &FieldAutocomplete::step_appearance)) {
, _inner(this, &_mrows, &_hrows, &_brows, &_srows) {
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(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)));
@ -47,15 +45,22 @@ FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent)
_scroll->show();
_inner->show();
hide();
connect(_scroll, SIGNAL(geometryChanged()), _inner, SLOT(onParentGeometryChanged()));
}
void FieldAutocomplete::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
auto opacity = _a_opacity.current(getms(), _hiding ? 0. : 1.);
if (opacity < 1.) {
if (opacity > 0.) {
p.setOpacity(opacity);
p.drawPixmap(0, 0, _cache);
} else if (_hiding) {
}
return;
}
@ -392,10 +397,7 @@ void FieldAutocomplete::recount(bool resetScroll) {
}
void FieldAutocomplete::hideFast() {
if (_a_appearance.animating()) {
_a_appearance.stop();
}
a_opacity = anim::value();
_a_opacity.finish();
hideFinish();
}
@ -410,9 +412,8 @@ void FieldAutocomplete::hideAnimated() {
}
_scroll->hide();
_hiding = true;
a_opacity.start(0);
_a_opacity.start([this] { animationCallback(); }, 1., 0., st::defaultDropdownDuration);
setAttribute(Qt::WA_OpaquePaintEvent, false);
_a_appearance.start();
}
void FieldAutocomplete::hideFinish() {
@ -423,7 +424,7 @@ void FieldAutocomplete::hideFinish() {
}
void FieldAutocomplete::showAnimated() {
if (!isHidden() && a_opacity.current() == 1 && !_hiding) {
if (!isHidden() && !_hiding) {
return;
}
if (_cache.isNull()) {
@ -433,16 +434,13 @@ void FieldAutocomplete::showAnimated() {
_scroll->hide();
_hiding = false;
show();
a_opacity.start(1);
_a_opacity.start([this] { animationCallback(); }, 0., 1., st::defaultDropdownDuration);
setAttribute(Qt::WA_OpaquePaintEvent, false);
_a_appearance.start();
}
void FieldAutocomplete::step_appearance(float64 ms, bool timer) {
float64 dt = ms / st::defaultDropdownDuration;
if (dt >= 1) {
_a_appearance.stop();
a_opacity.finish();
void FieldAutocomplete::animationCallback() {
update();
if (!_a_opacity.animating()) {
_cache = QPixmap();
setAttribute(Qt::WA_OpaquePaintEvent);
if (_hiding) {
@ -451,10 +449,7 @@ void FieldAutocomplete::step_appearance(float64 ms, bool timer) {
_scroll->show();
_inner->clearSel();
}
} else {
a_opacity.update(dt, anim::linear);
}
if (timer) update();
}
const QString &FieldAutocomplete::filter() const {

View File

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

View File

@ -189,8 +189,6 @@ historySendPadding: 9px;
historySendRight: 2px;
historyComposeButton: FlatButton {
duration: 200;
color: windowActiveTextFg;
overColor: windowActiveTextFg;
@ -214,12 +212,14 @@ historyUnblock: FlatButton(historyComposeButton) {
overColor: #d15948;
}
historySendIcon: icon {{ "send_control_send", historySendIconFg }};
historySendIconOver: icon {{ "send_control_send", historySendIconFgOver }};
historySend: IconButton {
width: 46px;
height: 46px;
icon: icon {{ "send_control_send", historySendIconFg }};
iconOver: icon {{ "send_control_send", historySendIconFgOver }};
icon: historySendIcon;
iconOver: historySendIconOver;
iconPosition: point(11px, 11px);
}
historyEditSaveIcon: icon {{ "send_control_save", historySendIconFg, point(3px, 7px) }};
@ -265,6 +265,7 @@ historyBotCommandStart: IconButton(historyAttach) {
historyRecordVoiceFg: historyComposeIconFg;
historyRecordVoiceFgOver: historyComposeIconFgOver;
historyRecordVoiceFgActive: windowBgActive;
historyRecordVoiceDuration: 200;
historyRecordVoice: icon {{ "send_control_record", historyRecordVoiceFg }};
historyRecordVoiceOver: icon {{ "send_control_record", historyRecordVoiceFgOver }};
historyRecordVoiceActive: icon {{ "send_control_record", historyRecordVoiceFgActive }};
@ -315,8 +316,6 @@ historyInlineBotCancel: IconButton(historyReplyCancel) {
}
reportSpamHide: FlatButton {
duration: 200;
color: 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)
, _hiding(false)
, _in(false)
, a_opacity(0)
, a_colorDrop(0)
, _a_appearance(animation(this, &DragArea::step_appearance))
, _shadow(st::boxShadow) {
setMouseTracking(true);
setAcceptDrops(true);
@ -49,28 +46,24 @@ DragArea::DragArea(QWidget *parent) : TWidget(parent)
void DragArea::mouseMoveEvent(QMouseEvent *e) {
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());
if (newIn != _in) {
_in = newIn;
a_opacity.start(1);
a_colorDrop.start(_in ? 1. : 0.);
_a_appearance.start();
}
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());
setIn(in);
}
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());
bool newIn = r.contains(e->pos());
if (newIn != _in) {
_in = newIn;
a_opacity.start(1);
a_colorDrop.start(_in ? 1. : 0.);
_a_appearance.start();
}
setIn(r.contains(e->pos()));
e->setDropAction(_in ? Qt::CopyAction : Qt::IgnoreAction);
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) {
_text = text;
_subtext = subtext;
@ -80,9 +73,12 @@ void DragArea::setText(const QString &text, const QString &subtext) {
void DragArea::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
auto ms = getms();
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());
@ -91,7 +87,7 @@ void DragArea::paintEvent(QPaintEvent *e) {
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.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) {
static_cast<HistoryWidget*>(parentWidget())->dragLeaveEvent(e);
_in = false;
a_opacity.start(_hiding ? 0 : 1);
a_colorDrop.start(_in ? 1. : 0.);
_a_appearance.start();
setIn(false);
}
void DragArea::dropEvent(QDropEvent *e) {
@ -130,47 +123,33 @@ void DragArea::otherLeave() {
}
void DragArea::hideFast() {
if (_a_appearance.animating()) {
_a_appearance.stop();
}
a_opacity = anim::value();
_a_opacity.finish();
hide();
}
void DragArea::hideStart() {
_hiding = true;
_in = false;
a_opacity.start(0.);
a_colorDrop.start(_in ? 1. : 0.);
_a_appearance.start();
setIn(false);
_a_opacity.start([this] { opacityAnimationCallback(); }, 1., 0., st::defaultDropdownDuration);
}
void DragArea::hideFinish() {
hide();
_in = false;
a_colorDrop = anim::value();
_a_in.finish();
}
void DragArea::showStart() {
_hiding = false;
show();
a_opacity.start(1);
a_colorDrop.start(_in ? 1. : 0.);
_a_appearance.start();
_a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::defaultDropdownDuration);
}
void DragArea::step_appearance(float64 ms, bool timer) {
float64 dt = ms / st::defaultDropdownDuration;
if (dt >= 1) {
a_opacity.finish();
a_colorDrop.finish();
void DragArea::opacityAnimationCallback() {
update();
if (!_a_opacity.animating()) {
if (_hiding) {
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 otherLeave();
void step_appearance(float64 ms, bool timer);
bool overlaps(const QRect &globalRect) {
if (isHidden() || _a_appearance.animating()) return false;
if (isHidden() || _a_opacity.animating()) return false;
return QRect(st::dragPadding.left(),
st::dragPadding.top(),
@ -67,12 +65,15 @@ public slots:
void showStart();
private:
bool _hiding, _in;
void setIn(bool in);
void opacityAnimationCallback();
bool _hiding = false;
bool _in = false;
base::lambda<void(const QMimeData *data)> _droppedCallback;
anim::value a_opacity;
anim::value a_colorDrop;
Animation _a_appearance;
Animation _a_opacity;
Animation _a_in;
Ui::RectShadow _shadow;

View File

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

View File

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

View File

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

View File

@ -2676,8 +2676,6 @@ HistoryHider::HistoryHider(MainWidget *parent, bool forwardSelected) : TWidget(p
, _forwardSelected(forwardSelected)
, _send(this, lang(lng_forward_send), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, a_opacity(0, 1)
, _a_appearance(animation(this, &HistoryHider::step_appearance))
, _shadow(st::boxShadow) {
init();
}
@ -2686,8 +2684,6 @@ HistoryHider::HistoryHider(MainWidget *parent, UserData *sharedContact) : TWidge
, _sharedContact(sharedContact)
, _send(this, lang(lng_forward_send), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, a_opacity(0, 1)
, _a_appearance(animation(this, &HistoryHider::step_appearance))
, _shadow(st::boxShadow) {
init();
}
@ -2696,8 +2692,6 @@ HistoryHider::HistoryHider(MainWidget *parent) : TWidget(parent)
, _sendPath(true)
, _send(this, lang(lng_forward_send), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, a_opacity(0, 1)
, _a_appearance(animation(this, &HistoryHider::step_appearance))
, _shadow(st::boxShadow) {
init();
}
@ -2706,8 +2700,6 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &botAndQuery) : TWi
, _botAndQuery(botAndQuery)
, _send(this, lang(lng_forward_send), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, a_opacity(0, 1)
, _a_appearance(animation(this, &HistoryHider::step_appearance))
, _shadow(st::boxShadow) {
init();
}
@ -2717,8 +2709,6 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString
, _shareText(text)
, _send(this, lang(lng_forward_send), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, a_opacity(0, 1)
, _a_appearance(animation(this, &HistoryHider::step_appearance))
, _shadow(st::boxShadow) {
init();
}
@ -2731,21 +2721,7 @@ void HistoryHider::init() {
_chooseWidth = st::forwardFont->width(lang(_botAndQuery.isEmpty() ? lng_forward_choose : lng_inline_switch_choose));
resizeEvent(0);
_a_appearance.start();
}
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();
_a_opacity.start([this] { update(); }, 0., 1., st::boxDuration);
}
bool HistoryHider::withConfirm() const {
@ -2754,7 +2730,15 @@ bool HistoryHider::withConfirm() const {
void HistoryHider::paintEvent(QPaintEvent *e) {
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) {
p.fillRect(rect(), st::layerBg);
}
@ -2813,10 +2797,16 @@ void HistoryHider::startHide() {
} else {
if (_offered) _cacheForAnim = myGrab(this, _box);
if (_forwardRequest) MTP::cancel(_forwardRequest);
a_opacity.start(0);
_send->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)
, _fieldBarCancel(this, st::historyReplyCancel)
, _scroll(this, st::historyScroll, false)
, _historyToEnd(this, st::historyToDown)
, _historyDown(_scroll, st::historyToDown)
, _fieldAutocomplete(this)
, _reportSpamPanel(this)
, _send(this, st::historySend)
@ -3052,8 +3042,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
, _botCommandStart(this, st::historyBotCommandStart)
, _silent(this)
, _field(this, st::historyComposeField, lang(lng_message_ph))
, _a_recording(animation(this, &HistoryWidget::step_recording))
, _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel)))
, _a_recording(animation(this, &HistoryWidget::step_recording))
, _kbScroll(this, st::botKbScroll)
, _keyboard(this)
, _emojiPan(this)
@ -3068,7 +3058,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
connect(_reportSpamPanel, SIGNAL(reportClicked()), this, SLOT(onReportSpamClicked()));
connect(_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide()));
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(_send, SIGNAL(clicked()), this, SLOT(onSend()));
connect(_unblock, SIGNAL(clicked()), this, SLOT(onUnblock()));
@ -3138,7 +3128,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
updateScrollColors();
_historyToEnd->installEventFilter(this);
_historyDown->installEventFilter(this);
_fieldAutocomplete->hide();
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;
onBotStart();
}
unreadCountChanged(_history); // set _historyToEnd badge.
unreadCountChanged(_history); // set _historyDown badge.
} else {
clearFieldText();
doneShow();
@ -4523,7 +4513,7 @@ void HistoryWidget::updateControlsVisibility() {
if (!_a_show.animating()) {
_topShadow->setVisible(_peer ? true : false);
}
updateToEndVisibility();
updateHistoryDownVisibility();
if (!_history || _a_show.animating()) {
_reportSpamPanel->hide();
_scroll->hide();
@ -4540,7 +4530,7 @@ void HistoryWidget::updateControlsVisibility() {
_attachToggle->hide();
_attachEmoji->hide();
_silent->hide();
_historyToEnd->hide();
_historyDown->hide();
_botKeyboardShow->hide();
_botKeyboardHide->hide();
_botCommandStart->hide();
@ -4781,9 +4771,9 @@ void HistoryWidget::historyWasRead(ReadServerHistoryChecks checks) {
void HistoryWidget::unreadCountChanged(History *history) {
if (history == _history || history == _migrated) {
updateToEndVisibility();
if (_historyToEnd) {
_historyToEnd->setUnreadCount(_history->unreadCount() + (_migrated ? _migrated->unreadCount() : 0));
updateHistoryDownVisibility();
if (_historyDown) {
_historyDown->setUnreadCount(_history->unreadCount() + (_migrated ? _migrated->unreadCount() : 0));
}
}
}
@ -5118,7 +5108,7 @@ void HistoryWidget::visibleAreaUpdated() {
void HistoryWidget::preloadHistoryIfNeeded() {
if (_firstLoadRequest || _scroll->isHidden() || !_peer) return;
updateToEndVisibility();
updateHistoryDownVisibility();
int st = _scroll->scrollTop(), stm = _scroll->scrollTopMax(), sh = _scroll->height();
if (st + PreloadHeightsCount * sh > stm) {
@ -5456,7 +5446,7 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window:
_cacheUnder = params.oldContentCache;
show();
_topShadow->setVisible(params.withTopBarShadow ? false : true);
_historyToEnd->finishAnimation();
historyDownAnimationFinish();
_cacheOver = App::main()->grabForShowAnimation(params);
App::main()->topBar()->startAnim();
_topShadow->setVisible(params.withTopBarShadow ? true : false);
@ -5464,7 +5454,7 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window:
_scroll->hide();
_kbScroll->hide();
_reportSpamPanel->hide();
_historyToEnd->hide();
_historyDown->hide();
_attachToggle->hide();
_attachEmoji->hide();
_fieldAutocomplete->hide();
@ -5500,7 +5490,7 @@ void HistoryWidget::animationCallback() {
App::main()->topBar()->update();
if (!_a_show.animating()) {
_topShadow->setVisible(_peer ? true : false);
_historyToEnd->finishAnimation();
historyDownAnimationFinish();
_cacheUnder = _cacheOver = QPixmap();
App::main()->topBar()->stopAnim();
@ -5528,7 +5518,12 @@ void HistoryWidget::finishAnimation() {
if (!_a_show.animating()) return;
_a_show.finish();
_topShadow->setVisible(_peer ? true : false);
_historyToEnd->finishAnimation();
historyDownAnimationFinish();
}
void HistoryWidget::historyDownAnimationFinish() {
_historyDownShown.finish();
updateHistoryDownPosition();
}
void HistoryWidget::recordActiveCallback() {
@ -5634,7 +5629,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) {
}
if (inField != _inField && _recording) {
_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;
_inPinnedMsg = inPinnedMsg;
@ -5680,7 +5675,7 @@ void HistoryWidget::stopRecording(bool send) {
updateField();
if (_inField) {
_a_recordActive.start([this] { recordActiveCallback(); }, 1., 0., st::historyComposeButton.duration);
_a_recordActive.start([this] { recordActiveCallback(); }, 1., 0., st::historyRecordVoiceDuration);
}
if (_recordRipple) {
@ -5873,7 +5868,7 @@ bool HistoryWidget::insertBotCommand(const QString &cmd, bool specialGif) {
}
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 TWidget::eventFilter(obj, e);
@ -7096,7 +7091,7 @@ void HistoryWidget::updateControlsGeometry() {
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());
if (_membersDropdown) {
@ -7196,7 +7191,7 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh
}
_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();
@ -7439,7 +7434,16 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
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;
auto haveUnreadBelowBottom = [this](History *history) {
@ -7451,7 +7455,7 @@ void HistoryWidget::updateToEndVisibility() {
}
return (_list->itemTop(history->showFrom) >= _scroll->scrollTop() + _scroll->height());
};
auto isToEndVisible = [this, &haveUnreadBelowBottom]() {
auto historyDownIsVisible = [this, &haveUnreadBelowBottom]() {
if (!_history || _firstLoadRequest) {
return false;
}
@ -7466,11 +7470,10 @@ void HistoryWidget::updateToEndVisibility() {
}
return false;
};
bool toEndVisible = isToEndVisible();
if (toEndVisible && _historyToEnd->hidden()) {
_historyToEnd->showAnimated();
} else if (!toEndVisible && !_historyToEnd->hidden()) {
_historyToEnd->hideAnimated();
auto historyDownIsShown = historyDownIsVisible();
if (_historyDownIsShown != historyDownIsShown) {
_historyDownIsShown = historyDownIsShown;
_historyDownShown.start([this] { updateHistoryDownPosition(); }, _historyDownIsShown ? 0. : 1., _historyDownIsShown ? 1. : 0., st::historyToDownDuration);
}
}
@ -7487,7 +7490,7 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
updateField();
_a_recordActive.start([this] { recordActiveCallback(); }, 0., 1., st::historyComposeButton.duration);
_a_recordActive.start([this] { recordActiveCallback(); }, 0., 1., st::historyRecordVoiceDuration);
if (!_recordRipple) {
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();
auto ms = getms();
_historyDownShown.step(ms);
auto progress = _a_show.current(ms, 1.);
if (_a_show.animating()) {
auto retina = cIntRetinaFactor();

View File

@ -274,7 +274,7 @@ private:
int _visibleAreaBottom = 0;
bool _scrollDateShown = false;
FloatAnimation _scrollDateOpacity;
Animation _scrollDateOpacity;
SingleDelayedCall _scrollDateCheck = { this, "onScrollDateCheck" };
SingleTimer _scrollDateHideTimer;
HistoryItem *_scrollDateLastItem = nullptr;
@ -467,7 +467,6 @@ public:
HistoryHider(MainWidget *parent, const QString &url, const QString &text); // share url
HistoryHider(MainWidget *parent, const QString &botAndQuery); // inline switch button handler
void step_appearance(float64 ms, bool timer);
bool withConfirm() const;
bool offerPeer(PeerId peer);
@ -496,6 +495,7 @@ signals:
void forwarded();
private:
void animationCallback();
void init();
MainWidget *parent();
@ -510,8 +510,7 @@ private:
ChildWidget<Ui::RoundButton> _cancel;
PeerData *_offered = nullptr;
anim::value a_opacity;
Animation _a_appearance;
Animation _a_opacity;
QRect _box;
bool _hiding = false;
@ -677,7 +676,8 @@ public:
void applyCloudDraft(History *history);
void contactsReceived();
void updateToEndVisibility();
void updateHistoryDownPosition();
void updateHistoryDownVisibility();
void updateAfterDrag();
void updateFieldSubmitSettings();
@ -850,6 +850,7 @@ private:
void animationCallback();
void recordActiveCallback();
void chooseAttach();
void historyDownAnimationFinish();
void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update);
struct SendingFilesLists {
QList<QUrl> nonLocalUrls;
@ -1088,7 +1089,9 @@ private:
TimeMs _lastScrolled = 0;
QTimer _updateHistoryItems;
ChildWidget<Ui::HistoryDownButton> _historyToEnd;
Animation _historyDownShown;
bool _historyDownIsShown = false;
ChildWidget<Ui::HistoryDownButton> _historyDown;
ChildWidget<FieldAutocomplete> _fieldAutocomplete;
@ -1122,19 +1125,22 @@ private:
ChildWidget<SilentToggle> _silent;
bool _cmdStartShown = false;
ChildWidget<MessageField> _field;
Animation _a_recording;
bool _recording = false;
bool _inRecord = false;
bool _inField = false;
bool _inReplyEdit = false;
bool _inPinnedMsg = false;
bool _inClickable = false;
anim::value a_recordingLevel;
int _recordingSamples = 0;
FloatAnimation _a_recordActive;
Animation _a_recordActive;
std_::unique_ptr<Ui::RippleAnimation> _recordRipple;
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;
bool kbWasHidden() const;
@ -1163,7 +1169,7 @@ private:
bool _titlePeerTextOnline = false;
int _titlePeerTextWidth = 0;
FloatAnimation _a_show;
Animation _a_show;
Window::SlideDirection _showDirection;
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 {
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) {
p.setOpacity(over);
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) {
if (p == _open || p == _cancel) {
if (active) {
ensureAnimation();
_animation->a_thumbOver.start(1);
} else {
if (!_animation) {
ensureAnimation();
_animation->a_thumbOver = anim::value(1, 1);
}
_animation->a_thumbOver.start(0);
}
_animation->_a_thumbOver.start();
ensureAnimation();
_animation->a_thumbOver.start([this] { thumbAnimationCallback(); }, active ? 0. : 1., active ? 1. : 0., st::msgFileOverDuration);
}
}
@ -798,18 +789,9 @@ File::~File() {
unregDocumentItem(getShownDocument(), this);
}
void File::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::repaintInlineItem(this);
}
void File::thumbAnimationCallback() {
Ui::repaintInlineItem(this);
checkAnimationFinished();
}
void File::step_radial(TimeMs ms, bool timer) {
@ -826,16 +808,14 @@ void File::step_radial(TimeMs ms, bool timer) {
void File::ensureAnimation() const {
if (!_animation) {
_animation.reset(new AnimationData(
animation(const_cast<File*>(this), &File::step_thumbOver),
animation(const_cast<File*>(this), &File::step_radial)));
_animation.reset(new AnimationData(animation(const_cast<File*>(this), &File::step_radial)));
}
}
void File::checkAnimationFinished() {
if (_animation && !_animation->_a_thumbOver.animating() && !_animation->radial.animating()) {
if (_animation && !_animation->a_thumbOver.animating() && !_animation->radial.animating()) {
if (getShownDocument()->loaded()) {
_animation = nullptr;
_animation.reset();
}
}
}

View File

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

View File

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

View File

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

View File

@ -157,7 +157,7 @@ private:
void fillEmojiString();
void resetGifAndCache();
FloatAnimation _a_shown;
Animation _a_shown;
bool _hiding = false;
DocumentData *_document = 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;
}
if (_history->peer() == channel) {
_history->updateToEndVisibility();
_history->updateHistoryDownVisibility();
_history->preloadHistoryIfNeeded();
}
h->asChannelHistory()->getRangeDifference();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -105,20 +105,30 @@ public:
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);
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;
}
resultLen = BN_bn2bin(&bnResult, (uchar*)gResult);
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;
}
BN_add_word(&bnResult, 1); // check g_b < dh_prime - 1
if (BN_cmp(&bnResult, &bnModul) >= 0) {
DEBUG_LOG(("BigNum Error: bad g_b >= dh_prime - 1"));
// check g_b < dh_prime - 2^{2048 - 8}
BN_sub(&bnTemp, &bnModul, &bnResult);
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;
}
@ -140,9 +150,25 @@ public:
return false;
}
BN_add_word(&bn_g_a, 1); // check g_a < dh_prime - 1
if (BN_cmp(&bn_g_a, &bnModul) >= 0) {
DEBUG_LOG(("BigNum Error: bad g_a >= dh_prime - 1"));
// check g_a > 2^{2048 - 8}
if (BN_is_negative(&bn_g_a)) {
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;
}
@ -155,6 +181,7 @@ public:
BN_init(&bn_g);
BN_init(&bn_g_a);
BN_init(&bnResult);
BN_init(&bnTemp);
}
~BigNumCounter() {
BN_CTX_free(ctx);
@ -163,11 +190,13 @@ public:
BN_clear_free(&bn_g);
BN_clear_free(&bn_g_a);
BN_clear_free(&bnResult);
BN_clear_free(&bnTemp);
}
private:
BIGNUM bnPower, bnModul, bn_g, bn_g_a, bnResult;
BIGNUM bnPower, bnModul, bn_g, bn_g_a, bnResult, bnTemp;
BN_CTX *ctx;
};
// 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)
, _state(DisconnectedState)
, _needSessionReset(false)
, dc(_dc)
, _owner(owner)
, _conn(nullptr)
, _conn4(nullptr)
, _conn6(nullptr)
, retryTimeout(1)
, oldConnection(true)
, _waitForReceived(MTPMinReceiveDelay)
, _waitForConnected(MTPMinConnectDelay)
, firstSentAt(-1)
, _pingId(0)
, _pingIdToSend(0)
, _pingSendAt(0)
, _pingMsgId(0)
, restarted(false)
, _finished(false)
, keyId(0)
// , sessionDataMutex(QReadWriteLock::Recursive)
, sessionData(data)
, myKeyLock(false)
, authKeyData(0)
, authKeyStrings(0) {
, sessionData(data) {
oldConnectionTimer.moveToThread(thread);
_waitForConnectedTimer.moveToThread(thread);
_waitForReceivedTimer.moveToThread(thread);
@ -474,7 +486,7 @@ ConnectionPrivate::ConnectionPrivate(QThread *thread, Connection *owner, Session
static bool _registered = false;
if (!_registered) {
_registered = true;
qRegisterMetaType<QVector<quint64> >("QVector<quint64>");
qRegisterMetaType<QVector<quint64> >("QVector<quint64>");
}
connect(this, SIGNAL(needToSendAsync()), sessionData->owner(), SLOT(needToResumeAndSend()), Qt::QueuedConnection);
@ -1459,7 +1471,7 @@ void ConnectionPrivate::handleReceived() {
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);
const mtpPrime *sfrom(data + 4);
MTP_LOG(dc, ("Recv: ") + mtpTextSerialize(sfrom, end));
@ -1467,19 +1479,14 @@ void ConnectionPrivate::handleReceived() {
bool needToHandle = false;
{
QWriteLocker lock(sessionData->receivedIdsMutex());
mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet());
needToHandle = receivedIds.insert(msgId, needAck);
needToHandle = sessionData->receivedIdsSet().registerMsgId(msgId, needAck);
}
if (needToHandle) {
res = handleOneReceived(from, end, msgId, serverTime, serverSalt, badTime);
}
{
QWriteLocker lock(sessionData->receivedIdsMutex());
mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet());
uint32 receivedIdsSize = receivedIds.size();
while (receivedIdsSize-- > MTPIdsBufferSize) {
receivedIds.erase(receivedIds.begin());
}
sessionData->receivedIdsSet().shrink();
}
// send acks
@ -1502,8 +1509,8 @@ void ConnectionPrivate::handleReceived() {
emit needToReceive();
}
if (res < 0) {
_needSessionReset = (res < -1);
if (res != HandleResult::Success && res != HandleResult::Ignored) {
_needSessionReset = (res == HandleResult::ResetSession);
lockFinished.unlock();
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;
try {
@ -1536,7 +1543,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
DEBUG_LOG(("Message Info: gzip container"));
mtpBuffer response = ungzip(++from, end);
if (!response.size()) {
return -1;
return HandleResult::RestartConnection;
}
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);
if (!isReply && ((inMsgId.v & 0x03) != 3)) {
LOG(("Message Error: bad msg_id %1 in contained message received").arg(inMsgId.v));
return -1;
return HandleResult::RestartConnection;
}
MTPint inSeqNo(from, otherEnd);
MTPint bytes(from, otherEnd);
if ((bytes.v & 0x03) || bytes.v < 4) {
LOG(("Message Error: bad length %1 of contained message received").arg(bytes.v));
return -1;
return HandleResult::RestartConnection;
}
bool needAck = (inSeqNo.v & 0x01);
@ -1576,21 +1583,20 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
bool needToHandle = false;
{
QWriteLocker lock(sessionData->receivedIdsMutex());
mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet());
needToHandle = receivedIds.insert(inMsgId.v, needAck);
needToHandle = sessionData->receivedIdsSet().registerMsgId(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) {
res = handleOneReceived(from, otherEnd, inMsgId.v, serverTime, serverSalt, badTime);
badTime = false;
}
if (res <= 0) {
if (res != HandleResult::Success) {
return res;
}
from = otherEnd;
}
} return 1;
} return HandleResult::Success;
case mtpc_msgs_ack: {
MTPMsgsAck msg(from, end);
@ -1598,17 +1604,17 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
uint32 idsCount = ids.size();
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 (requestsFixTimeSalt(ids, serverTime, serverSalt)) {
badTime = false;
} else {
return 0;
return HandleResult::Ignored;
}
}
requestsAcked(ids);
} return 1;
} return HandleResult::Success;
case mtpc_bad_msg_notification: {
MTPBadMsgNotification msg(from, end);
@ -1654,7 +1660,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
if (!wasSent(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
@ -1671,7 +1677,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
badTime = false;
}
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)
mtpMsgId resendId = data.vbad_msg_id.v;
@ -1682,9 +1688,9 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
} else {
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: {
MTPBadMsgNotification msg(from, end);
@ -1696,7 +1702,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
_pingId = 0;
} else if (!wasSent(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;
@ -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));
resend(resendId);
} return 1;
} return HandleResult::Success;
case mtpc_msgs_state_req: {
if (badTime) {
DEBUG_LOG(("Message Info: skipping with bad time..."));
return 0;
return HandleResult::Ignored;
}
MTPMsgsStateReq msg(from, end);
const auto &ids(msg.c_msgs_state_req().vmsg_ids.c_vector().v);
uint32 idsCount = ids.size();
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);
{
QReadLocker lock(sessionData->receivedIdsMutex());
const mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet());
mtpMsgIdsMap::const_iterator receivedIdsEnd(receivedIds.cend());
uint64 minRecv = receivedIds.min(), maxRecv = receivedIds.max();
auto &receivedIds = sessionData->receivedIdsSet();
auto minRecv = receivedIds.min();
auto maxRecv = receivedIds.max();
QReadLocker locker(sessionData->wereAckedMutex());
const mtpRequestIdsMap &wereAcked(sessionData->wereAckedMap());
@ -1746,15 +1752,15 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
} else if (reqMsgId > maxRecv) {
state |= 0x03;
} else {
mtpMsgIdsMap::const_iterator recv = receivedIds.constFind(reqMsgId);
if (recv == receivedIdsEnd) {
auto msgIdState = receivedIds.lookup(reqMsgId);
if (msgIdState == ReceivedMsgIds::State::NotFound) {
state |= 0x02;
} else {
state |= 0x04;
if (wereAcked.constFind(reqMsgId) != wereAckedEnd) {
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;
} else {
state |= 0x10;
@ -1765,7 +1771,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
}
}
emit sendMsgsStateInfoAsync(msgId, info);
} return 1;
} return HandleResult::Success;
case mtpc_msgs_state_info: {
MTPMsgsStateInfo msg(from, end);
@ -1782,7 +1788,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
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
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 (serverSalt) sessionData->setSalt(serverSalt); // requestsFixTimeSalt with no lookup
@ -1799,7 +1805,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
if (requestBuffer->size() < 9) {
LOG(("Message Error: bad request %1 found in requestMap, size: %2").arg(reqMsgId).arg(requestBuffer->size()));
return -1;
return HandleResult::RestartConnection;
}
try {
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);
} return 1;
} return HandleResult::Success;
case mtpc_msgs_all_info: {
if (badTime) {
DEBUG_LOG(("Message Info: skipping with bad time..."));
return 0;
return HandleResult::Ignored;
}
MTPMsgsAllInfo msg(from, end);
@ -1835,7 +1841,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
handleMsgsStates(ids, states, toAck);
requestsAcked(toAck);
} return 1;
} return HandleResult::Success;
case mtpc_msg_detailed_info: {
MTPMsgDetailedInfo msg(from, end);
@ -1849,7 +1855,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
badTime = false;
} else {
DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(data.vmsg_id.v));
return 0;
return HandleResult::Ignored;
}
}
requestsAcked(ids);
@ -1858,8 +1864,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
MTPlong resMsgId = data.vanswer_msg_id;
{
QReadLocker lock(sessionData->receivedIdsMutex());
const mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet());
received = (receivedIds.find(resMsgId.v) != receivedIds.cend()) && (receivedIds.min() < resMsgId.v);
received = (sessionData->receivedIdsSet().lookup(resMsgId.v) != ReceivedMsgIds::State::NotFound);
}
if (received) {
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));
resendRequestData.push_back(resMsgId);
}
} return 1;
} return HandleResult::Success;
case mtpc_msg_new_detailed_info: {
if (badTime) {
DEBUG_LOG(("Message Info: skipping msg_new_detailed_info with bad time..."));
return 0;
return HandleResult::Ignored;
}
MTPMsgDetailedInfo msg(from, end);
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;
{
QReadLocker lock(sessionData->receivedIdsMutex());
const mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet());
received = (receivedIds.find(resMsgId.v) != receivedIds.cend()) && (receivedIds.min() < resMsgId.v);
received = (sessionData->receivedIdsSet().lookup(resMsgId.v) != ReceivedMsgIds::State::NotFound);
}
if (received) {
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));
resendRequestData.push_back(resMsgId);
}
} return 1;
} return HandleResult::Success;
case mtpc_msg_resend_req: {
MTPMsgResendReq msg(from, end);
@ -1900,14 +1904,14 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
uint32 idsCount = ids.size();
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());
for (int32 i = 0, l = ids.size(); i < l; ++i) {
toResend[i] = ids.at(i).v;
}
resendMany(toResend, 0, false, true);
} return 1;
} return HandleResult::Success;
case mtpc_rpc_result: {
if (from + 3 > end) throw mtpErrorInsufficient();
@ -1924,7 +1928,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
badTime = false;
} else {
DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(reqMsgId.v));
return 0;
return HandleResult::Ignored;
}
}
requestsAcked(ids, true);
@ -1933,7 +1937,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
DEBUG_LOG(("RPC Info: gzip container"));
response = ungzip(++from, end);
if (!response.size()) {
return -1;
return HandleResult::RestartConnection;
}
typeId = response[0];
} else {
@ -1952,7 +1956,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
} else {
DEBUG_LOG(("RPC Info: requestId not found for msgId %1").arg(reqMsgId.v));
}
} return 1;
} return HandleResult::Success;
case mtpc_new_session_created: {
const mtpPrime *start = from;
@ -1964,7 +1968,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
badTime = false;
} else {
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());
mtpRequestId fakeRequestId = sessionData->nextFakeRequestId();
haveReceived.insert(fakeRequestId, mtpResponse(update)); // notify main process about new session - need to get difference
} return 1;
} return HandleResult::Success;
case mtpc_ping: {
if (badTime) return 0;
if (badTime) return HandleResult::Ignored;
MTPPing msg(from, end);
DEBUG_LOG(("Message Info: ping received, ping_id: %1, sending pong...").arg(msg.vping_id.v));
emit sendPongAsync(msgId, msg.vping_id.v);
} return 1;
} return HandleResult::Success;
case mtpc_pong: {
MTPPong msg(from, end);
@ -2009,7 +2013,7 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
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));
return 0;
return HandleResult::Ignored;
}
if (data.vping_id.v == _pingId) {
_pingId = 0;
@ -2022,21 +2026,21 @@ int32 ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime
if (requestsFixTimeSalt(ids, serverTime, serverSalt)) {
badTime = false;
} else {
return 0;
return HandleResult::Ignored;
}
}
requestsAcked(ids, true);
} return 1;
} return HandleResult::Success;
}
} catch (Exception &) {
return -1;
return HandleResult::RestartConnection;
}
if (badTime) {
DEBUG_LOG(("Message Error: bad time in updates cons, must create new session"));
return -2;
return HandleResult::ResetSession;
}
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?..
}
return 1;
return HandleResult::Success;
}
mtpBuffer ConnectionPrivate::ungzip(const mtpPrime *from, const mtpPrime *end) const {
@ -2377,13 +2381,13 @@ void ConnectionPrivate::updateAuthKey() {
return authKeyCreated();
}
authKeyData = new ConnectionPrivate::AuthKeyCreateData();
authKeyStrings = new ConnectionPrivate::AuthKeyCreateStrings();
authKeyData->req_num = 0;
authKeyData->nonce = rand_value<MTPint128>();
_authKeyData = std_::make_unique<ConnectionPrivate::AuthKeyCreateData>();
_authKeyStrings = std_::make_unique<ConnectionPrivate::AuthKeyCreateStrings>();
_authKeyData->req_num = 0;
_authKeyData->nonce = rand_value<MTPint128>();
MTPReq_pq req_pq;
req_pq.vnonce = authKeyData->nonce;
req_pq.vnonce = _authKeyData->nonce;
connect(_conn, SIGNAL(receivedData()), this, SLOT(pqAnswered()));
@ -2408,9 +2412,9 @@ void ConnectionPrivate::pqAnswered() {
}
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)!"));
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();
}
@ -2436,12 +2440,12 @@ void ConnectionPrivate::pqAnswered() {
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;
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.vserver_nonce = authKeyData->server_nonce;
p_q_inner_data.vnonce = _authKeyData->nonce;
p_q_inner_data.vserver_nonce = _authKeyData->server_nonce;
p_q_inner_data.vpq = res_pq_data.vpq;
const string &pq(res_pq_data.vpq.c_string().v);
@ -2453,12 +2457,12 @@ void ConnectionPrivate::pqAnswered() {
return restart();
}
authKeyData->new_nonce = rand_value<MTPint256>();
p_q_inner_data.vnew_nonce = authKeyData->new_nonce;
_authKeyData->new_nonce = rand_value<MTPint256>();
p_q_inner_data.vnew_nonce = _authKeyData->new_nonce;
MTPReq_DH_params req_DH_params;
req_DH_params.vnonce = authKeyData->nonce;
req_DH_params.vserver_nonce = authKeyData->server_nonce;
req_DH_params.vnonce = _authKeyData->nonce;
req_DH_params.vserver_nonce = _authKeyData->server_nonce;
req_DH_params.vpublic_key_fingerprint = MTP_long(rsaKey->getFingerPrint());
req_DH_params.vp = p_q_inner_data.vp;
req_DH_params.vq = p_q_inner_data.vq;
@ -2508,14 +2512,14 @@ void ConnectionPrivate::dhParamsAnswered() {
switch (res_DH_params.type()) {
case mtpc_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)!"));
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();
}
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)!"));
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();
}
@ -2527,12 +2531,12 @@ void ConnectionPrivate::dhParamsAnswered() {
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];
memcpy(tmp_aes, &authKeyData->new_nonce, nlen);
memcpy(tmp_aes + nlen, &authKeyData->server_nonce, slen);
memcpy(tmp_aes + nlen + slen, &authKeyData->new_nonce, nlen);
memcpy(tmp_aes + nlen + slen + nlen, &authKeyData->new_nonce, nlen);
memcpy(tmp_aes, &_authKeyData->new_nonce, nlen);
memcpy(tmp_aes + nlen, &_authKeyData->server_nonce, slen);
memcpy(tmp_aes + nlen + slen, &_authKeyData->new_nonce, nlen);
memcpy(tmp_aes + nlen + slen + nlen, &_authKeyData->new_nonce, nlen);
hashSha1(tmp_aes, nlen + slen, sha1ns);
hashSha1(tmp_aes + nlen, nlen + slen, sha1sn);
hashSha1(tmp_aes + nlen + slen, nlen + nlen, sha1nn);
@ -2540,31 +2544,31 @@ void ConnectionPrivate::dhParamsAnswered() {
mtpBuffer decBuffer;
decBuffer.resize(encDHBufLen);
memcpy(authKeyData->aesKey, sha1ns, 20);
memcpy(authKeyData->aesKey + 20, sha1sn, 12);
memcpy(authKeyData->aesIV, sha1sn + 12, 8);
memcpy(authKeyData->aesIV + 8, sha1nn, 20);
memcpy(authKeyData->aesIV + 28, &authKeyData->new_nonce, 4);
memcpy(_authKeyData->aesKey, sha1ns, 20);
memcpy(_authKeyData->aesKey + 20, sha1sn, 12);
memcpy(_authKeyData->aesIV, sha1sn + 12, 8);
memcpy(_authKeyData->aesIV + 8, sha1nn, 20);
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));
MTPServer_DH_inner_data dh_inner(to, end);
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)!"));
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();
}
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)!"));
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();
}
uchar 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!"));
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();
}
unixtimeSet(dh_inner_data.vserver_time.v);
@ -2584,29 +2588,29 @@ void ConnectionPrivate::dhParamsAnswered() {
return restart();
}
authKeyStrings->dh_prime = QByteArray(dhPrime.data(), dhPrime.size());
authKeyData->g = dh_inner_data.vg.v;
authKeyStrings->g_a = QByteArray(g_a.data(), g_a.size());
authKeyData->retry_id = MTP_long(0);
authKeyData->retries = 0;
_authKeyStrings->dh_prime = QByteArray(dhPrime.data(), dhPrime.size());
_authKeyData->g = dh_inner_data.vg.v;
_authKeyStrings->g_a = QByteArray(g_a.data(), g_a.size());
_authKeyData->retry_id = MTP_long(0);
_authKeyData->retries = 0;
} return dhClientParamsSend();
case mtpc_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)!"));
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();
}
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)!"));
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();
}
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!"));
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();
}
LOG(("AuthKey Error: server_DH_params_fail received!"));
@ -2618,16 +2622,16 @@ void ConnectionPrivate::dhParamsAnswered() {
}
void ConnectionPrivate::dhClientParamsSend() {
if (++authKeyData->retries > 5) {
LOG(("AuthKey Error: could not create auth_key for %1 retries").arg(authKeyData->retries - 1));
if (++_authKeyData->retries > 5) {
LOG(("AuthKey Error: could not create auth_key for %1 retries").arg(_authKeyData->retries - 1));
return restart();
}
MTPClient_DH_Inner_Data client_dh_inner;
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.vserver_nonce = authKeyData->server_nonce;
client_dh_inner_data.vretry_id = authKeyData->retry_id;
client_dh_inner_data.vnonce = _authKeyData->nonce;
client_dh_inner_data.vserver_nonce = _authKeyData->server_nonce;
client_dh_inner_data.vretry_id = _authKeyData->retry_id;
client_dh_inner_data.vg_b._string().v.resize(256);
// gen rand 'b'
@ -2636,19 +2640,19 @@ void ConnectionPrivate::dhClientParamsSend() {
// count g_b and auth_key using openssl BIGNUM methods
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();
}
// count auth_key hashes - parts of sha1(auth_key)
uchar sha1Buffer[20];
int32 *auth_key_sha = hashSha1(authKeyData->auth_key, 256, sha1Buffer);
memcpy(&authKeyData->auth_key_aux_hash, auth_key_sha, 8);
memcpy(&authKeyData->auth_key_hash, auth_key_sha + 3, 8);
int32 *auth_key_sha = hashSha1(_authKeyData->auth_key, 256, sha1Buffer);
memcpy(&_authKeyData->auth_key_aux_hash, auth_key_sha, 8);
memcpy(&_authKeyData->auth_key_hash, auth_key_sha + 3, 8);
MTPSet_client_DH_params req_client_DH_params;
req_client_DH_params.vnonce = authKeyData->nonce;
req_client_DH_params.vserver_nonce = authKeyData->server_nonce;
req_client_DH_params.vnonce = _authKeyData->nonce;
req_client_DH_params.vserver_nonce = _authKeyData->server_nonce;
string &sdhEncString(req_client_DH_params.vencrypted_data._string().v);
@ -2670,7 +2674,7 @@ void ConnectionPrivate::dhClientParamsSend() {
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()));
@ -2694,38 +2698,38 @@ void ConnectionPrivate::dhClientParamsAnswered() {
switch (res_client_DH_params.type()) {
case mtpc_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)!"));
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();
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)!"));
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();
return restart();
}
authKeyData->new_nonce_buf[32] = 1;
_authKeyData->new_nonce_buf[32] = 1;
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!"));
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();
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);
AuthKeyPtr authKey(new AuthKey());
authKey->setKey(authKeyData->auth_key);
authKey->setKey(_authKeyData->auth_key);
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->clear();
@ -2734,53 +2738,53 @@ void ConnectionPrivate::dhClientParamsAnswered() {
case mtpc_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)!"));
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();
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)!"));
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();
return restart();
}
authKeyData->new_nonce_buf[32] = 2;
_authKeyData->new_nonce_buf[32] = 2;
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!"));
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();
return restart();
}
authKeyData->retry_id = authKeyData->auth_key_aux_hash;
_authKeyData->retry_id = _authKeyData->auth_key_aux_hash;
} return dhClientParamsSend();
case mtpc_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)!"));
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();
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)!"));
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();
return restart();
}
authKeyData->new_nonce_buf[32] = 3;
_authKeyData->new_nonce_buf[32] = 3;
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!"));
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();
return restart();
@ -2817,20 +2821,18 @@ void ConnectionPrivate::authKeyCreated() {
}
void ConnectionPrivate::clearAuthKeyData() {
if (authKeyData) {
if (_authKeyData) {
#ifdef Q_OS_WIN
SecureZeroMemory(authKeyData, sizeof(AuthKeyCreateData));
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());
SecureZeroMemory(_authKeyData.get(), sizeof(AuthKeyCreateData));
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());
#else
memset(authKeyData, 0, sizeof(AuthKeyCreateData));
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->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());
#endif
delete authKeyData;
authKeyData = 0;
delete authKeyStrings;
authKeyStrings = 0;
_authKeyData.reset();
_authKeyStrings.reset();
}
}
@ -2877,14 +2879,14 @@ void ConnectionPrivate::sendRequestNotSecure(const TRequest &request) {
buffer.push_back(0); // tcp packet num
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(requestSize * 4);
request.write(buffer);
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);

View File

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

View File

@ -141,35 +141,6 @@ public:
typedef QMap<mtpRequestId, mtpRequest> mtpPreRequestMap;
typedef QMap<mtpMsgId, mtpRequest> mtpRequestMap;
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> {
public:

View File

@ -28,18 +28,64 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace MTP {
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 {
public:
SessionData(Session *creator)
: _session(0)
, _salt(0)
, _messagesSent(0)
, _fakeRequestId(-2000000000)
, _owner(creator)
, _keyChecked(false)
, _layerInited(false) {
SessionData(Session *creator) : _owner(creator) {
}
void setSession(uint64 session) {
@ -142,10 +188,10 @@ public:
const mtpRequestIdsMap &toResendMap() const {
return toResend;
}
mtpMsgIdsMap &receivedIdsSet() {
ReceivedMsgIds &receivedIdsSet() {
return receivedIds;
}
const mtpMsgIdsMap &receivedIdsSet() const {
const ReceivedMsgIds &receivedIdsSet() const {
return receivedIds;
}
mtpRequestIdsMap &wereAckedMap() {
@ -193,20 +239,22 @@ public:
void clear();
private:
uint64 _session, _salt;
uint64 _session = 0;
uint64 _salt = 0;
uint32 _messagesSent;
mtpRequestId _fakeRequestId;
uint32 _messagesSent = 0;
mtpRequestId _fakeRequestId = -2000000000;
Session *_owner;
Session *_owner = nullptr;
AuthKeyPtr _authKey;
bool _keyChecked, _layerInited;
bool _keyChecked = false;
bool _layerInited = false;
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
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
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

View File

@ -62,8 +62,9 @@ void ItemBase::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed
void RadialProgressItem::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (p == _openl || p == _savel || p == _cancell) {
a_iconOver.start(active ? 1 : 0);
_a_iconOver.start();
if (iconAnimated()) {
_a_iconOver.start([this] { Ui::repaintHistoryItem(_parent); }, active ? 0. : 1., active ? 1. : 0., st::msgFileOverDuration);
}
}
ItemBase::clickHandlerActiveChanged(p, active);
}
@ -78,19 +79,6 @@ void RadialProgressItem::setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&sav
_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) {
if (timer) {
Ui::repaintHistoryItem(_parent);
@ -104,20 +92,17 @@ void RadialProgressItem::step_radial(TimeMs ms, bool timer) {
void RadialProgressItem::ensureRadial() {
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() {
if (_radial && !_radial->animating() && dataLoaded()) {
delete _radial;
_radial = nullptr;
_radial.reset();
}
}
RadialProgressItem::~RadialProgressItem() {
delete base::take(_radial);
}
RadialProgressItem::~RadialProgressItem() = default;
void StatusText::update(int newSize, int fullSize, int duration, TimeMs realDuration) {
setSize(newSize);
@ -174,7 +159,7 @@ private:
base::lambda_copy<void()> _updateCallback;
Ui::RoundCheckbox _check;
FloatAnimation _pression;
Animation _pression;
bool _active = false;
bool _pressed = false;
@ -393,13 +378,9 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
p.setPen(Qt::NoPen);
if (selected) {
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 {
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);
if (selected) {
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 {
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);
if (selected) {
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 {
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);
if (selected) {
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 {
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());

View File

@ -86,10 +86,7 @@ protected:
class RadialProgressItem : public ItemBase {
public:
RadialProgressItem(HistoryItem *parent) : ItemBase(parent)
, _radial(0)
, a_iconOver(0, 0)
, _a_iconOver(animation(this, &RadialProgressItem::step_iconOver)) {
RadialProgressItem(HistoryItem *parent) : ItemBase(parent) {
}
RadialProgressItem(const RadialProgressItem &other) = delete;
@ -111,7 +108,6 @@ protected:
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 ensureRadial();
@ -131,8 +127,7 @@ protected:
return false;
}
Ui::RadialAnimation *_radial;
anim::value a_iconOver;
std_::unique_ptr<Ui::RadialAnimation> _radial;
Animation _a_iconOver;
};

View File

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

View File

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

View File

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

View File

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

View File

@ -84,7 +84,7 @@ private:
ChildWidget<Ui::FlatLabel> _username = { nullptr };
ChildWidget<Ui::LeftOutlineButton> _commonGroups = { nullptr };
FloatAnimation _height;
Animation _height;
bool _showFinished = 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 "ui/widgets/labels.h"
#include "ui/toast/toast.h"
#include "boxes/confirmbox.h"
#include "observer_peer.h"
#include "mainwindow.h"
#include "lang.h"
namespace Profile {
@ -108,7 +110,9 @@ void InviteLinkWidget::refreshLink() {
}
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;
});
}

View File

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

View File

@ -54,7 +54,7 @@ private:
PeerData *_peer;
bool _waiting = false;
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) {
ChildWidget<Widget> plainChild = { nullptr };
createChildRow(plainChild, margin, std_::forward<Args>(args)...);
child = new Ui::WidgetSlideWrap<Widget>(this, plainChild, padding, [this]() {
child.create(this, plainChild, padding, [this]() {
rowHeightUpdated();
});
margin.setLeft(margin.left() - padding.left());

View File

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

View File

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

View File

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

View File

@ -28,9 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
HistoryDownButton::HistoryDownButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple)
, _st(st)
//, a_arrowOpacity(st::historyAttachEmoji.opacity, st::historyAttachEmoji.opacity)
, _a_arrowOver(animation(this, &HistoryDownButton::step_arrowOver)) {
, _st(st) {
resize(_st.width, _st.height);
setCursor(style::cur_pointer);
@ -49,12 +47,6 @@ void HistoryDownButton::paintEvent(QPaintEvent *e) {
Painter p(this);
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 down = isDown();
((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width());
@ -80,44 +72,6 @@ void HistoryDownButton::setUnreadCount(int unreadCount) {
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)
, _st(st)
, _a_loading(animation(this, &EmojiButton::step_loading)) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,7 +29,7 @@ FadeAnimation::FadeAnimation(TWidget *widget) : _widget(widget) {
bool FadeAnimation::paint(Painter &p) {
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);
return true;
}

View File

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

View File

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

View File

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

View File

@ -28,10 +28,9 @@ namespace Ui {
namespace Toast {
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);
_a_fade.start();
_a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::toastFadeInDuration);
}
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() {
_fadingOut = true;
_a_fade.start();
_hiding = true;
_a_opacity.start([this] { opacityAnimationCallback(); }, 1., 0., st::toastFadeOutDuration);
}
void Instance::hide() {
@ -51,27 +60,5 @@ void Instance::hide() {
_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 Ui

View File

@ -49,9 +49,10 @@ public:
void hide();
private:
void step_fade(float64 ms, bool timer);
bool _fadingOut = false;
Animation _a_fade;
void opacityAnimationCallback();
bool _hiding = false;
Animation _a_opacity;
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)
, _text(text)
, _st(st)
, a_over(0)
, _a_appearance(animation(this, &FlatButton::step_appearance)) {
, _st(st) {
if (_st.width < 0) {
_width = textWidth() - _st.width;
} else if (!_st.width) {
@ -148,15 +146,6 @@ FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatBu
resize(_width, _st.height);
}
void FlatButton::setOpacity(float64 o) {
_opacity = o;
update();
}
float64 FlatButton::opacity() const {
return _opacity;
}
void FlatButton::setText(const QString &text) {
_text = text;
update();
@ -176,44 +165,22 @@ int32 FlatButton::textWidth() const {
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) {
RippleButton::onStateChanged(was, source);
a_over.start(isOver() ? 1. : 0.);
if (source == StateChangeSource::ByUser || source == StateChangeSource::ByPress) {
_a_appearance.stop();
a_over.finish();
update();
} else {
_a_appearance.start();
}
update();
}
void FlatButton::paintEvent(QPaintEvent *e) {
QPainter p(this);
QRect r(0, height() - _st.height, width(), _st.height);
p.fillRect(r, isOver() ? _st.overBgColor : _st.bgColor);
p.setOpacity(_opacity);
p.fillRect(r, anim::brush(_st.bgColor, _st.overBgColor, a_over.current()));
auto ms = getms();
paintRipple(p, 0, 0, ms);
paintRipple(p, 0, 0, getms());
p.setFont(isOver() ? _st.overFont : _st.font);
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);
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());
} else {
if (_st.width < innerWidth + (_st.height - _st.font->height)) {
_text = _st.font->elided(_fullText, qMax(_st.width - (_st.height - _st.font->height), 1));
innerWidth = _st.font->width(_text);
auto fullText = _fullText;
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());
}

View File

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

View File

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

View File

@ -105,10 +105,12 @@ private:
Callback _changeFinishedCallback;
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;
Animation _a_value;
BasicAnimation _a_value;
bool _mouseDown = false;
float64 _downValue = 0.;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -119,12 +119,12 @@ private:
PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft;
std_::unique_ptr<PanelAnimation> _showAnimation;
FloatAnimation _a_show;
Animation _a_show;
bool _useTransparency = true;
bool _hiding = false;
QPixmap _cache;
FloatAnimation _a_opacity;
Animation _a_opacity;
bool _deleteOnHide = true;
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)
, _st(st)
, _vertical(vert)
, _over(false)
, _overbar(false)
, _moving(false)
, _topSh(false)
, _bottomSh(false)
, _hiding(_st->hiding != 0)
, _connected(vert ? parent->verticalScrollBar() : parent->horizontalScrollBar())
, _scrollMax(_connected->maximum())
, _hideIn(-1)
, a_bgOver(0.)
, a_barOver(0.)
, a_fullOpacity(_st->hiding ? 0. : 1.)
, _a_appearance(animation(this, &ScrollBar::step_appearance)) {
, _scrollMax(_connected->maximum()) {
recountSize();
_hideTimer.setSingleShot(true);
@ -121,32 +112,78 @@ void ScrollBar::updateBar(bool force) {
}
void ScrollBar::onHideTimer() {
_hideIn = -1;
a_fullOpacity.start(0.);
a_bgOver.restart();
a_barOver.restart();
_a_appearance.start();
if (!_hiding) {
_hiding = true;
_a_opacity.start([this] { update(); }, 1., 0., _st->duration);
}
}
ScrollArea *ScrollBar::area() {
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) {
if (!_bar.width() && !_bar.height()) {
hide();
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);
auto deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0;
auto deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax;
p.setPen(Qt::NoPen);
auto bg = anim::color(_st->bgColor, _st->bgOverColor, a_bgOver.current());
bg.setAlpha(anim::interpolate(0, bg.alpha(), a_fullOpacity.current()));
auto bar = anim::color(_st->barColor, _st->barOverColor, a_barOver.current());
bar.setAlpha(anim::interpolate(0, bar.alpha(), a_fullOpacity.current()));
auto bg = anim::color(_st->bgColor, _st->bgOverColor, _a_over.current(ms, (_over || _moving) ? 1. : 0.));
bg.setAlpha(anim::interpolate(0, bg.alpha(), opacity));
auto bar = anim::color(_st->barColor, _st->barOverColor, _a_barOver.current(ms, (_overbar || _moving) ? 1. : 0.));
bar.setAlpha(anim::interpolate(0, bar.alpha(), opacity));
if (_st->round) {
PainterHighQualityEnabler hq(p);
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) {
if (_hideIn < 0) {
a_bgOver.start(_over ? 1. : 0.);
a_barOver.start(_over ? 1. : 0.);
a_fullOpacity.start(1.);
_a_appearance.start();
if (_hiding && dt > 0) {
_hiding = false;
_a_opacity.start([this] { update(); }, 0., 1., _st->duration);
}
_hideIn = dt;
if (!_moving && _hideIn >= 0) {
if (!_moving) {
_hideTimer.start(_hideIn);
}
}
@ -190,40 +210,22 @@ void ScrollBar::hideTimeout(TimeMs dt) {
void ScrollBar::enterEvent(QEvent *e) {
_hideTimer.stop();
setMouseTracking(true);
_over = true;
a_bgOver.start(1.);
a_barOver.start(1.);
a_fullOpacity.start(1.);
_a_appearance.start();
setOver(true);
}
void ScrollBar::leaveEvent(QEvent *e) {
if (!_moving) {
setMouseTracking(false);
a_bgOver.start(0.);
a_barOver.start(0.);
a_fullOpacity.start(1.);
_a_appearance.start();
if (_hideIn >= 0) {
_hideTimer.start(_hideIn);
} else if (_st->hiding) {
hideTimeout(_st->hiding);
}
}
_over = _overbar = false;
setOver(false);
setOverBar(false);
if (_st->hiding && !_hiding) {
_hideTimer.start(_hideIn);
}
}
void ScrollBar::mouseMoveEvent(QMouseEvent *e) {
bool newOverBar = _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();
}
}
setOverBar(_bar.contains(e->pos()));
if (_moving) {
int delta = 0, barDelta = _vertical ? (area()->height() - _bar.height()) : (area()->width() - _bar.width());
if (barDelta > 0) {
@ -238,7 +240,7 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) {
if (!width() || !height()) return;
_dragStart = e->globalPos();
_moving = true;
setMoving(true);
if (_overbar) {
_startFrom = _connected->value();
} else {
@ -247,13 +249,7 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) {
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);
_connected->setValue(_startFrom);
if (!_overbar) {
_overbar = true;
a_barOver.start(1.);
a_bgOver.start(1.);
a_fullOpacity.start(1.);
_a_appearance.start();
}
setOverBar(true);
}
area()->setMovingByScrollBar(true);
@ -262,28 +258,7 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) {
void ScrollBar::mouseReleaseEvent(QMouseEvent *e) {
if (_moving) {
_moving = 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();
setMoving(false);
area()->setMovingByScrollBar(false);
emit area()->scrollFinished();

View File

@ -55,53 +55,58 @@ class ScrollBar : public QWidget {
Q_OBJECT
public:
ScrollBar(ScrollArea *parent, bool vertical, const style::FlatScroll *st);
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);
public slots:
void onValueChanged();
void updateBar(bool force = false);
void onHideTimer();
signals:
void topShadowVisibility(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();
void setOver(bool over);
void setOverBar(bool overbar);
void setMoving(bool moving);
const style::FlatScroll *_st;
bool _vertical;
bool _over, _overbar, _moving;
bool _topSh, _bottomSh;
bool _vertical = true;
bool _hiding = false;
bool _over = false;
bool _overbar = false;
bool _moving = false;
bool _topSh = false;
bool _bottomSh = false;
QPoint _dragStart;
QScrollBar *_connected;
int32 _startFrom, _scrollMax;
TimeMs _hideIn;
TimeMs _hideIn = 0;
QTimer _hideTimer;
anim::value a_bgOver, a_barOver, a_fullOpacity;
Animation _a_appearance;
Animation _a_over;
Animation _a_barOver;
Animation _a_opacity;
QRect _bar;
};

View File

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

View File

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

View File

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

View File

@ -114,7 +114,7 @@ private:
using QueuedNotifications = QList<QueuedNotification>;
QueuedNotifications _queuedNotifications;
FloatAnimation _demoMasterOpacity;
Animation _demoMasterOpacity;
};
@ -150,23 +150,20 @@ protected:
virtual void updateGeometry(int x, int y, int width, int height);
private:
void opacityAnimationCallback();
void destroyDelayed();
void moveByShift();
void hideAnimated(float64 duration, const anim::transition &func);
void step_opacity(float64 ms, bool timer);
void step_shift(float64 ms, bool timer);
bool _hiding = false;
bool _deleted = false;
float64 _opacityDuration;
anim::value a_opacity;
anim::transition a_func;
Animation _a_opacity;
QPoint _startPosition;
Direction _direction;
anim::value a_shift;
Animation _a_shift;
BasicAnimation _a_shift;
};
@ -234,7 +231,7 @@ private:
bool _hideReplyButton = false;
bool _actionsVisible = false;
FloatAnimation a_actionsOpacity;
Animation a_actionsOpacity;
QPixmap _buttonsCache;
#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 "ui/widgets/widgets.style";
using "history/history.style";
windowMinWidth: 380px;
windowMinHeight: 480px;
@ -76,12 +77,10 @@ notifyReplyArea: InputArea(defaultInputArea) {
borderActive: 0px;
borderError: 0px;
}
notifySendReply: IconButton {
notifySendReply: IconButton(historySend) {
width: 36px;
height: 36px;
icon: icon {{ "notification_send", lightButtonFg, point(3px, 9px) }};
iconPosition: point(0px, 0px);
iconPosition: point(6px, 6px);
}
titleUnreadCounterTop: 5px;

View File

@ -28,7 +28,8 @@ namespace Window {
void SlideAnimation::paintContents(Painter &p, const QRect &update) const {
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 coordOver = anim::interpolate(_cacheOver.width() / cIntRetinaFactor(), 0, progress);
if (coordOver) {

View File

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

View File

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