mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-03 21:32:16 +00:00
Merge branch 'master' into player
Conflicts: Telegram/SourceFiles/core/observer.h Telegram/SourceFiles/mainwindow.h Telegram/SourceFiles/media/view/media_clip_playback.cpp Telegram/SourceFiles/media/view/media_clip_playback.h
This commit is contained in:
commit
0562024444
@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,10,8,0
|
||||
PRODUCTVERSION 0,10,8,0
|
||||
FILEVERSION 0,10,8,1
|
||||
PRODUCTVERSION 0,10,8,1
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@ -51,10 +51,10 @@ BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileVersion", "0.10.8.0"
|
||||
VALUE "FileVersion", "0.10.8.1"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2016"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "0.10.8.0"
|
||||
VALUE "ProductVersion", "0.10.8.1"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,10,8,0
|
||||
PRODUCTVERSION 0,10,8,0
|
||||
FILEVERSION 0,10,8,1
|
||||
PRODUCTVERSION 0,10,8,1
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@ -43,10 +43,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Updater"
|
||||
VALUE "FileVersion", "0.10.8.0"
|
||||
VALUE "FileVersion", "0.10.8.1"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2016"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "0.10.8.0"
|
||||
VALUE "ProductVersion", "0.10.8.1"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
@ -42,11 +42,10 @@ ApiWrap::ApiWrap(QObject *parent) : QObject(parent)
|
||||
void ApiWrap::init() {
|
||||
}
|
||||
|
||||
void ApiWrap::requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr<RequestMessageDataCallback> callback) {
|
||||
void ApiWrap::requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback &&callback) {
|
||||
MessageDataRequest &req(channel ? _channelMessageDataRequests[channel][msgId] : _messageDataRequests[msgId]);
|
||||
if (callback) {
|
||||
MessageDataRequest::CallbackPtr pcallback(callback.release());
|
||||
req.callbacks.append(pcallback);
|
||||
req.callbacks.append(std_::move(callback));
|
||||
}
|
||||
if (!req.req) _messageDataResolveDelayed->call();
|
||||
}
|
||||
@ -138,7 +137,7 @@ void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &
|
||||
for (auto i = requests->begin(); i != requests->cend();) {
|
||||
if (i.value().req == req) {
|
||||
for_const (auto &callback, i.value().callbacks) {
|
||||
callback->call(channel, i.key());
|
||||
callback(channel, i.key());
|
||||
}
|
||||
i = requests->erase(i);
|
||||
} else {
|
||||
|
@ -28,8 +28,8 @@ public:
|
||||
ApiWrap(QObject *parent);
|
||||
void init();
|
||||
|
||||
using RequestMessageDataCallback = SharedCallback<void, ChannelData*, MsgId>;
|
||||
void requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr<RequestMessageDataCallback> callback);
|
||||
using RequestMessageDataCallback = base::lambda_wrap<void(ChannelData*, MsgId)>;
|
||||
void requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback &&callback);
|
||||
|
||||
void requestFullPeer(PeerData *peer);
|
||||
void requestPeer(PeerData *peer);
|
||||
@ -82,11 +82,8 @@ private:
|
||||
|
||||
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
|
||||
struct MessageDataRequest {
|
||||
MessageDataRequest() : req(0) {
|
||||
}
|
||||
typedef SharedCallback<void, ChannelData*, MsgId>::Ptr CallbackPtr;
|
||||
typedef QList<CallbackPtr> Callbacks;
|
||||
mtpRequestId req;
|
||||
using Callbacks = QList<RequestMessageDataCallback>;
|
||||
mtpRequestId req = 0;
|
||||
Callbacks callbacks;
|
||||
};
|
||||
typedef QMap<MsgId, MessageDataRequest> MessageDataRequests;
|
||||
|
@ -1874,7 +1874,7 @@ namespace {
|
||||
}
|
||||
|
||||
History *history(const PeerId &peer) {
|
||||
return ::histories.findOrInsert(peer, 0, 0, 0);
|
||||
return ::histories.findOrInsert(peer);
|
||||
}
|
||||
|
||||
History *historyFromDialog(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead, int32 maxOutboxRead) {
|
||||
|
@ -218,7 +218,6 @@ void Application::singleInstanceChecked() {
|
||||
Logs::multipleInstances();
|
||||
}
|
||||
|
||||
Notify::startObservers();
|
||||
Sandbox::start();
|
||||
|
||||
if (!Logs::started() || (!cManyInstance() && !Logs::instanceChecked())) {
|
||||
@ -357,8 +356,6 @@ void Application::closeApplication() {
|
||||
}
|
||||
_updateThread = 0;
|
||||
#endif
|
||||
|
||||
Notify::finishObservers();
|
||||
}
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
@ -47,7 +47,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class AbstractBox : public LayerWidget {
|
||||
class AbstractBox : public LayerWidget, protected base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -1362,8 +1362,8 @@ RevokePublicLinkBox::RevokePublicLinkBox(base::lambda_unique<void()> &&revokeCal
|
||||
|
||||
updateMaxHeight();
|
||||
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
|
||||
prepare();
|
||||
}
|
||||
@ -1410,10 +1410,16 @@ void RevokePublicLinkBox::mouseReleaseEvent(QMouseEvent *e) {
|
||||
auto text_method = pressed->isMegagroup() ? lng_channels_too_much_public_revoke_confirm_group : lng_channels_too_much_public_revoke_confirm_channel;
|
||||
auto text = text_method(lt_link, qsl("telegram.me/") + pressed->userName(), lt_group, pressed->name);
|
||||
weakRevokeConfirmBox = new ConfirmBox(text, lang(lng_channels_too_much_public_revoke));
|
||||
weakRevokeConfirmBox->setConfirmedCallback([this, weak_this = weakThis(), pressed]() {
|
||||
if (!weak_this) return;
|
||||
struct Data {
|
||||
Data(QPointer<TWidget> &&weakThis, PeerData *pressed) : weakThis(std_::move(weakThis)), pressed(pressed) {
|
||||
}
|
||||
QPointer<TWidget> weakThis;
|
||||
PeerData *pressed;
|
||||
};
|
||||
weakRevokeConfirmBox->setConfirmedCallback([this, data = std_::make_unique<Data>(weakThis(), pressed)]() {
|
||||
if (!data->weakThis) return;
|
||||
if (_revokeRequestId) return;
|
||||
_revokeRequestId = MTP::send(MTPchannels_UpdateUsername(pressed->asChannel()->inputChannel, MTP_string("")), rpcDone(&RevokePublicLinkBox::revokeLinkDone), rpcFail(&RevokePublicLinkBox::revokeLinkFail));
|
||||
_revokeRequestId = MTP::send(MTPchannels_UpdateUsername(data->pressed->asChannel()->inputChannel, MTP_string("")), rpcDone(&RevokePublicLinkBox::revokeLinkDone), rpcFail(&RevokePublicLinkBox::revokeLinkFail));
|
||||
});
|
||||
Ui::showLayer(weakRevokeConfirmBox, KeepOtherLayers);
|
||||
}
|
||||
|
@ -29,13 +29,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
|
||||
BackgroundInner::BackgroundInner() :
|
||||
_bgCount(0), _rows(0), _over(-1), _overDown(-1) {
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
if (App::cServerBackgrounds().isEmpty()) {
|
||||
resize(BackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
|
||||
MTP::send(MTPaccount_GetWallPapers(), rpcDone(&BackgroundInner::gotWallpapers));
|
||||
} else {
|
||||
updateWallpapers();
|
||||
}
|
||||
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
#include "abstractbox.h"
|
||||
#include "core/lambda_wrap.h"
|
||||
|
||||
class BackgroundInner : public QWidget, public RPCSender {
|
||||
class BackgroundInner : public TWidget, public RPCSender, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -546,7 +546,7 @@ ConfirmInviteBox::ConfirmInviteBox(const QString &title, const MTPChatPhoto &pho
|
||||
if (!location.isNull()) {
|
||||
_photo = ImagePtr(location);
|
||||
if (!_photo->loaded()) {
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
_photo->load();
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public:
|
||||
void updateLink();
|
||||
|
||||
// You can use this instead of connecting to "confirmed()" signal.
|
||||
void setConfirmedCallback(base::lambda_wrap<void()> &&callback) {
|
||||
void setConfirmedCallback(base::lambda_unique<void()> &&callback) {
|
||||
_confirmedCallback = std_::move(callback);
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,7 @@ ContactsInner::ContactsInner(UserData *bot) : TWidget()
|
||||
}
|
||||
|
||||
void ContactsInner::init() {
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact()));
|
||||
connect(&_allAdmins, SIGNAL(changed()), this, SLOT(onAllAdminsChanged()));
|
||||
|
||||
@ -1750,7 +1750,8 @@ MembersInner::MembersInner(ChannelData *channel, MembersFilter filter) : TWidget
|
||||
, _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right())
|
||||
, _about(_aboutWidth)
|
||||
, _aboutHeight(0) {
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
|
||||
connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)));
|
||||
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*)));
|
||||
|
||||
|
@ -36,7 +36,7 @@ using MembersAlreadyIn = OrderedSet<UserData*>;
|
||||
QString cantInviteError();
|
||||
|
||||
class ConfirmBox;
|
||||
class ContactsInner : public TWidget, public RPCSender {
|
||||
class ContactsInner : public TWidget, public RPCSender, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
@ -269,7 +269,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class MembersInner : public TWidget, public RPCSender {
|
||||
class MembersInner : public TWidget, public RPCSender, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
@ -33,10 +33,11 @@ LocalStorageBox::LocalStorageBox() : AbstractBox()
|
||||
connect(_clear, SIGNAL(clicked()), this, SLOT(onClear()));
|
||||
connect(_close, SIGNAL(clicked()), this, SLOT(onClose()));
|
||||
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
connect(App::wnd(), SIGNAL(tempDirCleared(int)), this, SLOT(onTempDirCleared(int)));
|
||||
connect(App::wnd(), SIGNAL(tempDirClearFailed(int)), this, SLOT(onTempDirClearFailed(int)));
|
||||
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
|
||||
checkLocalStoredCounts();
|
||||
prepare();
|
||||
}
|
||||
|
@ -227,9 +227,9 @@ void ShareBox::onMustScrollTo(int top, int bottom) {
|
||||
to = bottom - (scrollBottom - scrollTop);
|
||||
}
|
||||
if (from != to) {
|
||||
START_ANIMATION(_scrollAnimation, func([this]() {
|
||||
_scrollAnimation.start([this]() {
|
||||
scrollArea()->scrollToY(_scrollAnimation.current(scrollArea()->scrollTop()));
|
||||
}), from, to, st::shareScrollDuration, anim::sineInOut);
|
||||
}, from, to, st::shareScrollDuration, anim::sineInOut);
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,8 +243,6 @@ namespace internal {
|
||||
|
||||
ShareInner::ShareInner(QWidget *parent) : ScrolledWidget(parent)
|
||||
, _chatsIndexed(std_::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Add)) {
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
|
||||
_rowsTop = st::shareRowsTop;
|
||||
_rowHeight = st::shareRowHeight;
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
@ -264,7 +262,10 @@ ShareInner::ShareInner(QWidget *parent) : ScrolledWidget(parent)
|
||||
|
||||
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
auto observeEvents = UpdateFlag::NameChanged | UpdateFlag::PhotoChanged;
|
||||
Notify::registerPeerObserver(observeEvents, this, &ShareInner::notifyPeerUpdated);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
}
|
||||
|
||||
void ShareInner::setVisibleTopBottom(int visibleTop, int visibleBottom) {
|
||||
@ -437,9 +438,9 @@ void ShareInner::setActive(int active) {
|
||||
if (active != _active) {
|
||||
auto changeNameFg = [this](int index, style::color from, style::color to) {
|
||||
if (auto chat = getChatAtIndex(index)) {
|
||||
START_ANIMATION(chat->nameFg, func([this, chat] {
|
||||
chat->nameFg.start([this, chat] {
|
||||
repaintChat(chat->peer);
|
||||
}), from->c, to->c, st::shareActivateDuration, anim::linear);
|
||||
}, from->c, to->c, st::shareActivateDuration);
|
||||
}
|
||||
};
|
||||
changeNameFg(_active, st::shareNameActiveFg, st::shareNameFg);
|
||||
@ -459,16 +460,7 @@ void ShareInner::paintChat(Painter &p, Chat *chat, int index) {
|
||||
auto w = width();
|
||||
auto photoLeft = (_rowWidth - (st::sharePhotoRadius * 2)) / 2;
|
||||
auto photoTop = st::sharePhotoTop;
|
||||
if (chat->selection.isNull()) {
|
||||
if (!chat->wideUserpicCache.isNull()) {
|
||||
chat->wideUserpicCache = QPixmap();
|
||||
}
|
||||
auto userpicRadius = chat->selected ? st::sharePhotoSmallRadius : st::sharePhotoRadius;
|
||||
auto userpicShift = st::sharePhotoRadius - userpicRadius;
|
||||
auto userpicLeft = x + photoLeft + userpicShift;
|
||||
auto userpicTop = y + photoTop + userpicShift;
|
||||
chat->peer->paintUserpicLeft(p, userpicRadius * 2, userpicLeft, userpicTop, w);
|
||||
} else {
|
||||
if (chat->selection.animating()) {
|
||||
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||
auto userpicRadius = qRound(WideCacheScale * (st::sharePhotoRadius + (st::sharePhotoSmallRadius - st::sharePhotoRadius) * selectionLevel));
|
||||
auto userpicShift = WideCacheScale * st::sharePhotoRadius - userpicRadius;
|
||||
@ -478,6 +470,15 @@ void ShareInner::paintChat(Painter &p, Chat *chat, int index) {
|
||||
auto from = QRect(QPoint(0, 0), chat->wideUserpicCache.size());
|
||||
p.drawPixmapLeft(to, w, chat->wideUserpicCache, from);
|
||||
p.setRenderHint(QPainter::SmoothPixmapTransform, false);
|
||||
} else {
|
||||
if (!chat->wideUserpicCache.isNull()) {
|
||||
chat->wideUserpicCache = QPixmap();
|
||||
}
|
||||
auto userpicRadius = chat->selected ? st::sharePhotoSmallRadius : st::sharePhotoRadius;
|
||||
auto userpicShift = st::sharePhotoRadius - userpicRadius;
|
||||
auto userpicLeft = x + photoLeft + userpicShift;
|
||||
auto userpicTop = y + photoTop + userpicShift;
|
||||
chat->peer->paintUserpicLeft(p, userpicRadius * 2, userpicLeft, userpicTop, w);
|
||||
}
|
||||
|
||||
if (selectionLevel > 0) {
|
||||
@ -516,11 +517,12 @@ void ShareInner::paintChat(Painter &p, Chat *chat, int index) {
|
||||
p.setRenderHint(QPainter::SmoothPixmapTransform, false);
|
||||
p.setOpacity(1.);
|
||||
|
||||
if (chat->nameFg.isNull()) {
|
||||
p.setPen((index == _active) ? st::shareNameActiveFg : st::shareNameFg);
|
||||
} else {
|
||||
if (chat->nameFg.animating()) {
|
||||
p.setPen(chat->nameFg.current());
|
||||
} else {
|
||||
p.setPen((index == _active) ? st::shareNameActiveFg : st::shareNameFg);
|
||||
}
|
||||
|
||||
auto nameWidth = (_rowWidth - st::shareColumnSkip);
|
||||
auto nameLeft = st::shareColumnSkip / 2;
|
||||
auto nameTop = photoTop + st::sharePhotoRadius * 2 + st::shareNameTop;
|
||||
@ -670,21 +672,21 @@ void ShareInner::changeCheckState(Chat *chat) {
|
||||
if (chat->selected) {
|
||||
_selected.insert(chat->peer);
|
||||
chat->icons.push_back(Chat::Icon());
|
||||
START_ANIMATION(chat->icons.back().fadeIn, func([this, chat] {
|
||||
chat->icons.back().fadeIn.start([this, chat] {
|
||||
repaintChat(chat->peer);
|
||||
}), 0, 1, st::shareSelectDuration, anim::linear);
|
||||
}, 0, 1, st::shareSelectDuration);
|
||||
} else {
|
||||
_selected.remove(chat->peer);
|
||||
prepareWideCheckIconCache(&chat->icons.back());
|
||||
START_ANIMATION(chat->icons.back().fadeOut, func([this, chat] {
|
||||
removeFadeOutedIcons(chat);
|
||||
chat->icons.back().fadeOut.start([this, chat] {
|
||||
repaintChat(chat->peer);
|
||||
}), 1, 0, st::shareSelectDuration, anim::linear);
|
||||
removeFadeOutedIcons(chat); // this call can destroy current lambda
|
||||
}, 1, 0, st::shareSelectDuration);
|
||||
}
|
||||
prepareWideUserpicCache(chat);
|
||||
START_ANIMATION(chat->selection, func([this, chat] {
|
||||
chat->selection.start([this, chat] {
|
||||
repaintChat(chat->peer);
|
||||
}), chat->selected ? 0 : 1, chat->selected ? 1 : 0, st::shareSelectDuration, anim_bumpy);
|
||||
}, chat->selected ? 0 : 1, chat->selected ? 1 : 0, st::shareSelectDuration, anim_bumpy);
|
||||
if (chat->selected) {
|
||||
setActive(chatIndex(chat->peer));
|
||||
}
|
||||
@ -692,9 +694,9 @@ void ShareInner::changeCheckState(Chat *chat) {
|
||||
}
|
||||
|
||||
void ShareInner::removeFadeOutedIcons(Chat *chat) {
|
||||
while (!chat->icons.empty() && chat->icons.front().fadeIn.isNull() && chat->icons.front().fadeOut.isNull()) {
|
||||
while (!chat->icons.empty() && !chat->icons.front().fadeIn.animating() && !chat->icons.front().fadeOut.animating()) {
|
||||
if (chat->icons.size() > 1 || !chat->selected) {
|
||||
chat->icons.pop_front();
|
||||
chat->icons.erase(chat->icons.begin());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -1016,18 +1018,6 @@ void shareGameScoreFromItem(HistoryItem *item) {
|
||||
Ui::showLayer(new ShareBox(std_::move(copyCallback), std_::move(submitCallback)));
|
||||
}
|
||||
|
||||
class GameMessageResolvedCallback : public SharedCallback<void, ChannelData*, MsgId> {
|
||||
public:
|
||||
void call(ChannelData *channel, MsgId msgId) const override {
|
||||
if (auto item = App::histItemById(channel, msgId)) {
|
||||
shareGameScoreFromItem(item);
|
||||
} else {
|
||||
Ui::showLayer(new InformBox(lang(lng_edit_deleted)));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void shareGameScoreByHash(const QString &hash) {
|
||||
@ -1062,7 +1052,13 @@ void shareGameScoreByHash(const QString &hash) {
|
||||
} else if (App::api()) {
|
||||
auto channel = channelId ? App::channelLoaded(channelId) : nullptr;
|
||||
if (channel || !channelId) {
|
||||
App::api()->requestMessageData(channel, msgId, std_::make_unique<GameMessageResolvedCallback>());
|
||||
App::api()->requestMessageData(channel, msgId, [](ChannelData *channel, MsgId msgId) {
|
||||
if (auto item = App::histItemById(channel, msgId)) {
|
||||
shareGameScoreFromItem(item);
|
||||
} else {
|
||||
Ui::showLayer(new InformBox(lang(lng_edit_deleted)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
#include "abstractbox.h"
|
||||
#include "core/lambda_wrap.h"
|
||||
#include "core/observer.h"
|
||||
#include "core/vector_of_moveable.h"
|
||||
|
||||
namespace Dialogs {
|
||||
class Row;
|
||||
@ -107,7 +108,7 @@ private:
|
||||
|
||||
namespace internal {
|
||||
|
||||
class ShareInner : public ScrolledWidget, public RPCSender, public Notify::Observer {
|
||||
class ShareInner : public ScrolledWidget, public RPCSender, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@ -163,7 +164,7 @@ private:
|
||||
FloatAnimation fadeOut;
|
||||
QPixmap wideCheckCache;
|
||||
};
|
||||
QList<Icon> icons;
|
||||
std_::vector_of_moveable<Icon> icons;
|
||||
};
|
||||
void paintChat(Painter &p, Chat *chat, int index);
|
||||
void updateChat(PeerData *peer);
|
||||
|
@ -41,7 +41,6 @@ constexpr int kArchivedLimitPerPage = 30;
|
||||
|
||||
StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : ScrolledWidget()
|
||||
, _input(set) {
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
switch (set.type()) {
|
||||
case mtpc_inputStickerSetID: _setId = set.c_inputStickerSetID().vid.v; _setAccess = set.c_inputStickerSetID().vaccess_hash.v; break;
|
||||
case mtpc_inputStickerSetShortName: _setShortName = qs(set.c_inputStickerSetShortName().vshort_name); break;
|
||||
@ -49,6 +48,8 @@ StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : ScrolledWidget
|
||||
MTP::send(MTPmessages_GetStickerSet(_input), rpcDone(&StickerSetInner::gotSet), rpcFail(&StickerSetInner::failedSet));
|
||||
App::main()->updateStickers();
|
||||
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
|
||||
setMouseTracking(true);
|
||||
|
||||
_previewTimer.setSingleShot(true);
|
||||
@ -62,8 +63,8 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
|
||||
_selected = -1;
|
||||
setCursor(style::cur_default);
|
||||
if (set.type() == mtpc_messages_stickerSet) {
|
||||
auto &d(set.c_messages_stickerSet());
|
||||
auto &v(d.vdocuments.c_vector().v);
|
||||
auto &d = set.c_messages_stickerSet();
|
||||
auto &v = d.vdocuments.c_vector().v;
|
||||
_pack.reserve(v.size());
|
||||
_packOvers.reserve(v.size());
|
||||
for (int i = 0, l = v.size(); i < l; ++i) {
|
||||
@ -247,13 +248,13 @@ void StickerSetInner::updateSelected() {
|
||||
|
||||
void StickerSetInner::startOverAnimation(int index, float64 from, float64 to) {
|
||||
if (index >= 0 && index < _packOvers.size()) {
|
||||
START_ANIMATION(_packOvers[index], func([this, index]() {
|
||||
_packOvers[index].start([this, index] {
|
||||
int row = index / StickerPanPerRow;
|
||||
int column = index % StickerPanPerRow;
|
||||
int left = st::stickersPadding.left() + column * st::stickersSize.width();
|
||||
int top = st::stickersPadding.top() + row * st::stickersSize.height();
|
||||
rtlupdate(left, top, st::stickersSize.width(), st::stickersSize.height());
|
||||
}), from, to, st::emojiPanDuration, anim::linear);
|
||||
}, from, to, st::emojiPanDuration);
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,7 +516,7 @@ StickersInner::StickersInner(const Stickers::Order &archivedIds) : ScrolledWidge
|
||||
}
|
||||
|
||||
void StickersInner::setup() {
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(onImageLoaded()));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
|
@ -21,10 +21,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
#pragma once
|
||||
|
||||
#include "abstractbox.h"
|
||||
#include "core/vector_of_moveable.h"
|
||||
|
||||
class ConfirmBox;
|
||||
|
||||
class StickerSetInner : public ScrolledWidget, public RPCSender {
|
||||
class StickerSetInner : public ScrolledWidget, public RPCSender, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@ -69,7 +70,7 @@ private:
|
||||
return (_setFlags & MTPDstickerSet::Flag::f_masks);
|
||||
}
|
||||
|
||||
QVector<FloatAnimation> _packOvers;
|
||||
std_::vector_of_moveable<FloatAnimation> _packOvers;
|
||||
StickerPack _pack;
|
||||
StickersByEmojiMap _emoji;
|
||||
bool _loaded = false;
|
||||
@ -206,7 +207,7 @@ int32 stickerPacksCount(bool includeDisabledOfficial = false);
|
||||
|
||||
namespace internal {
|
||||
|
||||
class StickersInner : public ScrolledWidget, public RPCSender {
|
||||
class StickersInner : public ScrolledWidget, public RPCSender, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -20,6 +20,17 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
#include <QtCore/QReadWriteLock>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#ifndef OS_MAC_OLD
|
||||
#include <memory>
|
||||
#endif // OS_MAC_OLD
|
||||
|
||||
template <typename T>
|
||||
void deleteAndMark(T *&link) {
|
||||
delete link;
|
||||
@ -259,13 +270,6 @@ typedef float float32;
|
||||
typedef double float64;
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
#include <QtCore/QReadWriteLock>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
using std::string;
|
||||
using std::exception;
|
||||
#ifdef OS_MAC_OLD
|
||||
@ -1035,13 +1039,15 @@ struct ComponentWrapStruct {
|
||||
// global scope, so it will be filled by zeros from the start
|
||||
ComponentWrapStruct() {
|
||||
}
|
||||
ComponentWrapStruct(int size, ComponentConstruct construct, ComponentDestruct destruct, ComponentMove move)
|
||||
ComponentWrapStruct(std::size_t size, std::size_t align, ComponentConstruct construct, ComponentDestruct destruct, ComponentMove move)
|
||||
: Size(size)
|
||||
, Align(align)
|
||||
, Construct(construct)
|
||||
, Destruct(destruct)
|
||||
, Move(move) {
|
||||
}
|
||||
int Size;
|
||||
std::size_t Size;
|
||||
std::size_t Align;
|
||||
ComponentConstruct Construct;
|
||||
ComponentDestruct Destruct;
|
||||
ComponentMove Move;
|
||||
@ -1058,6 +1064,7 @@ extern QAtomicInt ComponentIndexLast;
|
||||
template <typename Type>
|
||||
struct BaseComponent {
|
||||
BaseComponent() {
|
||||
static_assert(alignof(Type) <= alignof(SmallestSizeType), "Components should align to a pointer!");
|
||||
}
|
||||
BaseComponent(const BaseComponent &other) = delete;
|
||||
BaseComponent &operator=(const BaseComponent &other) = delete;
|
||||
@ -1075,8 +1082,11 @@ struct BaseComponent {
|
||||
t_assert(last < 64);
|
||||
if (_index.testAndSetOrdered(0, last + 1)) {
|
||||
ComponentWraps[last] = ComponentWrapStruct(
|
||||
CeilDivideMinimumOne<sizeof(Type), sizeof(uint64)>::Result * sizeof(uint64),
|
||||
Type::ComponentConstruct, Type::ComponentDestruct, Type::ComponentMove);
|
||||
CeilDivideMinimumOne<sizeof(Type), sizeof(SmallestSizeType)>::Result * sizeof(SmallestSizeType),
|
||||
alignof(Type),
|
||||
Type::ComponentConstruct,
|
||||
Type::ComponentDestruct,
|
||||
Type::ComponentMove);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1088,6 +1098,8 @@ struct BaseComponent {
|
||||
}
|
||||
|
||||
protected:
|
||||
using SmallestSizeType = void*;
|
||||
|
||||
static void ComponentConstruct(void *location, Composer *composer) {
|
||||
new (location) Type();
|
||||
}
|
||||
@ -1102,7 +1114,6 @@ protected:
|
||||
|
||||
class ComposerMetadata {
|
||||
public:
|
||||
|
||||
ComposerMetadata(uint64 mask) : size(0), last(64), _mask(mask) {
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
uint64 m = (1ULL << i);
|
||||
@ -1147,15 +1158,13 @@ const ComposerMetadata *GetComposerMetadata(uint64 mask);
|
||||
|
||||
class Composer {
|
||||
public:
|
||||
|
||||
Composer(uint64 mask = 0) : _data(zerodata()) {
|
||||
if (mask) {
|
||||
const ComposerMetadata *meta = GetComposerMetadata(mask);
|
||||
int size = sizeof(meta) + meta->size;
|
||||
void *data = operator new(size);
|
||||
if (!data) { // terminate if we can't allocate memory
|
||||
throw "Can't allocate memory!";
|
||||
}
|
||||
|
||||
auto data = operator new(size);
|
||||
t_assert(data != nullptr);
|
||||
|
||||
_data = data;
|
||||
_meta() = meta;
|
||||
@ -1163,7 +1172,13 @@ public:
|
||||
int offset = meta->offsets[i];
|
||||
if (offset >= 0) {
|
||||
try {
|
||||
ComponentWraps[i].Construct(_dataptrunsafe(offset), this);
|
||||
auto constructAt = _dataptrunsafe(offset);
|
||||
#ifndef OS_MAC_OLD
|
||||
auto space = ComponentWraps[i].Size;
|
||||
auto alignedAt = std::align(ComponentWraps[i].Align, space, constructAt, space);
|
||||
t_assert(alignedAt == constructAt);
|
||||
#endif // OS_MAC_OLD
|
||||
ComponentWraps[i].Construct(constructAt, this);
|
||||
} catch (...) {
|
||||
while (i > 0) {
|
||||
--i;
|
||||
@ -1182,7 +1197,7 @@ public:
|
||||
Composer &operator=(const Composer &other) = delete;
|
||||
~Composer() {
|
||||
if (_data != zerodata()) {
|
||||
const ComposerMetadata *meta = _meta();
|
||||
auto meta = _meta();
|
||||
for (int i = 0; i < meta->last; ++i) {
|
||||
int offset = meta->offsets[i];
|
||||
if (offset >= 0) {
|
||||
@ -1213,7 +1228,7 @@ protected:
|
||||
Composer tmp(mask);
|
||||
tmp.swap(*this);
|
||||
if (_data != zerodata() && tmp._data != zerodata()) {
|
||||
const ComposerMetadata *meta = _meta(), *wasmeta = tmp._meta();
|
||||
auto meta = _meta(), wasmeta = tmp._meta();
|
||||
for (int i = 0; i < meta->last; ++i) {
|
||||
int offset = meta->offsets[i], wasoffset = wasmeta->offsets[i];
|
||||
if (offset >= 0 && wasoffset >= 0) {
|
||||
@ -1252,103 +1267,3 @@ private:
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
class SharedCallback {
|
||||
public:
|
||||
virtual R call(Args... args) const = 0;
|
||||
virtual ~SharedCallback() {
|
||||
}
|
||||
using Ptr = QSharedPointer<SharedCallback<R, Args...>>;
|
||||
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
class FunctionImplementation {
|
||||
public:
|
||||
virtual R call(Args... args) = 0;
|
||||
virtual void destroy() { delete this; }
|
||||
virtual ~FunctionImplementation() {}
|
||||
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
class NullFunctionImplementation : public FunctionImplementation<R, Args...> {
|
||||
public:
|
||||
R call(Args... args) override { return R(); }
|
||||
void destroy() override {}
|
||||
static NullFunctionImplementation<R, Args...> SharedInstance;
|
||||
|
||||
};
|
||||
template <typename R, typename... Args>
|
||||
NullFunctionImplementation<R, Args...> NullFunctionImplementation<R, Args...>::SharedInstance;
|
||||
|
||||
template <typename R, typename... Args>
|
||||
class Function {
|
||||
public:
|
||||
Function() : _implementation(nullImpl()) {}
|
||||
Function(FunctionImplementation<R, Args...> *implementation) : _implementation(implementation) {}
|
||||
Function(const Function<R, Args...> &other) = delete;
|
||||
Function<R, Args...> &operator=(const Function<R, Args...> &other) = delete;
|
||||
Function(Function<R, Args...> &&other) : _implementation(other._implementation) {
|
||||
other._implementation = nullImpl();
|
||||
}
|
||||
Function<R, Args...> &operator=(Function<R, Args...> &&other) {
|
||||
std::swap(_implementation, other._implementation);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return (_implementation == nullImpl());
|
||||
}
|
||||
|
||||
R call(Args... args) { return _implementation->call(args...); }
|
||||
~Function() {
|
||||
if (_implementation) {
|
||||
_implementation->destroy();
|
||||
_implementation = nullptr;
|
||||
}
|
||||
deleteAndMark(_implementation);
|
||||
}
|
||||
|
||||
private:
|
||||
static FunctionImplementation<R, Args...> *nullImpl() {
|
||||
return &NullFunctionImplementation<R, Args...>::SharedInstance;
|
||||
}
|
||||
|
||||
FunctionImplementation<R, Args...> *_implementation;
|
||||
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
class WrappedFunction : public FunctionImplementation<R, Args...> {
|
||||
public:
|
||||
using Method = R(*)(Args... args);
|
||||
WrappedFunction(Method method) : _method(method) {}
|
||||
R call(Args... args) override { return (*_method)(args...); }
|
||||
|
||||
private:
|
||||
Method _method;
|
||||
|
||||
};
|
||||
template <typename R, typename... Args>
|
||||
inline Function<R, Args...> func(R(*method)(Args... args)) {
|
||||
return Function<R, Args...>(new WrappedFunction<R, Args...>(method));
|
||||
}
|
||||
|
||||
template <typename O, typename I, typename R, typename... Args>
|
||||
class ObjectFunction : public FunctionImplementation<R, Args...> {
|
||||
public:
|
||||
using Method = R(I::*)(Args... args);
|
||||
ObjectFunction(O *obj, Method method) : _obj(obj), _method(method) {}
|
||||
R call(Args... args) override { return (_obj->*_method)(args...); }
|
||||
|
||||
private:
|
||||
O *_obj;
|
||||
Method _method;
|
||||
|
||||
};
|
||||
template <typename O, typename I, typename R, typename... Args>
|
||||
inline Function<R, Args...> func(O *obj, R(I::*method)(Args...)) {
|
||||
return Function<R, Args...>(new ObjectFunction<O, I, R, Args...>(obj, method));
|
||||
}
|
||||
|
@ -20,6 +20,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifndef OS_MAC_OLD
|
||||
#include <memory>
|
||||
#endif // OS_MAC_OLD
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
@ -50,7 +54,7 @@ struct lambda_wrap_helper_base {
|
||||
const call_type call;
|
||||
const destruct_type destruct;
|
||||
|
||||
static constexpr size_t kFullStorageSize = 40U;
|
||||
static constexpr size_t kFullStorageSize = sizeof(void*) + 24U;
|
||||
static constexpr size_t kStorageSize = kFullStorageSize - sizeof(void*);
|
||||
|
||||
template <typename Lambda>
|
||||
@ -90,43 +94,47 @@ const lambda_wrap_empty<Return, Args...> lambda_wrap_empty<Return, Args...>::ins
|
||||
|
||||
template <typename Lambda, typename IsLarge, typename Return, typename ...Args> struct lambda_wrap_helper_move_impl;
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
struct lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...> : public lambda_wrap_helper_base<Return, Args...> {
|
||||
using JustLambda = std_::decay_simple_t<Lambda>;
|
||||
using LambdaPtr = std_::unique_ptr<JustLambda>;
|
||||
using Parent = lambda_wrap_helper_base<Return, Args...>;
|
||||
static void construct_move_other_method(void *lambda, void *source) {
|
||||
auto source_lambda = static_cast<LambdaPtr*>(source);
|
||||
new (lambda) LambdaPtr(std_::move(*source_lambda));
|
||||
}
|
||||
static void construct_move_lambda_method(void *lambda, void *source) {
|
||||
auto source_lambda = static_cast<JustLambda*>(source);
|
||||
new (lambda) LambdaPtr(std_::make_unique<JustLambda>(static_cast<JustLambda&&>(*source_lambda)));
|
||||
}
|
||||
static Return call_method(const void *lambda, Args... args) {
|
||||
return (**static_cast<const LambdaPtr*>(lambda))(std_::forward<Args>(args)...);
|
||||
}
|
||||
static void destruct_method(const void *lambda) {
|
||||
static_cast<const LambdaPtr*>(lambda)->~LambdaPtr();
|
||||
}
|
||||
lambda_wrap_helper_move_impl() : Parent(
|
||||
&Parent::bad_construct_copy,
|
||||
&lambda_wrap_helper_move_impl::construct_move_other_method,
|
||||
&lambda_wrap_helper_move_impl::call_method,
|
||||
&lambda_wrap_helper_move_impl::destruct_method) {
|
||||
}
|
||||
|
||||
protected:
|
||||
lambda_wrap_helper_move_impl(
|
||||
typename Parent::construct_copy_other_type construct_copy_other
|
||||
) : Parent(
|
||||
construct_copy_other,
|
||||
&lambda_wrap_helper_move_impl::construct_move_other_method,
|
||||
&lambda_wrap_helper_move_impl::call_method,
|
||||
&lambda_wrap_helper_move_impl::destruct_method) {
|
||||
}
|
||||
|
||||
};
|
||||
//
|
||||
// Disable large lambda support.
|
||||
// If you really need it, just store data in some std_::unique_ptr<struct>.
|
||||
//
|
||||
//template <typename Lambda, typename Return, typename ...Args>
|
||||
//struct lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...> : public lambda_wrap_helper_base<Return, Args...> {
|
||||
// using JustLambda = std_::decay_simple_t<Lambda>;
|
||||
// using LambdaPtr = std_::unique_ptr<JustLambda>;
|
||||
// using Parent = lambda_wrap_helper_base<Return, Args...>;
|
||||
// static void construct_move_other_method(void *lambda, void *source) {
|
||||
// auto source_lambda = static_cast<LambdaPtr*>(source);
|
||||
// new (lambda) LambdaPtr(std_::move(*source_lambda));
|
||||
// }
|
||||
// static void construct_move_lambda_method(void *lambda, void *source) {
|
||||
// auto source_lambda = static_cast<JustLambda*>(source);
|
||||
// new (lambda) LambdaPtr(std_::make_unique<JustLambda>(static_cast<JustLambda&&>(*source_lambda)));
|
||||
// }
|
||||
// static Return call_method(const void *lambda, Args... args) {
|
||||
// return (**static_cast<const LambdaPtr*>(lambda))(std_::forward<Args>(args)...);
|
||||
// }
|
||||
// static void destruct_method(const void *lambda) {
|
||||
// static_cast<const LambdaPtr*>(lambda)->~LambdaPtr();
|
||||
// }
|
||||
// lambda_wrap_helper_move_impl() : Parent(
|
||||
// &Parent::bad_construct_copy,
|
||||
// &lambda_wrap_helper_move_impl::construct_move_other_method,
|
||||
// &lambda_wrap_helper_move_impl::call_method,
|
||||
// &lambda_wrap_helper_move_impl::destruct_method) {
|
||||
// }
|
||||
//
|
||||
//protected:
|
||||
// lambda_wrap_helper_move_impl(
|
||||
// typename Parent::construct_copy_other_type construct_copy_other
|
||||
// ) : Parent(
|
||||
// construct_copy_other,
|
||||
// &lambda_wrap_helper_move_impl::construct_move_other_method,
|
||||
// &lambda_wrap_helper_move_impl::call_method,
|
||||
// &lambda_wrap_helper_move_impl::destruct_method) {
|
||||
// }
|
||||
//
|
||||
//};
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
struct lambda_wrap_helper_move_impl<Lambda, std_::false_type, Return, Args...> : public lambda_wrap_helper_base<Return, Args...> {
|
||||
@ -137,6 +145,12 @@ struct lambda_wrap_helper_move_impl<Lambda, std_::false_type, Return, Args...> :
|
||||
new (lambda) JustLambda(static_cast<JustLambda&&>(*source_lambda));
|
||||
}
|
||||
static void construct_move_lambda_method(void *lambda, void *source) {
|
||||
static_assert(alignof(JustLambda) <= alignof(void*), "Bad lambda alignment.");
|
||||
#ifndef OS_MAC_OLD
|
||||
auto space = sizeof(JustLambda);
|
||||
auto aligned = std::align(alignof(JustLambda), space, lambda, space);
|
||||
t_assert(aligned == lambda);
|
||||
#endif // OS_MAC_OLD
|
||||
auto source_lambda = static_cast<JustLambda*>(source);
|
||||
new (lambda) JustLambda(static_cast<JustLambda&&>(*source_lambda));
|
||||
}
|
||||
@ -177,23 +191,27 @@ const lambda_wrap_helper_move<Lambda, Return, Args...> lambda_wrap_helper_move<L
|
||||
|
||||
template <typename Lambda, typename IsLarge, typename Return, typename ...Args> struct lambda_wrap_helper_copy_impl;
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
struct lambda_wrap_helper_copy_impl<Lambda, std_::true_type, Return, Args...> : public lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...> {
|
||||
using JustLambda = std_::decay_simple_t<Lambda>;
|
||||
using LambdaPtr = std_::unique_ptr<JustLambda>;
|
||||
using Parent = lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...>;
|
||||
static void construct_copy_other_method(void *lambda, const void *source) {
|
||||
auto source_lambda = static_cast<const LambdaPtr*>(source);
|
||||
new (lambda) LambdaPtr(std_::make_unique<JustLambda>(*source_lambda->get()));
|
||||
}
|
||||
static void construct_copy_lambda_method(void *lambda, const void *source) {
|
||||
auto source_lambda = static_cast<const JustLambda*>(source);
|
||||
new (lambda) LambdaPtr(std_::make_unique<JustLambda>(static_cast<const JustLambda &>(*source_lambda)));
|
||||
}
|
||||
lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) {
|
||||
}
|
||||
|
||||
};
|
||||
//
|
||||
// Disable large lambda support.
|
||||
// If you really need it, just store data in some QSharedPointer<struct>.
|
||||
//
|
||||
//template <typename Lambda, typename Return, typename ...Args>
|
||||
//struct lambda_wrap_helper_copy_impl<Lambda, std_::true_type, Return, Args...> : public lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...> {
|
||||
// using JustLambda = std_::decay_simple_t<Lambda>;
|
||||
// using LambdaPtr = std_::unique_ptr<JustLambda>;
|
||||
// using Parent = lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...>;
|
||||
// static void construct_copy_other_method(void *lambda, const void *source) {
|
||||
// auto source_lambda = static_cast<const LambdaPtr*>(source);
|
||||
// new (lambda) LambdaPtr(std_::make_unique<JustLambda>(*source_lambda->get()));
|
||||
// }
|
||||
// static void construct_copy_lambda_method(void *lambda, const void *source) {
|
||||
// auto source_lambda = static_cast<const JustLambda*>(source);
|
||||
// new (lambda) LambdaPtr(std_::make_unique<JustLambda>(static_cast<const JustLambda &>(*source_lambda)));
|
||||
// }
|
||||
// lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) {
|
||||
// }
|
||||
//
|
||||
//};
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
struct lambda_wrap_helper_copy_impl<Lambda, std_::false_type, Return, Args...> : public lambda_wrap_helper_move_impl<Lambda, std_::false_type, Return, Args...> {
|
||||
@ -204,6 +222,12 @@ struct lambda_wrap_helper_copy_impl<Lambda, std_::false_type, Return, Args...> :
|
||||
new (lambda) JustLambda(static_cast<const JustLambda &>(*source_lambda));
|
||||
}
|
||||
static void construct_copy_lambda_method(void *lambda, const void *source) {
|
||||
static_assert(alignof(JustLambda) <= alignof(void*), "Bad lambda alignment.");
|
||||
#ifndef OS_MAC_OLD
|
||||
auto space = sizeof(JustLambda);
|
||||
auto aligned = std::align(alignof(JustLambda), space, lambda, space);
|
||||
t_assert(aligned == lambda);
|
||||
#endif // OS_MAC_OLD
|
||||
auto source_lambda = static_cast<const JustLambda*>(source);
|
||||
new (lambda) JustLambda(static_cast<const JustLambda &>(*source_lambda));
|
||||
}
|
||||
@ -356,7 +380,7 @@ public:
|
||||
auto temp = other;
|
||||
this->helper_->destruct(this->storage_);
|
||||
this->helper_ = &internal::lambda_wrap_helper_copy<Lambda, Return, Args...>::instance;
|
||||
internal::lambda_wrap_helper_copy<Lambda, Return, Args...>::construct_move_lambda_method(this->storage_, &other);
|
||||
internal::lambda_wrap_helper_copy<Lambda, Return, Args...>::construct_copy_lambda_method(this->storage_, &other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -373,48 +397,20 @@ public:
|
||||
|
||||
} // namespace base
|
||||
|
||||
// While we still use Function<>
|
||||
// While we still use rpcDone() and rpcFail()
|
||||
|
||||
#include "mtproto/rpc_sender.h"
|
||||
|
||||
template <typename FunctionType>
|
||||
struct LambdaFunctionHelper;
|
||||
struct LambdaUniqueHelper;
|
||||
|
||||
template <typename Lambda, typename R, typename ...Args>
|
||||
struct LambdaFunctionHelper<R(Lambda::*)(Args...) const> {
|
||||
using FunctionType = Function<R, Args...>;
|
||||
struct LambdaUniqueHelper<R(Lambda::*)(Args...) const> {
|
||||
using UniqueType = base::lambda_unique<R(Args...)>;
|
||||
};
|
||||
|
||||
template <typename FunctionType>
|
||||
using LambdaGetFunction = typename LambdaFunctionHelper<FunctionType>::FunctionType;
|
||||
|
||||
template <typename FunctionType>
|
||||
using LambdaGetUnique = typename LambdaFunctionHelper<FunctionType>::UniqueType;
|
||||
|
||||
template <typename R, typename ...Args>
|
||||
class LambdaFunctionImplementation : public FunctionImplementation<R, Args...> {
|
||||
public:
|
||||
LambdaFunctionImplementation(base::lambda_unique<R(Args...)> &&lambda) : _lambda(std_::move(lambda)) {
|
||||
}
|
||||
R call(Args... args) override { return _lambda(std_::forward<Args>(args)...); }
|
||||
|
||||
private:
|
||||
base::lambda_unique<R(Args...)> _lambda;
|
||||
|
||||
};
|
||||
|
||||
template <typename R, typename ...Args>
|
||||
inline Function<R, Args...> func_lambda_wrap_helper(base::lambda_unique<R(Args...)> &&lambda) {
|
||||
return Function<R, Args...>(new LambdaFunctionImplementation<R, Args...>(std_::move(lambda)));
|
||||
}
|
||||
|
||||
template <typename Lambda, typename = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::value>>
|
||||
inline LambdaGetFunction<decltype(&Lambda::operator())> func(Lambda &&lambda) {
|
||||
return func_lambda_wrap_helper(LambdaGetUnique<decltype(&Lambda::operator())>(std_::move(lambda)));
|
||||
}
|
||||
|
||||
// While we still use rpcDone() and rpcFail()
|
||||
|
||||
#include "mtproto/rpc_sender.h"
|
||||
using LambdaGetUnique = typename LambdaUniqueHelper<FunctionType>::UniqueType;
|
||||
|
||||
template <typename Base, typename FunctionType>
|
||||
class RPCHandlerImplementation : public Base {
|
||||
|
@ -21,99 +21,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
#include "stdafx.h"
|
||||
#include "core/observer.h"
|
||||
|
||||
namespace Notify {
|
||||
namespace internal {
|
||||
namespace {
|
||||
|
||||
struct StartCallbackData {
|
||||
void *that;
|
||||
StartCallback call;
|
||||
};
|
||||
struct FinishCallbackData {
|
||||
void *that;
|
||||
FinishCallback call;
|
||||
};
|
||||
struct UnregisterCallbackData {
|
||||
void *that;
|
||||
UnregisterCallback call;
|
||||
};
|
||||
using StartCallbacksList = QVector<StartCallbackData>;
|
||||
using FinishCallbacksList = QVector<FinishCallbackData>;
|
||||
NeverFreedPointer<StartCallbacksList> StartCallbacks;
|
||||
NeverFreedPointer<FinishCallbacksList> FinishCallbacks;
|
||||
UnregisterCallbackData UnregisterCallbacks[256]/* = { nullptr }*/;
|
||||
|
||||
ObservedEvent LastRegisteredEvent/* = 0*/;
|
||||
|
||||
} // namespace
|
||||
} // namespace internal
|
||||
|
||||
void startObservers() {
|
||||
if (!internal::StartCallbacks) return;
|
||||
|
||||
for (auto &callback : *internal::StartCallbacks) {
|
||||
callback.call(callback.that);
|
||||
}
|
||||
}
|
||||
|
||||
void finishObservers() {
|
||||
if (!internal::FinishCallbacks) return;
|
||||
|
||||
for (auto &callback : *internal::FinishCallbacks) {
|
||||
callback.call(callback.that);
|
||||
}
|
||||
internal::StartCallbacks.clear();
|
||||
internal::FinishCallbacks.clear();
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
BaseObservedEventRegistrator::BaseObservedEventRegistrator(void *that
|
||||
, StartCallback startCallback
|
||||
, FinishCallback finishCallback
|
||||
, UnregisterCallback unregisterCallback) {
|
||||
_event = LastRegisteredEvent++;
|
||||
|
||||
StartCallbacks.makeIfNull();
|
||||
StartCallbacks->push_back({ that, startCallback });
|
||||
|
||||
FinishCallbacks.makeIfNull();
|
||||
FinishCallbacks->push_back({ that, finishCallback });
|
||||
|
||||
UnregisterCallbacks[_event] = { that, unregisterCallback };
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// Observer base interface.
|
||||
Observer::~Observer() {
|
||||
for_const (auto connection, _connections) {
|
||||
unregisterObserver(connection);
|
||||
}
|
||||
}
|
||||
|
||||
void Observer::observerRegistered(ConnectionId connection) {
|
||||
_connections.push_back(connection);
|
||||
}
|
||||
|
||||
void unregisterObserver(ConnectionId connection) {
|
||||
auto event = static_cast<internal::ObservedEvent>(connection >> 24);
|
||||
auto connectionIndex = int(connection & 0x00FFFFFFU) - 1;
|
||||
auto &callback = internal::UnregisterCallbacks[event];
|
||||
if (connectionIndex >= 0 && callback.call && callback.that) {
|
||||
callback.call(callback.that, connectionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
void observerRegisteredDefault(Observer *observer, ConnectionId connection) {
|
||||
observer->observerRegistered(connection);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Notify
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
namespace {
|
||||
|
@ -22,230 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include "core/vector_of_moveable.h"
|
||||
|
||||
namespace Notify {
|
||||
|
||||
using ConnectionId = uint32;
|
||||
|
||||
// startObservers() must be called after main() started (not in a global variable constructor).
|
||||
// finishObservers() must be called before main() finished (not in a global variable destructor).
|
||||
void startObservers();
|
||||
void finishObservers();
|
||||
|
||||
using StartObservedEventCallback = void(*)();
|
||||
using FinishObservedEventCallback = void(*)();
|
||||
|
||||
namespace internal {
|
||||
|
||||
using ObservedEvent = uchar;
|
||||
using StartCallback = void(*)(void*);
|
||||
using FinishCallback = void(*)(void*);
|
||||
using UnregisterCallback = void(*)(void*,int connectionIndex);
|
||||
|
||||
class BaseObservedEventRegistrator {
|
||||
public:
|
||||
BaseObservedEventRegistrator(void *that
|
||||
, StartCallback startCallback
|
||||
, FinishCallback finishCallback
|
||||
, UnregisterCallback unregisterCallback);
|
||||
|
||||
protected:
|
||||
inline ObservedEvent event() const {
|
||||
return _event;
|
||||
}
|
||||
|
||||
private:
|
||||
ObservedEvent _event;
|
||||
|
||||
};
|
||||
|
||||
// Handler is one of Function<> instantiations.
|
||||
template <typename Flags, typename Handler>
|
||||
struct ObserversList {
|
||||
struct Entry {
|
||||
Flags flags;
|
||||
Handler handler;
|
||||
};
|
||||
std_::vector_of_moveable<Entry> entries;
|
||||
QVector<int> freeIndices;
|
||||
};
|
||||
|
||||
// If no filtering by flags is done, you can use Flags=int and this value.
|
||||
constexpr int UniversalFlag = 0x01;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// Objects of this class should be constructed in global scope.
|
||||
// startCallback will be called from Notify::startObservers().
|
||||
// finishCallback will be called from Notify::finishObservers().
|
||||
template <typename Flags, typename Handler>
|
||||
class ObservedEventRegistrator : public internal::BaseObservedEventRegistrator {
|
||||
public:
|
||||
ObservedEventRegistrator(StartObservedEventCallback startCallback,
|
||||
FinishObservedEventCallback finishCallback) : internal::BaseObservedEventRegistrator(static_cast<void*>(this),
|
||||
ObservedEventRegistrator<Flags, Handler>::start,
|
||||
ObservedEventRegistrator<Flags, Handler>::finish,
|
||||
ObservedEventRegistrator<Flags, Handler>::unregister)
|
||||
, _startCallback(startCallback), _finishCallback(finishCallback) {
|
||||
}
|
||||
|
||||
bool started() const {
|
||||
return _list != nullptr;
|
||||
}
|
||||
|
||||
ConnectionId registerObserver(Flags flags, Handler &&handler) {
|
||||
t_assert(started());
|
||||
|
||||
int connectionIndex = doRegisterObserver(flags, std_::forward<Handler>(handler));
|
||||
return (static_cast<uint32>(event()) << 24) | static_cast<uint32>(connectionIndex + 1);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void notify(Flags flags, Args&&... args) {
|
||||
t_assert(started());
|
||||
|
||||
auto &entries = _list->entries;
|
||||
// This way of iterating (i < entries.size() should be used
|
||||
// because some entries can be removed from the end of the
|
||||
// entries list while the loop is still running.
|
||||
for (int i = 0; i < entries.size(); ++i) {
|
||||
auto &entry = entries[i];
|
||||
if (!entry.handler.isNull() && (flags & entry.flags)) {
|
||||
entry.handler.call(std_::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
using Self = ObservedEventRegistrator<Flags, Handler>;
|
||||
static void start(void *vthat) {
|
||||
Self *that = static_cast<Self*>(vthat);
|
||||
|
||||
t_assert(!that->started());
|
||||
if (that->_startCallback) that->_startCallback();
|
||||
that->_list = new internal::ObserversList<Flags, Handler>();
|
||||
}
|
||||
static void finish(void *vthat) {
|
||||
Self *that = static_cast<Self*>(vthat);
|
||||
|
||||
if (that->_finishCallback) that->_finishCallback();
|
||||
delete that->_list;
|
||||
that->_list = nullptr;
|
||||
}
|
||||
static void unregister(void *vthat, int connectionIndex) {
|
||||
Self *that = static_cast<Self*>(vthat);
|
||||
|
||||
t_assert(that->started());
|
||||
|
||||
auto &entries = that->_list->entries;
|
||||
if (entries.size() <= connectionIndex) return;
|
||||
|
||||
if (entries.size() == connectionIndex + 1) {
|
||||
for (entries.pop_back(); !entries.isEmpty() && entries.back().handler.isNull();) {
|
||||
entries.pop_back();
|
||||
}
|
||||
} else {
|
||||
entries[connectionIndex].handler = Handler();
|
||||
that->_list->freeIndices.push_back(connectionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
int doRegisterObserver(Flags flags, Handler &&handler) {
|
||||
while (!_list->freeIndices.isEmpty()) {
|
||||
auto freeIndex = _list->freeIndices.back();
|
||||
_list->freeIndices.pop_back();
|
||||
|
||||
if (freeIndex < _list->entries.size()) {
|
||||
_list->entries[freeIndex] = { flags, std_::move(handler) };
|
||||
return freeIndex;
|
||||
}
|
||||
}
|
||||
_list->entries.push_back({ flags, std_::move(handler) });
|
||||
return _list->entries.size() - 1;
|
||||
}
|
||||
|
||||
StartObservedEventCallback _startCallback;
|
||||
FinishObservedEventCallback _finishCallback;
|
||||
internal::ObserversList<Flags, Handler> *_list = nullptr;
|
||||
|
||||
};
|
||||
|
||||
// If no filtering of notifications by Flags is intended use this class.
|
||||
template <typename Handler>
|
||||
class SimpleObservedEventRegistrator {
|
||||
public:
|
||||
SimpleObservedEventRegistrator(StartObservedEventCallback startCallback,
|
||||
FinishObservedEventCallback finishCallback) : _implementation(startCallback, finishCallback) {
|
||||
}
|
||||
|
||||
bool started() const {
|
||||
return _implementation.started();
|
||||
}
|
||||
|
||||
ConnectionId registerObserver(Handler &&handler) {
|
||||
return _implementation.registerObserver(internal::UniversalFlag, std_::forward<Handler>(handler));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void notify(Args&&... args) {
|
||||
return _implementation.notify(internal::UniversalFlag, std_::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
ObservedEventRegistrator<int, Handler> _implementation;
|
||||
|
||||
};
|
||||
|
||||
// Each observer type should have observerRegistered(Notify::ConnectionId connection) method.
|
||||
// Usually it is done by deriving the type from the Notify::Observer base class.
|
||||
// In destructor it should call Notify::unregisterObserver(connection) for all the connections.
|
||||
|
||||
class Observer;
|
||||
namespace internal {
|
||||
void observerRegisteredDefault(Observer *observer, ConnectionId connection);
|
||||
} // namespace internal
|
||||
|
||||
void unregisterObserver(ConnectionId connection);
|
||||
|
||||
class Observer {
|
||||
public:
|
||||
virtual ~Observer() = 0;
|
||||
|
||||
private:
|
||||
void observerRegistered(ConnectionId connection);
|
||||
friend void internal::observerRegisteredDefault(Observer *observer, ConnectionId connection);
|
||||
|
||||
QVector<ConnectionId> _connections;
|
||||
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename ObserverType, int>
|
||||
struct ObserverRegisteredGeneric {
|
||||
static inline void call(ObserverType *observer, ConnectionId connection) {
|
||||
observer->observerRegistered(connection);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ObserverType>
|
||||
struct ObserverRegisteredGeneric<ObserverType, true> {
|
||||
static inline void call(ObserverType *observer, ConnectionId connection) {
|
||||
observerRegisteredDefault(observer, connection);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename ObserverType>
|
||||
inline void observerRegistered(ObserverType *observer, ConnectionId connection) {
|
||||
// For derivatives of the Observer class we call special friend function observerRegistered().
|
||||
// For all other classes we call just a member function observerRegistered().
|
||||
using ObserverRegistered = internal::ObserverRegisteredGeneric<ObserverType, std_::is_base_of<Observer, ObserverType>::value>;
|
||||
ObserverRegistered::call(observer, connection);
|
||||
}
|
||||
|
||||
} // namespace Notify
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
@ -271,10 +47,10 @@ using SubscriptionHandler = typename SubscriptionHandlerHelper<EventType>::type;
|
||||
class BaseObservableData {
|
||||
};
|
||||
|
||||
template <typename EventType>
|
||||
template <typename EventType, typename Handler>
|
||||
class CommonObservableData;
|
||||
|
||||
template <typename EventType>
|
||||
template <typename EventType, typename Handler>
|
||||
class ObservableData;
|
||||
|
||||
} // namespace internal
|
||||
@ -317,43 +93,41 @@ private:
|
||||
Node *_node = nullptr;
|
||||
RemoveMethod _removeMethod;
|
||||
|
||||
template <typename EventType>
|
||||
template <typename EventType, typename Handler>
|
||||
friend class internal::CommonObservableData;
|
||||
|
||||
template <typename EventType>
|
||||
template <typename EventType, typename Handler>
|
||||
friend class internal::ObservableData;
|
||||
|
||||
};
|
||||
|
||||
template <typename EventType>
|
||||
template <typename EventType, typename Handler>
|
||||
class Observable;
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename EventType>
|
||||
template <typename EventType, typename Handler>
|
||||
class CommonObservable {
|
||||
public:
|
||||
using Handler = typename CommonObservableData<EventType>::Handler;
|
||||
|
||||
Subscription add_subscription(Handler &&handler) {
|
||||
if (!_data) {
|
||||
_data = MakeShared<ObservableData<EventType>>(this);
|
||||
_data = MakeShared<ObservableData<EventType, Handler>>(this);
|
||||
}
|
||||
return _data->append(std_::forward<Handler>(handler));
|
||||
}
|
||||
|
||||
private:
|
||||
QSharedPointer<ObservableData<EventType>> _data;
|
||||
QSharedPointer<ObservableData<EventType, Handler>> _data;
|
||||
|
||||
friend class CommonObservableData<EventType>;
|
||||
friend class Observable<EventType>;
|
||||
friend class CommonObservableData<EventType, Handler>;
|
||||
friend class Observable<EventType, Handler>;
|
||||
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename EventType>
|
||||
class Observable : public internal::CommonObservable<EventType> {
|
||||
template <typename EventType, typename Handler = internal::SubscriptionHandler<EventType>>
|
||||
class Observable : public internal::CommonObservable<EventType, Handler> {
|
||||
public:
|
||||
void notify(EventType &&event, bool sync = false) {
|
||||
if (this->_data) {
|
||||
@ -368,12 +142,10 @@ public:
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename EventType>
|
||||
template <typename EventType, typename Handler>
|
||||
class CommonObservableData : public BaseObservableData {
|
||||
public:
|
||||
using Handler = SubscriptionHandler<EventType>;
|
||||
|
||||
CommonObservableData(CommonObservable<EventType> *observable) : _observable(observable) {
|
||||
CommonObservableData(CommonObservable<EventType, Handler> *observable) : _observable(observable) {
|
||||
}
|
||||
|
||||
Subscription append(Handler &&handler) {
|
||||
@ -444,20 +216,20 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
CommonObservable<EventType> *_observable = nullptr;
|
||||
CommonObservable<EventType, Handler> *_observable = nullptr;
|
||||
Node *_begin = nullptr;
|
||||
Node *_current = nullptr;
|
||||
Node *_end = nullptr;
|
||||
ObservableCallHandlers _callHandlers;
|
||||
|
||||
friend class ObservableData<EventType>;
|
||||
friend class ObservableData<EventType, Handler>;
|
||||
|
||||
};
|
||||
|
||||
template <typename EventType>
|
||||
class ObservableData : public CommonObservableData<EventType> {
|
||||
template <typename EventType, typename Handler>
|
||||
class ObservableData : public CommonObservableData<EventType, Handler> {
|
||||
public:
|
||||
using CommonObservableData<EventType>::CommonObservableData;
|
||||
using CommonObservableData<EventType, Handler>::CommonObservableData;
|
||||
|
||||
void notify(EventType &&event, bool sync) {
|
||||
if (_handling) {
|
||||
@ -501,10 +273,10 @@ private:
|
||||
|
||||
};
|
||||
|
||||
template <>
|
||||
class ObservableData<void> : public CommonObservableData<void> {
|
||||
template <class Handler>
|
||||
class ObservableData<void, Handler> : public CommonObservableData<void, Handler> {
|
||||
public:
|
||||
using CommonObservableData<void>::CommonObservableData;
|
||||
using CommonObservableData<void, Handler>::CommonObservableData;
|
||||
|
||||
void notify(bool sync) {
|
||||
if (_handling) {
|
||||
@ -514,20 +286,20 @@ public:
|
||||
++_eventsCount;
|
||||
callHandlers();
|
||||
} else {
|
||||
if (!_callHandlers) {
|
||||
_callHandlers = [this]() {
|
||||
if (!this->_callHandlers) {
|
||||
this->_callHandlers = [this]() {
|
||||
callHandlers();
|
||||
};
|
||||
}
|
||||
if (!_eventsCount) {
|
||||
RegisterPendingObservable(&_callHandlers);
|
||||
RegisterPendingObservable(&this->_callHandlers);
|
||||
}
|
||||
++_eventsCount;
|
||||
}
|
||||
}
|
||||
|
||||
~ObservableData() {
|
||||
UnregisterObservable(&_callHandlers);
|
||||
UnregisterObservable(&this->_callHandlers);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -535,12 +307,12 @@ private:
|
||||
_handling = true;
|
||||
auto eventsCount = createAndSwap(_eventsCount);
|
||||
for (int i = 0; i != eventsCount; ++i) {
|
||||
notifyEnumerate([this]() {
|
||||
_current->handler();
|
||||
this->notifyEnumerate([this]() {
|
||||
this->_current->handler();
|
||||
});
|
||||
}
|
||||
_handling = false;
|
||||
UnregisterActiveObservable(&_callHandlers);
|
||||
UnregisterActiveObservable(&this->_callHandlers);
|
||||
}
|
||||
|
||||
int _eventsCount = 0;
|
||||
@ -550,12 +322,12 @@ private:
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <>
|
||||
class Observable<void> : public internal::CommonObservable<void> {
|
||||
template <typename Handler>
|
||||
class Observable<void, Handler> : public internal::CommonObservable<void, Handler> {
|
||||
public:
|
||||
void notify(bool sync = false) {
|
||||
if (_data) {
|
||||
_data->notify(sync);
|
||||
if (this->_data) {
|
||||
this->_data->notify(sync);
|
||||
}
|
||||
}
|
||||
|
||||
@ -563,14 +335,14 @@ public:
|
||||
|
||||
class Subscriber {
|
||||
protected:
|
||||
template <typename EventType, typename Lambda>
|
||||
int subscribe(base::Observable<EventType> &observable, Lambda &&handler) {
|
||||
template <typename EventType, typename Handler, typename Lambda>
|
||||
int subscribe(base::Observable<EventType, Handler> &observable, Lambda &&handler) {
|
||||
_subscriptions.push_back(observable.add_subscription(std_::forward<Lambda>(handler)));
|
||||
return _subscriptions.size() - 1;
|
||||
}
|
||||
|
||||
template <typename EventType, typename Lambda>
|
||||
int subscribe(base::Observable<EventType> *observable, Lambda &&handler) {
|
||||
template <typename EventType, typename Handler, typename Lambda>
|
||||
int subscribe(base::Observable<EventType, Handler> *observable, Lambda &&handler) {
|
||||
return subscribe(*observable, std_::forward<Lambda>(handler));
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,7 @@ public:
|
||||
*prev = std_::move(*next);
|
||||
}
|
||||
--_size;
|
||||
end()->~T();
|
||||
return it;
|
||||
}
|
||||
|
||||
@ -143,15 +144,21 @@ public:
|
||||
}
|
||||
inline const T &at(int index) const {
|
||||
if (index < 0 || index >= _size) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
|
||||
throw std::exception();
|
||||
#else // QT_VERSION < 5.5.0
|
||||
#ifndef OS_MAC_OLD
|
||||
throw std::out_of_range("");
|
||||
#endif // QT_VERSION < 5.5.0
|
||||
#else // OS_MAC_OLD
|
||||
throw std::exception();
|
||||
#endif // OS_MAC_OLD
|
||||
}
|
||||
return data()[index];
|
||||
}
|
||||
|
||||
void reserve(int newCapacity) {
|
||||
if (newCapacity > _capacity) {
|
||||
reallocate(newCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
inline ~vector_of_moveable() {
|
||||
clear();
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include "core/basic_types.h"
|
||||
|
||||
#define BETA_VERSION_MACRO (0ULL)
|
||||
#define BETA_VERSION_MACRO (10008001ULL)
|
||||
|
||||
constexpr int AppVersion = 10008;
|
||||
constexpr str_const AppVersionStr = "0.10.8";
|
||||
|
@ -46,13 +46,15 @@ DialogsInner::DialogsInner(QWidget *parent, MainWidget *main) : SplittedWidget(p
|
||||
if (Global::DialogsModeEnabled()) {
|
||||
importantDialogs = std_::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Date);
|
||||
}
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
connect(main, SIGNAL(peerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)));
|
||||
connect(main, SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(onPeerPhotoChanged(PeerData*)));
|
||||
connect(main, SIGNAL(dialogRowReplaced(Dialogs::Row*,Dialogs::Row*)), this, SLOT(onDialogRowReplaced(Dialogs::Row*,Dialogs::Row*)));
|
||||
connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact()));
|
||||
connect(&_cancelSearchInPeer, SIGNAL(clicked()), this, SIGNAL(cancelSearchInPeer()));
|
||||
_cancelSearchInPeer.hide();
|
||||
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ enum DialogsSearchRequestType {
|
||||
DialogsSearchMigratedFromOffset,
|
||||
};
|
||||
|
||||
class DialogsInner : public SplittedWidget, public RPCSender {
|
||||
class DialogsInner : public SplittedWidget, public RPCSender, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
|
||||
class LayerWidget;
|
||||
namespace base {
|
||||
template <typename Type>
|
||||
template <typename Type, typename>
|
||||
class Observable;
|
||||
} // namespace base
|
||||
namespace InlineBots {
|
||||
|
@ -574,6 +574,15 @@ History *Histories::find(const PeerId &peerId) {
|
||||
return (i == map.cend()) ? 0 : i.value();
|
||||
}
|
||||
|
||||
History *Histories::findOrInsert(const PeerId &peerId) {
|
||||
auto i = map.constFind(peerId);
|
||||
if (i == map.cend()) {
|
||||
auto history = peerIsChannel(peerId) ? static_cast<History*>(new ChannelHistory(peerId)) : (new History(peerId));
|
||||
i = map.insert(peerId, history);
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
History *Histories::findOrInsert(const PeerId &peerId, int32 unreadCount, int32 maxInboxRead, int32 maxOutboxRead) {
|
||||
auto i = map.constFind(peerId);
|
||||
if (i == map.cend()) {
|
||||
@ -584,10 +593,10 @@ History *Histories::findOrInsert(const PeerId &peerId, int32 unreadCount, int32
|
||||
history->outboxReadBefore = maxOutboxRead + 1;
|
||||
} else {
|
||||
auto history = i.value();
|
||||
if (unreadCount >= history->unreadCount()) {
|
||||
if (unreadCount > history->unreadCount()) {
|
||||
history->setUnreadCount(unreadCount);
|
||||
history->inboxReadBefore = maxInboxRead + 1;
|
||||
}
|
||||
accumulate_max(history->inboxReadBefore, maxInboxRead + 1);
|
||||
accumulate_max(history->outboxReadBefore, maxOutboxRead + 1);
|
||||
}
|
||||
return i.value();
|
||||
@ -1442,8 +1451,8 @@ MsgId History::inboxRead(MsgId upTo) {
|
||||
|
||||
updateChatListEntry();
|
||||
if (peer->migrateTo()) {
|
||||
if (History *h = App::historyLoaded(peer->migrateTo()->id)) {
|
||||
h->updateChatListEntry();
|
||||
if (auto migrateTo = App::historyLoaded(peer->migrateTo()->id)) {
|
||||
migrateTo->updateChatListEntry();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2635,10 +2644,12 @@ void HistoryMessageReplyMarkup::create(const MTPReplyMarkup &markup) {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryDependentItemCallback::call(ChannelData *channel, MsgId msgId) const {
|
||||
if (HistoryItem *item = App::histItemById(_dependent)) {
|
||||
item->updateDependencyItem();
|
||||
}
|
||||
ApiWrap::RequestMessageDataCallback historyDependentItemCallback(const FullMsgId &msgId) {
|
||||
return [dependent = msgId](ChannelData *channel, MsgId msgId) {
|
||||
if (HistoryItem *item = App::histItemById(dependent)) {
|
||||
item->updateDependencyItem();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void HistoryMessageUnreadBar::init(int count) {
|
||||
@ -4901,7 +4912,9 @@ bool HistoryGif::playInline(bool autoplay) {
|
||||
if (!cAutoPlayGif()) {
|
||||
App::stopGifItems();
|
||||
}
|
||||
_gif = new Media::Clip::Reader(_data->location(), _data->data(), func(_parent, &HistoryItem::clipCallback));
|
||||
_gif = new Media::Clip::Reader(_data->location(), _data->data(), [this](Media::Clip::Notification notification) {
|
||||
_parent->clipCallback(notification);
|
||||
});
|
||||
App::regGifItem(_gif, _parent);
|
||||
if (gif()) _gif->setAutoplay();
|
||||
}
|
||||
@ -6828,7 +6841,7 @@ void HistoryMessage::createComponents(const CreateConfig &config) {
|
||||
if (auto reply = Get<HistoryMessageReply>()) {
|
||||
reply->replyToMsgId = config.replyTo;
|
||||
if (!reply->updateData(this) && App::api()) {
|
||||
App::api()->requestMessageData(history()->peer->asChannel(), reply->replyToMsgId, std_::make_unique<HistoryDependentItemCallback>(fullId()));
|
||||
App::api()->requestMessageData(history()->peer->asChannel(), reply->replyToMsgId, historyDependentItemCallback(fullId()));
|
||||
}
|
||||
}
|
||||
if (auto via = Get<HistoryMessageVia>()) {
|
||||
@ -8379,7 +8392,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||
if (auto dependent = GetDependentData()) {
|
||||
dependent->msgId = message.vreply_to_msg_id.v;
|
||||
if (!updateDependent() && App::api()) {
|
||||
App::api()->requestMessageData(history()->peer->asChannel(), dependent->msgId, std_::make_unique<HistoryDependentItemCallback>(fullId()));
|
||||
App::api()->requestMessageData(history()->peer->asChannel(), dependent->msgId, historyDependentItemCallback(fullId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ public:
|
||||
void step_typings(uint64 ms, bool timer);
|
||||
|
||||
History *find(const PeerId &peerId);
|
||||
History *findOrInsert(const PeerId &peerId);
|
||||
History *findOrInsert(const PeerId &peerId, int32 unreadCount, int32 maxInboxRead, int32 maxOutboxRead);
|
||||
|
||||
void clear();
|
||||
@ -934,17 +935,6 @@ private:
|
||||
StylePtr _st;
|
||||
};
|
||||
|
||||
class HistoryDependentItemCallback : public SharedCallback<void, ChannelData*, MsgId> {
|
||||
public:
|
||||
HistoryDependentItemCallback(FullMsgId dependent) : _dependent(dependent) {
|
||||
}
|
||||
void call(ChannelData *channel, MsgId msgId) const override;
|
||||
|
||||
private:
|
||||
FullMsgId _dependent;
|
||||
|
||||
};
|
||||
|
||||
// any HistoryItem can have this Interface for
|
||||
// displaying the day mark above the message
|
||||
struct HistoryMessageDate : public BaseComponent<HistoryMessageDate> {
|
||||
|
@ -44,8 +44,6 @@ FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent)
|
||||
connect(_inner, SIGNAL(stickerChosen(DocumentData*,FieldAutocomplete::ChooseMethod)), this, SIGNAL(stickerChosen(DocumentData*,FieldAutocomplete::ChooseMethod)));
|
||||
connect(_inner, SIGNAL(mustScrollTo(int, int)), _scroll, SLOT(scrollToY(int, int)));
|
||||
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), _inner, SLOT(update()));
|
||||
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
_scroll->setFocusPolicy(Qt::NoFocus);
|
||||
_scroll->viewport()->setFocusPolicy(Qt::NoFocus);
|
||||
@ -539,6 +537,7 @@ FieldAutocompleteInner::FieldAutocompleteInner(FieldAutocomplete *parent, Mentio
|
||||
, _previewShown(false) {
|
||||
_previewTimer.setSingleShot(true);
|
||||
connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview()));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
}
|
||||
|
||||
void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
|
||||
@ -933,7 +932,4 @@ void FieldAutocompleteInner::onPreview() {
|
||||
}
|
||||
}
|
||||
|
||||
FieldAutocompleteInner::~FieldAutocompleteInner() {
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -138,7 +138,7 @@ private:
|
||||
|
||||
namespace internal {
|
||||
|
||||
class FieldAutocompleteInner final : public TWidget {
|
||||
class FieldAutocompleteInner final : public TWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@ -150,8 +150,6 @@ public:
|
||||
|
||||
void setRecentInlineBotsInRows(int32 bots);
|
||||
|
||||
~FieldAutocompleteInner();
|
||||
|
||||
signals:
|
||||
void mentionChosen(UserData *user, FieldAutocomplete::ChooseMethod method) const;
|
||||
void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const;
|
||||
|
@ -101,6 +101,14 @@ public:
|
||||
|
||||
constexpr int ScrollDateHideTimeout = 1000;
|
||||
|
||||
ApiWrap::RequestMessageDataCallback replyEditMessageDataCallback() {
|
||||
return [](ChannelData *channel, MsgId msgId) {
|
||||
if (App::main()) {
|
||||
App::main()->messageDataReceived(channel, msgId);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
|
||||
@ -1684,7 +1692,7 @@ void HistoryInner::toggleScrollDateShown() {
|
||||
_scrollDateShown = !_scrollDateShown;
|
||||
auto from = _scrollDateShown ? 0. : 1.;
|
||||
auto to = _scrollDateShown ? 1. : 0.;
|
||||
START_ANIMATION(_scrollDateOpacity, func(this, &HistoryInner::repaintScrollDateCallback), from, to, st::btnAttachEmoji.duration, anim::linear);
|
||||
_scrollDateOpacity.start([this] { repaintScrollDateCallback(); }, from, to, st::btnAttachEmoji.duration);
|
||||
}
|
||||
|
||||
void HistoryInner::repaintScrollDateCallback() {
|
||||
@ -3032,7 +3040,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
|
||||
|
||||
setAcceptDrops(true);
|
||||
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
|
||||
connect(&_reportSpamPanel, SIGNAL(reportClicked()), this, SLOT(onReportSpamClicked()));
|
||||
connect(&_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide()));
|
||||
@ -4117,7 +4125,7 @@ void HistoryWidget::applyDraft(bool parseLinks) {
|
||||
if (_editMsgId || _replyToId) {
|
||||
updateReplyEditTexts();
|
||||
if (!_replyEditMsg && App::api()) {
|
||||
App::api()->requestMessageData(_peer->asChannel(), _editMsgId ? _editMsgId : _replyToId, std_::make_unique<ReplyEditMessageDataCallback>());
|
||||
App::api()->requestMessageData(_peer->asChannel(), _editMsgId ? _editMsgId : _replyToId, replyEditMessageDataCallback());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7633,7 +7641,7 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() {
|
||||
update();
|
||||
}
|
||||
if (!_pinnedBar->msg && App::api()) {
|
||||
App::api()->requestMessageData(_peer->asChannel(), _pinnedBar->msgId, std_::make_unique<ReplyEditMessageDataCallback>());
|
||||
App::api()->requestMessageData(_peer->asChannel(), _pinnedBar->msgId, replyEditMessageDataCallback());
|
||||
}
|
||||
} else if (_pinnedBar) {
|
||||
destroyPinnedBar();
|
||||
@ -7652,12 +7660,6 @@ void HistoryWidget::destroyPinnedBar() {
|
||||
_inPinnedMsg = false;
|
||||
}
|
||||
|
||||
void HistoryWidget::ReplyEditMessageDataCallback::call(ChannelData *channel, MsgId msgId) const {
|
||||
if (App::main()) {
|
||||
App::main()->messageDataReceived(channel, msgId);
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &caption) {
|
||||
if (!_history || !doc || !canSendMessages(_peer)) {
|
||||
return false;
|
||||
|
@ -908,11 +908,6 @@ private:
|
||||
void destroyPinnedBar();
|
||||
void unpinDone(const MTPUpdates &updates);
|
||||
|
||||
class ReplyEditMessageDataCallback : public SharedCallback<void, ChannelData*, MsgId> {
|
||||
public:
|
||||
void call(ChannelData *channel, MsgId msgId) const override;
|
||||
};
|
||||
|
||||
bool sendExistingDocument(DocumentData *doc, const QString &caption);
|
||||
void sendExistingPhoto(PhotoData *photo, const QString &caption);
|
||||
|
||||
|
@ -135,7 +135,9 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons
|
||||
bool loaded = document->loaded(), loading = document->loading(), displayLoading = document->displayLoading();
|
||||
if (loaded && !gif() && _gif != Media::Clip::BadReader) {
|
||||
Gif *that = const_cast<Gif*>(this);
|
||||
that->_gif = new Media::Clip::Reader(document->location(), document->data(), func(that, &Gif::clipCallback));
|
||||
that->_gif = new Media::Clip::Reader(document->location(), document->data(), [that](Media::Clip::Notification notification) {
|
||||
that->clipCallback(notification);
|
||||
});
|
||||
if (gif()) _gif->setAutoplay();
|
||||
}
|
||||
|
||||
@ -165,9 +167,9 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons
|
||||
}
|
||||
|
||||
if (radial || (!_gif && !loaded && !loading) || (_gif == Media::Clip::BadReader)) {
|
||||
float64 radialOpacity = (radial && loaded) ? _animation->radial.opacity() : 1;
|
||||
auto radialOpacity = (radial && loaded) ? _animation->radial.opacity() : 1.;
|
||||
if (_animation && _animation->_a_over.animating(context->ms)) {
|
||||
float64 over = _animation->_a_over.current();
|
||||
auto over = _animation->_a_over.current();
|
||||
p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over));
|
||||
p.fillRect(r, st::black);
|
||||
} else {
|
||||
@ -219,7 +221,7 @@ void Gif::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||
bool wasactive = (_state & StateFlag::DeleteOver);
|
||||
if (active != wasactive) {
|
||||
auto from = active ? 0. : 1., to = active ? 1. : 0.;
|
||||
START_ANIMATION(_a_deleteOver, func(this, &Gif::update), from, to, st::stickersRowDuration, anim::linear);
|
||||
_a_deleteOver.start([this] { update(); }, from, to, st::stickersRowDuration);
|
||||
if (active) {
|
||||
_state |= StateFlag::DeleteOver;
|
||||
} else {
|
||||
@ -233,7 +235,7 @@ void Gif::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||
if (!getShownDocument()->loaded()) {
|
||||
ensureAnimation();
|
||||
auto from = active ? 0. : 1., to = active ? 1. : 0.;
|
||||
START_ANIMATION(_animation->_a_over, func(this, &Gif::update), from, to, st::stickersRowDuration, anim::linear);
|
||||
_animation->_a_over.start([this] { update(); }, from, to, st::stickersRowDuration);
|
||||
}
|
||||
if (active) {
|
||||
_state |= StateFlag::Over;
|
||||
@ -386,7 +388,7 @@ void Sticker::preload() const {
|
||||
void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
|
||||
bool loaded = getShownDocument()->loaded();
|
||||
|
||||
float64 over = _a_over.isNull() ? (_active ? 1 : 0) : _a_over.current();
|
||||
auto over = _a_over.current(_active ? 1. : 0.);
|
||||
if (over > 0) {
|
||||
p.setOpacity(over);
|
||||
App::roundRect(p, QRect(QPoint(0, 0), st::stickerPanSize), st::emojiPanHover, StickerHoverCorners);
|
||||
@ -415,7 +417,7 @@ void Sticker::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||
_active = active;
|
||||
|
||||
auto from = active ? 0. : 1., to = active ? 1. : 0.;
|
||||
START_ANIMATION(_a_over, func(this, &Sticker::update), from, to, st::stickersRowDuration, anim::linear);
|
||||
_a_over.start([this] { update(); }, from, to, st::stickersRowDuration);
|
||||
}
|
||||
}
|
||||
ItemBase::clickHandlerActiveChanged(p, active);
|
||||
|
@ -437,7 +437,7 @@ MediaPreviewWidget::MediaPreviewWidget(QWidget *parent) : TWidget(parent)
|
||||
, _a_shown(animation(this, &MediaPreviewWidget::step_shown))
|
||||
, _emojiSize(EmojiSizes[EIndex + 1] / cIntRetinaFactor()) {
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
}
|
||||
|
||||
void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
|
||||
@ -456,11 +456,11 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
|
||||
p.drawPixmap((width() - w) / 2, (height() - h) / 2, image);
|
||||
if (!_emojiList.isEmpty()) {
|
||||
int emojiCount = _emojiList.size();
|
||||
int emojiWidth = emojiCount * _emojiSize + (emojiCount - 1) * st::stickerEmojiSkip;
|
||||
int emojiWidth = (emojiCount * _emojiSize) + (emojiCount - 1) * st::stickerEmojiSkip;
|
||||
int emojiLeft = (width() - emojiWidth) / 2;
|
||||
int esize = _emojiSize * cIntRetinaFactor();
|
||||
int esize = EmojiSizes[EIndex + 1];
|
||||
for_const (auto emoji, _emojiList) {
|
||||
p.drawPixmapLeft(emojiLeft, (height() - h) / 2 - _emojiSize * 2, width(), App::emojiLarge(), QRect(emoji->x * esize, emoji->y * esize, esize, esize));
|
||||
p.drawPixmapLeft(emojiLeft, (height() - h) / 2 - (_emojiSize * 2), width(), App::emojiLarge(), QRect(emoji->x * esize, emoji->y * esize, esize, esize));
|
||||
emojiLeft += _emojiSize + st::stickerEmojiSkip;
|
||||
}
|
||||
}
|
||||
@ -638,7 +638,9 @@ QPixmap MediaPreviewWidget::currentImage() const {
|
||||
if (_document->loaded()) {
|
||||
if (!_gif && _gif != Media::Clip::BadReader) {
|
||||
auto that = const_cast<MediaPreviewWidget*>(this);
|
||||
that->_gif = new Media::Clip::Reader(_document->location(), _document->data(), func(that, &MediaPreviewWidget::clipCallback));
|
||||
that->_gif = new Media::Clip::Reader(_document->location(), _document->data(), [this, that](Media::Clip::Notification notification) {
|
||||
that->clipCallback(notification);
|
||||
});
|
||||
if (gif()) _gif->setAutoplay();
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class MediaPreviewWidget : public TWidget {
|
||||
class MediaPreviewWidget : public TWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -2897,18 +2897,18 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
|
||||
bool isFinal = true;
|
||||
switch (diff.type()) {
|
||||
case mtpc_updates_channelDifferenceEmpty: {
|
||||
const auto &d(diff.c_updates_channelDifferenceEmpty());
|
||||
auto &d = diff.c_updates_channelDifferenceEmpty();
|
||||
if (d.has_timeout()) timeout = d.vtimeout.v;
|
||||
isFinal = d.is_final();
|
||||
channel->ptsInit(d.vpts.v);
|
||||
} break;
|
||||
|
||||
case mtpc_updates_channelDifferenceTooLong: {
|
||||
const auto &d(diff.c_updates_channelDifferenceTooLong());
|
||||
auto &d = diff.c_updates_channelDifferenceTooLong();
|
||||
|
||||
App::feedUsers(d.vusers);
|
||||
App::feedChats(d.vchats);
|
||||
History *h = App::historyLoaded(channel->id);
|
||||
auto h = App::historyLoaded(channel->id);
|
||||
if (h) {
|
||||
h->setNotLoadedAtBottom();
|
||||
}
|
||||
@ -2934,7 +2934,7 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
|
||||
} break;
|
||||
|
||||
case mtpc_updates_channelDifference: {
|
||||
const auto &d(diff.c_updates_channelDifference());
|
||||
auto &d = diff.c_updates_channelDifference();
|
||||
|
||||
App::feedUsers(d.vusers);
|
||||
App::feedChats(d.vchats);
|
||||
@ -2943,11 +2943,11 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
|
||||
feedMessageIds(d.vother_updates);
|
||||
|
||||
// feed messages and groups, copy from App::feedMsgs
|
||||
History *h = App::history(channel->id);
|
||||
const auto &vmsgs(d.vnew_messages.c_vector().v);
|
||||
QMap<uint64, int32> msgsIds;
|
||||
for (int32 i = 0, l = vmsgs.size(); i < l; ++i) {
|
||||
const auto &msg(vmsgs.at(i));
|
||||
auto h = App::history(channel->id);
|
||||
auto &vmsgs = d.vnew_messages.c_vector().v;
|
||||
QMap<uint64, int> msgsIds;
|
||||
for (int i = 0, l = vmsgs.size(); i < l; ++i) {
|
||||
auto &msg = vmsgs[i];
|
||||
switch (msg.type()) {
|
||||
case mtpc_message: {
|
||||
const auto &d(msg.c_message());
|
||||
@ -2961,9 +2961,9 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
|
||||
case mtpc_messageService: msgsIds.insert((uint64(uint32(msg.c_messageService().vid.v)) << 32) | uint64(i), i + 1); break;
|
||||
}
|
||||
}
|
||||
for (QMap<uint64, int32>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
|
||||
if (i.value() > 0) { // add message
|
||||
const auto &msg(vmsgs.at(i.value() - 1));
|
||||
for_const (auto msgIndex, msgsIds) {
|
||||
if (msgIndex > 0) { // add message
|
||||
auto &msg = vmsgs.at(msgIndex - 1);
|
||||
if (channel->id != peerFromMessage(msg)) {
|
||||
LOG(("API Error: message with invalid peer returned in channelDifference, channelId: %1, peer: %2").arg(peerToChannel(channel->id)).arg(peerFromMessage(msg)));
|
||||
continue; // wtf
|
||||
@ -4043,7 +4043,7 @@ DataIsLoadedResult allDataLoadedForMessage(const MTPMessage &msg) {
|
||||
void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
|
||||
switch (updates.type()) {
|
||||
case mtpc_updates: {
|
||||
const auto &d(updates.c_updates());
|
||||
auto &d = updates.c_updates();
|
||||
if (d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq) return;
|
||||
if (d.vseq.v > updSeq + 1) {
|
||||
@ -4060,7 +4060,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
|
||||
} break;
|
||||
|
||||
case mtpc_updatesCombined: {
|
||||
const auto &d(updates.c_updatesCombined());
|
||||
auto &d = updates.c_updatesCombined();
|
||||
if (d.vseq_start.v) {
|
||||
if (d.vseq_start.v <= updSeq) return;
|
||||
if (d.vseq_start.v > updSeq + 1) {
|
||||
@ -4077,15 +4077,14 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateShort: {
|
||||
const auto &d(updates.c_updateShort());
|
||||
|
||||
auto &d = updates.c_updateShort();
|
||||
feedUpdate(d.vupdate);
|
||||
|
||||
updSetState(0, d.vdate.v, updQts, updSeq);
|
||||
} break;
|
||||
|
||||
case mtpc_updateShortMessage: {
|
||||
const auto &d(updates.c_updateShortMessage());
|
||||
auto &d = updates.c_updateShortMessage();
|
||||
if (!App::userLoaded(d.vuser_id.v)
|
||||
|| (d.has_via_bot_id() && !App::userLoaded(d.vvia_bot_id.v))
|
||||
|| (d.has_entities() && !mentionUsersLoaded(d.ventities))
|
||||
@ -4099,7 +4098,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
|
||||
|
||||
// update before applying skipped
|
||||
MTPDmessage::Flags flags = mtpCastFlags(d.vflags.v) | MTPDmessage::Flag::f_from_id;
|
||||
HistoryItem *item = App::histories().addNewMessage(MTP_message(MTP_flags(flags), d.vid, d.is_out() ? MTP_int(MTP::authedId()) : d.vuser_id, MTP_peerUser(d.is_out() ? d.vuser_id : MTP_int(MTP::authedId())), d.vfwd_from, d.vvia_bot_id, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup, d.has_entities() ? d.ventities : MTPnullEntities, MTPint(), MTPint()), NewMessageUnread);
|
||||
auto item = App::histories().addNewMessage(MTP_message(MTP_flags(flags), d.vid, d.is_out() ? MTP_int(MTP::authedId()) : d.vuser_id, MTP_peerUser(d.is_out() ? d.vuser_id : MTP_int(MTP::authedId())), d.vfwd_from, d.vvia_bot_id, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup, d.has_entities() ? d.ventities : MTPnullEntities, MTPint(), MTPint()), NewMessageUnread);
|
||||
if (item) {
|
||||
_history->peerMessagesUpdated(item->history()->peer->id);
|
||||
}
|
||||
@ -4110,7 +4109,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateShortChatMessage: {
|
||||
const auto &d(updates.c_updateShortChatMessage());
|
||||
auto &d = updates.c_updateShortChatMessage();
|
||||
bool noFrom = !App::userLoaded(d.vfrom_id.v);
|
||||
if (!App::chatLoaded(d.vchat_id.v)
|
||||
|| noFrom
|
||||
@ -4127,7 +4126,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
|
||||
|
||||
// update before applying skipped
|
||||
MTPDmessage::Flags flags = mtpCastFlags(d.vflags.v) | MTPDmessage::Flag::f_from_id;
|
||||
HistoryItem *item = App::histories().addNewMessage(MTP_message(MTP_flags(flags), d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vfwd_from, d.vvia_bot_id, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup, d.has_entities() ? d.ventities : MTPnullEntities, MTPint(), MTPint()), NewMessageUnread);
|
||||
auto item = App::histories().addNewMessage(MTP_message(MTP_flags(flags), d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vfwd_from, d.vvia_bot_id, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup, d.has_entities() ? d.ventities : MTPnullEntities, MTPint(), MTPint()), NewMessageUnread);
|
||||
if (item) {
|
||||
_history->peerMessagesUpdated(item->history()->peer->id);
|
||||
}
|
||||
@ -4138,7 +4137,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateShortSentMessage: {
|
||||
const auto &d(updates.c_updateShortSentMessage());
|
||||
auto &d = updates.c_updateShortSentMessage();
|
||||
if (randomId) {
|
||||
PeerId peerId = 0;
|
||||
QString text;
|
||||
@ -4148,7 +4147,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
|
||||
if (peerId) {
|
||||
if (auto item = App::histItemById(peerToChannel(peerId), d.vid.v)) {
|
||||
if (d.has_entities() && !mentionUsersLoaded(d.ventities)) {
|
||||
api()->requestMessageData(item->history()->peer->asChannel(), item->id, nullptr);
|
||||
api()->requestMessageData(item->history()->peer->asChannel(), item->id, ApiWrap::RequestMessageDataCallback());
|
||||
}
|
||||
auto entities = d.has_entities() ? entitiesFromMTP(d.ventities.c_vector().v) : EntitiesInText();
|
||||
item->setText({ text, entities });
|
||||
@ -4524,13 +4523,13 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
} break;
|
||||
|
||||
case mtpc_updatePrivacy: {
|
||||
const auto &d(update.c_updatePrivacy());
|
||||
auto &d = update.c_updatePrivacy();
|
||||
} break;
|
||||
|
||||
/////// Channel updates
|
||||
case mtpc_updateChannel: {
|
||||
const auto &d(update.c_updateChannel());
|
||||
if (ChannelData *channel = App::channelLoaded(d.vchannel_id.v)) {
|
||||
auto &d = update.c_updateChannel();
|
||||
if (auto channel = App::channelLoaded(d.vchannel_id.v)) {
|
||||
App::markPeerUpdated(channel);
|
||||
channel->inviter = 0;
|
||||
if (!channel->amIn()) {
|
||||
@ -4544,7 +4543,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateNewChannelMessage: {
|
||||
auto &d = update.c_updateNewChannelMessage();
|
||||
ChannelData *channel = App::channelLoaded(peerToChannel(peerFromMessage(d.vmessage)));
|
||||
auto channel = App::channelLoaded(peerToChannel(peerFromMessage(d.vmessage)));
|
||||
DataIsLoadedResult isDataLoaded = allDataLoadedForMessage(d.vmessage);
|
||||
if (!requestingDifference() && (!channel || isDataLoaded != DataIsLoadedResult::Ok)) {
|
||||
MTP_LOG(0, ("getDifference { good - after not all data loaded in updateNewChannelMessage }%1").arg(cTestMode() ? " TESTMODE" : ""));
|
||||
@ -4579,8 +4578,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
}
|
||||
if (needToAdd) {
|
||||
HistoryItem *item = App::histories().addNewMessage(d.vmessage, NewMessageUnread);
|
||||
if (item) {
|
||||
if (auto item = App::histories().addNewMessage(d.vmessage, NewMessageUnread)) {
|
||||
_history->peerMessagesUpdated(item->history()->peer->id);
|
||||
}
|
||||
}
|
||||
@ -4590,8 +4588,8 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateEditChannelMessage: {
|
||||
const auto &d(update.c_updateEditChannelMessage());
|
||||
ChannelData *channel = App::channelLoaded(peerToChannel(peerFromMessage(d.vmessage)));
|
||||
auto &d = update.c_updateEditChannelMessage();
|
||||
auto channel = App::channelLoaded(peerToChannel(peerFromMessage(d.vmessage)));
|
||||
|
||||
if (channel && !_handlingChannelDifference) {
|
||||
if (channel->ptsRequesting()) { // skip global updates while getting channel difference
|
||||
@ -4610,7 +4608,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateEditMessage: {
|
||||
const auto &d(update.c_updateEditMessage());
|
||||
auto &d = update.c_updateEditMessage();
|
||||
|
||||
if (!ptsUpdated(d.vpts.v, d.vpts_count.v, update)) {
|
||||
return;
|
||||
@ -4623,9 +4621,9 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelPinnedMessage: {
|
||||
const auto &d(update.c_updateChannelPinnedMessage());
|
||||
auto &d = update.c_updateChannelPinnedMessage();
|
||||
|
||||
if (ChannelData *channel = App::channelLoaded(d.vchannel_id.v)) {
|
||||
if (auto channel = App::channelLoaded(d.vchannel_id.v)) {
|
||||
if (channel->isMegagroup()) {
|
||||
channel->mgInfo->pinnedMsgId = d.vid.v;
|
||||
if (App::api()) {
|
||||
@ -4636,13 +4634,12 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadChannelInbox: {
|
||||
auto &d(update.c_updateReadChannelInbox());
|
||||
auto channel = App::channelLoaded(d.vchannel_id.v);
|
||||
auto &d = update.c_updateReadChannelInbox();
|
||||
App::feedInboxRead(peerFromChannel(d.vchannel_id.v), d.vmax_id.v);
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadChannelOutbox: {
|
||||
auto &d(update.c_updateReadChannelOutbox());
|
||||
auto &d = update.c_updateReadChannelOutbox();
|
||||
auto peerId = peerFromChannel(d.vchannel_id.v);
|
||||
auto when = requestingDifference() ? 0 : unixtime();
|
||||
App::feedOutboxRead(peerId, d.vmax_id.v, when);
|
||||
@ -4652,8 +4649,8 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateDeleteChannelMessages: {
|
||||
const auto &d(update.c_updateDeleteChannelMessages());
|
||||
ChannelData *channel = App::channelLoaded(d.vchannel_id.v);
|
||||
auto &d = update.c_updateDeleteChannelMessages();
|
||||
auto channel = App::channelLoaded(d.vchannel_id.v);
|
||||
|
||||
if (channel && !_handlingChannelDifference) {
|
||||
if (channel->ptsRequesting()) { // skip global updates while getting channel difference
|
||||
@ -4673,8 +4670,8 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelTooLong: {
|
||||
const auto &d(update.c_updateChannelTooLong());
|
||||
if (ChannelData *channel = App::channelLoaded(d.vchannel_id.v)) {
|
||||
auto &d = update.c_updateChannelTooLong();
|
||||
if (auto channel = App::channelLoaded(d.vchannel_id.v)) {
|
||||
if (!d.has_pts() || channel->pts() < d.vpts.v) {
|
||||
getChannelDifference(channel);
|
||||
}
|
||||
@ -4682,8 +4679,8 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelMessageViews: {
|
||||
const auto &d(update.c_updateChannelMessageViews());
|
||||
if (HistoryItem *item = App::histItemById(d.vchannel_id.v, d.vid.v)) {
|
||||
auto &d = update.c_updateChannelMessageViews();
|
||||
if (auto item = App::histItemById(d.vchannel_id.v, d.vid.v)) {
|
||||
item->setViewsCount(d.vviews.v);
|
||||
}
|
||||
} break;
|
||||
|
@ -406,8 +406,7 @@ MainWindow::MainWindow() {
|
||||
|
||||
connect(&_autoLockTimer, SIGNAL(timeout()), this, SLOT(checkAutoLock()));
|
||||
|
||||
connect(this, SIGNAL(imageLoaded()), this, SLOT(notifyUpdateAllPhotos()));
|
||||
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { notifyUpdateAllPhotos(); });
|
||||
subscribe(Global::RefSelfChanged(), [this]() { updateGlobalMenu(); });
|
||||
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
|
@ -288,9 +288,7 @@ signals:
|
||||
void tempDirClearFailed(int task);
|
||||
void newAuthorization();
|
||||
|
||||
void imageLoaded();
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void onStateChanged(Qt::WindowState state);
|
||||
void onSettingsDestroyed(QObject *was);
|
||||
|
||||
|
@ -209,6 +209,9 @@ bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const Q
|
||||
}
|
||||
|
||||
QSize toSize(size.isEmpty() ? QSize(_width, _height) : size);
|
||||
if (!size.isEmpty() && rotationSwapWidthHeight()) {
|
||||
toSize.transpose();
|
||||
}
|
||||
if (to.isNull() || to.size() != toSize) {
|
||||
to = QImage(toSize, QImage::Format_ARGB32);
|
||||
}
|
||||
@ -231,6 +234,15 @@ bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const Q
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_rotation != Rotation::None) {
|
||||
QTransform rotationTransform;
|
||||
switch (_rotation) {
|
||||
case Rotation::Degrees90: rotationTransform.rotate(90); break;
|
||||
case Rotation::Degrees180: rotationTransform.rotate(180); break;
|
||||
case Rotation::Degrees270: rotationTransform.rotate(270); break;
|
||||
}
|
||||
to = to.transformed(rotationTransform);
|
||||
}
|
||||
|
||||
// Read some future packets for audio stream.
|
||||
if (_audioStreamId >= 0) {
|
||||
@ -247,6 +259,15 @@ bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const Q
|
||||
return true;
|
||||
}
|
||||
|
||||
FFMpegReaderImplementation::Rotation FFMpegReaderImplementation::rotationFromDegrees(int degrees) const {
|
||||
switch (degrees) {
|
||||
case 90: return Rotation::Degrees90;
|
||||
case 180: return Rotation::Degrees180;
|
||||
case 270: return Rotation::Degrees270;
|
||||
}
|
||||
return Rotation::None;
|
||||
}
|
||||
|
||||
bool FFMpegReaderImplementation::start(Mode mode, int64 &positionMs) {
|
||||
_mode = mode;
|
||||
|
||||
@ -286,6 +307,16 @@ bool FFMpegReaderImplementation::start(Mode mode, int64 &positionMs) {
|
||||
}
|
||||
_packetNull.stream_index = _streamId;
|
||||
|
||||
auto rotateTag = av_dict_get(_fmtContext->streams[_streamId]->metadata, "rotate", NULL, 0);
|
||||
if (rotateTag && *rotateTag->value) {
|
||||
auto stringRotateTag = QString::fromUtf8(rotateTag->value);
|
||||
auto toIntSucceeded = false;
|
||||
auto rotateDegrees = stringRotateTag.toInt(&toIntSucceeded);
|
||||
if (toIntSucceeded) {
|
||||
_rotation = rotationFromDegrees(rotateDegrees);
|
||||
}
|
||||
}
|
||||
|
||||
_codecContext = avcodec_alloc_context3(nullptr);
|
||||
if (!_codecContext) {
|
||||
LOG(("Audio Error: Unable to avcodec_alloc_context3 %1").arg(logData()));
|
||||
|
@ -71,6 +71,17 @@ private:
|
||||
int64 countPacketMs(AVPacket *packet) const;
|
||||
PacketResult readAndProcessPacket();
|
||||
|
||||
enum class Rotation {
|
||||
None,
|
||||
Degrees90,
|
||||
Degrees180,
|
||||
Degrees270,
|
||||
};
|
||||
Rotation rotationFromDegrees(int degrees) const;
|
||||
bool rotationSwapWidthHeight() const {
|
||||
return (_rotation == Rotation::Degrees90) || (_rotation == Rotation::Degrees270);
|
||||
}
|
||||
|
||||
void startPacket();
|
||||
void finishPacket();
|
||||
void clearPacketQueue();
|
||||
@ -80,6 +91,8 @@ private:
|
||||
|
||||
Mode _mode = Mode::Normal;
|
||||
|
||||
Rotation _rotation = Rotation::None;
|
||||
|
||||
uchar *_ioBuffer = nullptr;
|
||||
AVIOContext *_ioContext = nullptr;
|
||||
AVFormatContext *_fmtContext = nullptr;
|
||||
|
@ -180,8 +180,8 @@ void Reader::moveToNextWrite() const {
|
||||
|
||||
void Reader::callback(Reader *reader, int32 threadIndex, Notification notification) {
|
||||
// check if reader is not deleted already
|
||||
if (managers.size() > threadIndex && managers.at(threadIndex)->carries(reader)) {
|
||||
reader->_callback.call(notification);
|
||||
if (managers.size() > threadIndex && managers.at(threadIndex)->carries(reader) && reader->_callback) {
|
||||
reader->_callback(notification);
|
||||
}
|
||||
}
|
||||
|
||||
@ -588,15 +588,12 @@ void Manager::start(Reader *reader) {
|
||||
}
|
||||
|
||||
void Manager::update(Reader *reader) {
|
||||
QReadLocker lock(&_readerPointersMutex);
|
||||
auto i = _readerPointers.constFind(reader);
|
||||
QMutexLocker lock(&_readerPointersMutex);
|
||||
auto i = _readerPointers.find(reader);
|
||||
if (i == _readerPointers.cend()) {
|
||||
lock.unlock();
|
||||
|
||||
QWriteLocker lock(&_readerPointersMutex);
|
||||
_readerPointers.insert(reader, MutableAtomicInt(1));
|
||||
_readerPointers.insert(reader, QAtomicInt(1));
|
||||
} else {
|
||||
i->v.storeRelease(1);
|
||||
i->storeRelease(1);
|
||||
}
|
||||
emit processDelayed();
|
||||
}
|
||||
@ -604,13 +601,13 @@ void Manager::update(Reader *reader) {
|
||||
void Manager::stop(Reader *reader) {
|
||||
if (!carries(reader)) return;
|
||||
|
||||
QWriteLocker lock(&_readerPointersMutex);
|
||||
QMutexLocker lock(&_readerPointersMutex);
|
||||
_readerPointers.remove(reader);
|
||||
emit processDelayed();
|
||||
}
|
||||
|
||||
bool Manager::carries(Reader *reader) const {
|
||||
QReadLocker lock(&_readerPointersMutex);
|
||||
QMutexLocker lock(&_readerPointersMutex);
|
||||
return _readerPointers.contains(reader);
|
||||
}
|
||||
|
||||
@ -629,19 +626,13 @@ Manager::ReaderPointers::const_iterator Manager::constUnsafeFindReaderPointer(Re
|
||||
}
|
||||
|
||||
bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, uint64 ms) {
|
||||
QReadLocker lock(&_readerPointersMutex);
|
||||
auto it = constUnsafeFindReaderPointer(reader);
|
||||
QMutexLocker lock(&_readerPointersMutex);
|
||||
auto it = unsafeFindReaderPointer(reader);
|
||||
if (result == ProcessResult::Error) {
|
||||
if (it != _readerPointers.cend()) {
|
||||
lock.unlock();
|
||||
QWriteLocker lock(&_readerPointersMutex);
|
||||
|
||||
auto i = unsafeFindReaderPointer(reader);
|
||||
if (i != _readerPointers.cend()) {
|
||||
i.key()->error();
|
||||
emit callback(i.key(), i.key()->threadIndex(), NotificationReinit);
|
||||
_readerPointers.erase(i);
|
||||
}
|
||||
it.key()->error();
|
||||
emit callback(it.key(), it.key()->threadIndex(), NotificationReinit);
|
||||
_readerPointers.erase(it);
|
||||
}
|
||||
return false;
|
||||
} else if (result == ProcessResult::Finished) {
|
||||
@ -663,8 +654,8 @@ bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, u
|
||||
// See if we need to pause GIF because it is not displayed right now.
|
||||
if (!reader->_autoPausedGif && reader->_mode == Reader::Mode::Gif && result == ProcessResult::Repaint) {
|
||||
int32 ishowing, iprevious;
|
||||
Reader::Frame *showing = it.key()->frameToShow(&ishowing), *previous = it.key()->frameToWriteNext(false, &iprevious);
|
||||
t_assert(previous != 0 && showing != 0 && ishowing >= 0 && iprevious >= 0);
|
||||
auto showing = it.key()->frameToShow(&ishowing), previous = it.key()->frameToWriteNext(false, &iprevious);
|
||||
t_assert(previous != nullptr && showing != nullptr && ishowing >= 0 && iprevious >= 0);
|
||||
if (reader->_frames[ishowing].when > 0 && showing->displayed.loadAcquire() <= 0) { // current frame was not shown
|
||||
if (reader->_frames[ishowing].when + WaitBeforeGifPause < ms || (reader->_frames[iprevious].when && previous->displayed.loadAcquire() <= 0)) {
|
||||
reader->_autoPausedGif = true;
|
||||
@ -675,7 +666,7 @@ bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, u
|
||||
}
|
||||
if (result == ProcessResult::Started || result == ProcessResult::CopyFrame) {
|
||||
t_assert(reader->_frame >= 0);
|
||||
Reader::Frame *frame = it.key()->_frames + reader->_frame;
|
||||
auto frame = it.key()->_frames + reader->_frame;
|
||||
frame->clear();
|
||||
frame->pix = reader->frame()->pix;
|
||||
frame->original = reader->frame()->original;
|
||||
@ -710,8 +701,8 @@ Manager::ResultHandleState Manager::handleResult(ReaderPrivate *reader, ProcessR
|
||||
|
||||
if (result == ProcessResult::Repaint) {
|
||||
{
|
||||
QReadLocker lock(&_readerPointersMutex);
|
||||
ReaderPointers::const_iterator it = constUnsafeFindReaderPointer(reader);
|
||||
QMutexLocker lock(&_readerPointersMutex);
|
||||
auto it = constUnsafeFindReaderPointer(reader);
|
||||
if (it != _readerPointers.cend()) {
|
||||
int32 index = 0;
|
||||
Reader *r = it.key();
|
||||
@ -742,9 +733,9 @@ void Manager::process() {
|
||||
bool checkAllReaders = false;
|
||||
uint64 ms = getms(), minms = ms + 86400 * 1000ULL;
|
||||
{
|
||||
QReadLocker lock(&_readerPointersMutex);
|
||||
QMutexLocker lock(&_readerPointersMutex);
|
||||
for (auto it = _readerPointers.begin(), e = _readerPointers.end(); it != e; ++it) {
|
||||
if (it->v.loadAcquire() && it.key()->_private != nullptr) {
|
||||
if (it->loadAcquire() && it.key()->_private != nullptr) {
|
||||
auto i = _readers.find(it.key()->_private);
|
||||
if (i == _readers.cend()) {
|
||||
_readers.insert(it.key()->_private, 0);
|
||||
@ -759,9 +750,9 @@ void Manager::process() {
|
||||
i.key()->resumeVideo(ms);
|
||||
}
|
||||
}
|
||||
Reader::Frame *frame = it.key()->frameToWrite();
|
||||
auto frame = it.key()->frameToWrite();
|
||||
if (frame) it.key()->_private->_request = frame->request;
|
||||
it->v.storeRelease(0);
|
||||
it->storeRelease(0);
|
||||
}
|
||||
}
|
||||
checkAllReaders = (_readers.size() > _readerPointers.size());
|
||||
@ -787,8 +778,8 @@ void Manager::process() {
|
||||
i.value() = (ms + 86400 * 1000ULL);
|
||||
}
|
||||
} else if (checkAllReaders) {
|
||||
QReadLocker lock(&_readerPointersMutex);
|
||||
ReaderPointers::const_iterator it = constUnsafeFindReaderPointer(reader);
|
||||
QMutexLocker lock(&_readerPointersMutex);
|
||||
auto it = constUnsafeFindReaderPointer(reader);
|
||||
if (it == _readerPointers.cend()) {
|
||||
_loadLevel.fetchAndAddRelaxed(-1 * (reader->_width > 0 ? reader->_width * reader->_height : AverageGifSize));
|
||||
delete reader;
|
||||
@ -820,8 +811,8 @@ void Manager::finish() {
|
||||
|
||||
void Manager::clear() {
|
||||
{
|
||||
QWriteLocker lock(&_readerPointersMutex);
|
||||
for (ReaderPointers::iterator it = _readerPointers.begin(), e = _readerPointers.end(); it != e; ++it) {
|
||||
QMutexLocker lock(&_readerPointersMutex);
|
||||
for (auto it = _readerPointers.begin(), e = _readerPointers.end(); it != e; ++it) {
|
||||
it.key()->_private = nullptr;
|
||||
}
|
||||
_readerPointers.clear();
|
||||
|
@ -52,7 +52,7 @@ enum ReaderSteps {
|
||||
class ReaderPrivate;
|
||||
class Reader {
|
||||
public:
|
||||
using Callback = Function<void, Notification>;
|
||||
using Callback = base::lambda_unique<void(Notification)>;
|
||||
enum class Mode {
|
||||
Gif,
|
||||
Video,
|
||||
@ -211,14 +211,9 @@ private:
|
||||
void clear();
|
||||
|
||||
QAtomicInt _loadLevel;
|
||||
struct MutableAtomicInt {
|
||||
MutableAtomicInt(int value) : v(value) {
|
||||
}
|
||||
mutable QAtomicInt v;
|
||||
};
|
||||
typedef QMap<Reader*, MutableAtomicInt> ReaderPointers;
|
||||
using ReaderPointers = QMap<Reader*, QAtomicInt>;
|
||||
ReaderPointers _readerPointers;
|
||||
mutable QReadWriteLock _readerPointersMutex;
|
||||
mutable QMutex _readerPointersMutex;
|
||||
|
||||
ReaderPointers::const_iterator constUnsafeFindReaderPointer(ReaderPrivate *reader) const;
|
||||
ReaderPointers::iterator unsafeFindReaderPointer(ReaderPrivate *reader);
|
||||
|
@ -66,9 +66,7 @@ void TitleButton::updatePauseState() {
|
||||
void TitleButton::setShowPause(bool showPause) {
|
||||
if (_showPause != showPause) {
|
||||
_showPause = showPause;
|
||||
START_ANIMATION(_iconTransformToPause, func([this]() {
|
||||
update();
|
||||
}), _showPause ? 0. : 1., _showPause ? 1. : 0., st::mediaPlayerTitleButtonTransformDuration, anim::linear);
|
||||
_iconTransformToPause.start([this] { update(); }, _showPause ? 0. : 1., _showPause ? 1. : 0., st::mediaPlayerTitleButtonTransformDuration);
|
||||
update();
|
||||
}
|
||||
}
|
||||
@ -97,9 +95,7 @@ void TitleButton::paintEvent(QPaintEvent *e) {
|
||||
void TitleButton::onStateChanged(int oldState, ButtonStateChangeSource source) {
|
||||
if ((oldState & StateOver) != (_state & StateOver)) {
|
||||
auto over = (_state & StateOver);
|
||||
START_ANIMATION(_iconFg, func([this]() {
|
||||
update();
|
||||
}), over ? st::titleButtonFg->c : st::titleButtonActiveFg->c, over ? st::titleButtonActiveFg->c : st::titleButtonFg->c, st::titleButtonDuration, anim::linear);
|
||||
_iconFg.start([this] { update(); }, over ? st::titleButtonFg->c : st::titleButtonActiveFg->c, over ? st::titleButtonActiveFg->c : st::titleButtonFg->c, st::titleButtonDuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,10 @@ Instance::Instance() {
|
||||
handleSongUpdate(audioId);
|
||||
}
|
||||
});
|
||||
Notify::registerPeerObserver(Notify::PeerUpdate::Flag::SharedMediaChanged, this, &Instance::notifyPeerUpdated);
|
||||
auto observeEvents = Notify::PeerUpdate::Flag::SharedMediaChanged;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
}
|
||||
|
||||
void Instance::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
|
@ -53,7 +53,7 @@ struct UpdatedEvent {
|
||||
const AudioPlaybackState *playbackState;
|
||||
};
|
||||
|
||||
class Instance : private base::Subscriber, public Notify::Observer {
|
||||
class Instance : private base::Subscriber {
|
||||
public:
|
||||
void play();
|
||||
void pause();
|
||||
|
@ -51,7 +51,7 @@ Widget::Widget(QWidget *parent) : TWidget(parent)
|
||||
}
|
||||
|
||||
bool Widget::overlaps(const QRect &globalRect) {
|
||||
if (isHidden() || !_a_appearance.isNull()) return false;
|
||||
if (isHidden() || _a_appearance.animating()) return false;
|
||||
|
||||
auto marginLeft = rtl() ? 0 : contentLeft();
|
||||
auto marginRight = rtl() ? contentLeft() : 0;
|
||||
@ -171,18 +171,18 @@ void Widget::onHideStart() {
|
||||
void Widget::startAnimation() {
|
||||
auto from = _hiding ? 1. : 0.;
|
||||
auto to = _hiding ? 0. : 1.;
|
||||
if (_a_appearance.isNull()) {
|
||||
if (!_a_appearance.animating()) {
|
||||
showChildren();
|
||||
_cache = myGrab(this);
|
||||
}
|
||||
hideChildren();
|
||||
START_ANIMATION(_a_appearance, func([this]() {
|
||||
_a_appearance.start([this]() {
|
||||
update();
|
||||
if (!_a_appearance.animating(getms()) && _hiding) {
|
||||
_hiding = false;
|
||||
hidingFinished();
|
||||
}
|
||||
}), from, to, st::defaultInnerDropdown.duration, anim::linear);
|
||||
}, from, to, st::defaultInnerDropdown.duration);
|
||||
}
|
||||
|
||||
void Widget::hidingFinished() {
|
||||
|
@ -41,8 +41,8 @@ Controller::Controller(QWidget *parent) : TWidget(parent)
|
||||
, _toPlayLeft(this, st::mediaviewPlayProgressLabel)
|
||||
, _fadeAnimation(std_::make_unique<Ui::FadeAnimation>(this)) {
|
||||
_fadeAnimation->show();
|
||||
_fadeAnimation->setFinishedCallback(func(this, &Controller::fadeFinished));
|
||||
_fadeAnimation->setUpdatedCallback(func(this, &Controller::fadeUpdated));
|
||||
_fadeAnimation->setFinishedCallback([this] { fadeFinished(); });
|
||||
_fadeAnimation->setUpdatedCallback([this](float64 opacity) { fadeUpdated(opacity); });
|
||||
|
||||
_volumeController->setVolume(Global::VideoVolume());
|
||||
|
||||
|
@ -55,6 +55,7 @@ void Playback::updateState(const AudioPlaybackState &playbackState) {
|
||||
_position = position;
|
||||
_duration = duration;
|
||||
}
|
||||
_slider->update();
|
||||
}
|
||||
|
||||
} // namespace Clip
|
||||
|
@ -100,7 +100,7 @@ void VolumeController::setOver(bool over) {
|
||||
|
||||
_over = over;
|
||||
auto from = _over ? 0. : 1., to = _over ? 1. : 0.;
|
||||
START_ANIMATION(_a_over, func(this, &VolumeController::updateCallback), from, to, st::mediaviewOverDuration, anim::linear);
|
||||
_a_over.start([this] { update(); }, from, to, st::mediaviewOverDuration);
|
||||
}
|
||||
|
||||
} // namespace Clip
|
||||
|
@ -43,9 +43,6 @@ protected:
|
||||
void leaveEvent(QEvent *e) override;
|
||||
|
||||
private:
|
||||
void updateCallback() {
|
||||
update();
|
||||
}
|
||||
void setOver(bool over);
|
||||
void changeVolume(float64 newVolume);
|
||||
|
||||
|
@ -1368,7 +1368,9 @@ void MediaView::createClipReader() {
|
||||
_current = _doc->thumb->pixNoCache(_doc->thumb->width(), _doc->thumb->height(), ImagePixSmooth | ImagePixBlurred, st::mvDocIconSize, st::mvDocIconSize);
|
||||
}
|
||||
auto mode = _doc->isVideo() ? Media::Clip::Reader::Mode::Video : Media::Clip::Reader::Mode::Gif;
|
||||
_gif = std_::make_unique<Media::Clip::Reader>(_doc->location(), _doc->data(), func(this, &MediaView::clipCallback), mode);
|
||||
_gif = std_::make_unique<Media::Clip::Reader>(_doc->location(), _doc->data(), [this](Media::Clip::Notification notification) {
|
||||
clipCallback(notification);
|
||||
}, mode);
|
||||
|
||||
// Correct values will be set when gif gets inited.
|
||||
_videoPaused = _videoIsSilent = _videoStopped = false;
|
||||
@ -1439,7 +1441,9 @@ void MediaView::restartVideoAtSeekPosition(int64 positionMs) {
|
||||
if (_current.isNull()) {
|
||||
_current = _gif->current(_gif->width(), _gif->height(), _gif->width(), _gif->height(), getms());
|
||||
}
|
||||
_gif = std_::make_unique<Media::Clip::Reader>(_doc->location(), _doc->data(), func(this, &MediaView::clipCallback), Media::Clip::Reader::Mode::Video, positionMs);
|
||||
_gif = std_::make_unique<Media::Clip::Reader>(_doc->location(), _doc->data(), [this](Media::Clip::Notification notification) {
|
||||
clipCallback(notification);
|
||||
}, Media::Clip::Reader::Mode::Video, positionMs);
|
||||
|
||||
// Correct values will be set when gif gets inited.
|
||||
_videoPaused = _videoIsSilent = _videoStopped = false;
|
||||
|
@ -206,7 +206,7 @@ namespace {
|
||||
}
|
||||
req = i.value();
|
||||
}
|
||||
if (internal::Session *session = internal::getSession(newdcWithShift)) {
|
||||
if (auto session = internal::getSession(newdcWithShift)) {
|
||||
internal::registerRequest(requestId, (dcWithShift < 0) ? -newdcWithShift : newdcWithShift);
|
||||
session->sendPrepared(req);
|
||||
}
|
||||
|
@ -207,9 +207,8 @@ void FileLoader::localLoaded(const StorageImageSaved &result, const QByteArray &
|
||||
_fileIsOpen = false;
|
||||
psPostprocessFile(QFileInfo(_file).absoluteFilePath());
|
||||
}
|
||||
emit App::wnd()->imageLoaded();
|
||||
emit progress(this);
|
||||
FileDownload::internal::notifyImageLoaded();
|
||||
FileDownload::ImageLoaded().notify();
|
||||
loadNext();
|
||||
}
|
||||
|
||||
@ -516,8 +515,6 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
|
||||
}
|
||||
removeFromQueue();
|
||||
|
||||
emit App::wnd()->imageLoaded();
|
||||
|
||||
if (!_queue->queries) {
|
||||
App::app()->killDownloadSessionsStart(_dc);
|
||||
}
|
||||
@ -544,7 +541,7 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
|
||||
}
|
||||
emit progress(this);
|
||||
if (_complete) {
|
||||
FileDownload::internal::notifyImageLoaded();
|
||||
FileDownload::ImageLoaded().notify();
|
||||
}
|
||||
loadNext();
|
||||
}
|
||||
@ -669,13 +666,11 @@ void webFileLoader::onFinished(const QByteArray &data) {
|
||||
}
|
||||
removeFromQueue();
|
||||
|
||||
emit App::wnd()->imageLoaded();
|
||||
|
||||
if (_localStatus == LocalNotFound || _localStatus == LocalFailed) {
|
||||
Local::writeWebFile(_url, _data);
|
||||
}
|
||||
emit progress(this);
|
||||
FileDownload::internal::notifyImageLoaded();
|
||||
FileDownload::ImageLoaded().notify();
|
||||
loadNext();
|
||||
}
|
||||
|
||||
@ -1093,21 +1088,12 @@ namespace MTP {
|
||||
namespace FileDownload {
|
||||
namespace {
|
||||
|
||||
using internal::ImageLoadedHandler;
|
||||
|
||||
Notify::SimpleObservedEventRegistrator<ImageLoadedHandler> creator(nullptr, nullptr);
|
||||
base::Observable<void> ImageLoadedObservable;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace internal {
|
||||
|
||||
Notify::ConnectionId plainRegisterImageLoadedObserver(ImageLoadedHandler &&handler) {
|
||||
return creator.registerObserver(std_::forward<ImageLoadedHandler>(handler));
|
||||
base::Observable<void> &ImageLoaded() {
|
||||
return ImageLoadedObservable;
|
||||
}
|
||||
|
||||
void notifyImageLoaded() {
|
||||
creator.notify();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
}
|
||||
} // namespace FileDownload
|
||||
|
@ -396,19 +396,7 @@ void reinitWebLoadManager();
|
||||
void stopWebLoadManager();
|
||||
|
||||
namespace FileDownload {
|
||||
namespace internal {
|
||||
|
||||
using ImageLoadedHandler = Function<void>;
|
||||
Notify::ConnectionId plainRegisterImageLoadedObserver(ImageLoadedHandler &&handler);
|
||||
|
||||
void notifyImageLoaded();
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename ObserverType>
|
||||
void registerImageLoadedObserver(ObserverType *observer, void (ObserverType::*handler)()) {
|
||||
auto connection = internal::plainRegisterImageLoadedObserver(func(observer, handler));
|
||||
Notify::observerRegistered(observer, connection);
|
||||
}
|
||||
base::Observable<void> &ImageLoaded();
|
||||
|
||||
} // namespace FileDownload
|
||||
|
@ -31,8 +31,6 @@ void emitPeerUpdated();
|
||||
namespace Notify {
|
||||
namespace {
|
||||
|
||||
using internal::PeerUpdateHandler;
|
||||
|
||||
using SmallUpdatesList = QVector<PeerUpdate>;
|
||||
NeverFreedPointer<SmallUpdatesList> SmallUpdates;
|
||||
using AllUpdatesList = QMap<PeerData*, PeerUpdate>;
|
||||
@ -46,19 +44,11 @@ void FinishCallback() {
|
||||
SmallUpdates.clear();
|
||||
AllUpdates.clear();
|
||||
}
|
||||
ObservedEventRegistrator<PeerUpdate::Flags, PeerUpdateHandler> creator(StartCallback, FinishCallback);
|
||||
|
||||
base::Observable<PeerUpdate, PeerUpdatedHandler> PeerUpdatedObservable;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace internal {
|
||||
|
||||
ConnectionId plainRegisterPeerObserver(PeerUpdate::Flags events, PeerUpdateHandler &&handler) {
|
||||
constexpr auto tmp = sizeof(PeerUpdate);
|
||||
return creator.registerObserver(events, std_::forward<PeerUpdateHandler>(handler));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) {
|
||||
if (!(mergeTo.flags & PeerUpdate::Flag::NameChanged)) {
|
||||
if (mergeFrom.flags & PeerUpdate::Flag::NameChanged) {
|
||||
@ -73,7 +63,8 @@ void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) {
|
||||
}
|
||||
|
||||
void peerUpdatedDelayed(const PeerUpdate &update) {
|
||||
t_assert(creator.started());
|
||||
SmallUpdates.makeIfNull();
|
||||
AllUpdates.makeIfNull();
|
||||
|
||||
Global::RefHandleDelayedPeerUpdates().call();
|
||||
|
||||
@ -85,6 +76,7 @@ void peerUpdatedDelayed(const PeerUpdate &update) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (AllUpdates->isEmpty()) {
|
||||
if (existingUpdatesCount < 5) {
|
||||
SmallUpdates->push_back(update);
|
||||
@ -102,24 +94,27 @@ void peerUpdatedDelayed(const PeerUpdate &update) {
|
||||
}
|
||||
|
||||
void peerUpdatedSendDelayed() {
|
||||
if (!creator.started()) return;
|
||||
|
||||
App::emitPeerUpdated();
|
||||
|
||||
if (SmallUpdates->isEmpty()) return;
|
||||
if (!SmallUpdates || !AllUpdates || SmallUpdates->empty()) return;
|
||||
|
||||
auto smallList = createAndSwap(*SmallUpdates);
|
||||
auto allList = createAndSwap(*AllUpdates);
|
||||
for_const (auto &update, smallList) {
|
||||
creator.notify(update.flags, update);
|
||||
for (auto &update : smallList) {
|
||||
PeerUpdated().notify(std_::move(update), true);
|
||||
}
|
||||
for_const (auto &update, allList) {
|
||||
creator.notify(update.flags, update);
|
||||
for (auto &update : allList) {
|
||||
PeerUpdated().notify(std_::move(update), true);
|
||||
}
|
||||
|
||||
if (SmallUpdates->isEmpty()) {
|
||||
std::swap(smallList, *SmallUpdates);
|
||||
SmallUpdates->resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
base::Observable<PeerUpdate, PeerUpdatedHandler> &PeerUpdated() {
|
||||
return PeerUpdatedObservable;
|
||||
}
|
||||
|
||||
} // namespace Notify
|
||||
|
@ -89,17 +89,22 @@ inline void peerUpdatedDelayed(PeerData *peer, PeerUpdate::Flags events) {
|
||||
}
|
||||
void peerUpdatedSendDelayed();
|
||||
|
||||
namespace internal {
|
||||
class PeerUpdatedHandler {
|
||||
public:
|
||||
template <typename Lambda>
|
||||
PeerUpdatedHandler(PeerUpdate::Flags events, Lambda &&handler) : _events(events), _handler(std_::move(handler)) {
|
||||
}
|
||||
void operator()(const PeerUpdate &update) const {
|
||||
if (update.flags & _events) {
|
||||
_handler(update);
|
||||
}
|
||||
}
|
||||
|
||||
using PeerUpdateHandler = Function<void, const PeerUpdate&>;
|
||||
ConnectionId plainRegisterPeerObserver(PeerUpdate::Flags events, PeerUpdateHandler &&handler);
|
||||
private:
|
||||
PeerUpdate::Flags _events;
|
||||
base::lambda_unique<void(const PeerUpdate&)> _handler;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename ObserverType>
|
||||
void registerPeerObserver(PeerUpdate::Flags events, ObserverType *observer, void (ObserverType::*handler)(const PeerUpdate &)) {
|
||||
auto connection = internal::plainRegisterPeerObserver(events, func(observer, handler));
|
||||
observerRegistered(observer, connection);
|
||||
}
|
||||
};
|
||||
base::Observable<PeerUpdate, PeerUpdatedHandler> &PeerUpdated();
|
||||
|
||||
} // namespace Notify
|
||||
|
@ -89,7 +89,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD
|
||||
, _touchAccelerationTime(0)
|
||||
, _touchTime(0)
|
||||
, _menu(0) {
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
|
||||
resize(_width, st::wndMinHeight);
|
||||
|
||||
|
@ -33,7 +33,7 @@ class Date;
|
||||
} // namespace Overview
|
||||
|
||||
class OverviewWidget;
|
||||
class OverviewInner : public QWidget, public AbstractTooltipShower, public RPCSender {
|
||||
class OverviewInner : public QWidget, public AbstractTooltipShower, public RPCSender, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -39,7 +39,9 @@ ActionsWidget::ActionsWidget(QWidget *parent, PeerData *peer) : BlockWidget(pare
|
||||
| UpdateFlag::UserIsBlocked
|
||||
| UpdateFlag::BotCommandsChanged
|
||||
| UpdateFlag::MembersChanged;
|
||||
Notify::registerPeerObserver(observeEvents, this, &ActionsWidget::notifyPeerUpdated);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
validateBlockStatus();
|
||||
refreshButtons();
|
||||
|
@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class BlockWidget : public ScrolledWidget, public Notify::Observer {
|
||||
class BlockWidget : public ScrolledWidget, protected base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -68,8 +68,12 @@ CoverWidget::CoverWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
|
||||
auto observeEvents = ButtonsUpdateFlags
|
||||
| UpdateFlag::NameChanged
|
||||
| UpdateFlag::UserOnlineChanged;
|
||||
Notify::registerPeerObserver(observeEvents, this, &CoverWidget::notifyPeerUpdated);
|
||||
FileDialog::registerObserver(this, &CoverWidget::notifyFileQueryUpdated);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) {
|
||||
notifyFileQueryUpdated(update);
|
||||
});
|
||||
|
||||
connect(App::app(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUploadStatusChanged(PeerId)));
|
||||
connect(App::app(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUploadStatusChanged(PeerId)));
|
||||
@ -285,7 +289,7 @@ void CoverWidget::dragEnterEvent(QDragEnterEvent *e) {
|
||||
|
||||
void CoverWidget::dragLeaveEvent(QDragLeaveEvent *e) {
|
||||
if (_dropArea && !_dropArea->hiding()) {
|
||||
_dropArea->hideAnimated(func(this, &CoverWidget::dropAreaHidden));
|
||||
_dropArea->hideAnimated([this](CoverDropArea *area) { dropAreaHidden(area); });
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,7 +310,7 @@ void CoverWidget::dropEvent(QDropEvent *e) {
|
||||
}
|
||||
|
||||
if (!_dropArea->hiding()) {
|
||||
_dropArea->hideAnimated(func(this, &CoverWidget::dropAreaHidden));
|
||||
_dropArea->hideAnimated([this](CoverDropArea *area) { dropAreaHidden(area); });
|
||||
}
|
||||
e->acceptProposedAction();
|
||||
|
||||
|
@ -40,7 +40,7 @@ class BackButton;
|
||||
class UserpicButton;
|
||||
class CoverDropArea;
|
||||
|
||||
class CoverWidget final : public TWidget, public Notify::Observer {
|
||||
class CoverWidget final : public TWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -57,7 +57,9 @@ void CoverDropArea::paintEvent(QPaintEvent *e) {
|
||||
_cache = QPixmap();
|
||||
if (_hiding) {
|
||||
hide();
|
||||
_hideFinishCallback.call(this);
|
||||
if (_hideFinishCallback) {
|
||||
_hideFinishCallback(this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -93,7 +95,7 @@ void CoverDropArea::setupAnimation() {
|
||||
_cache = myGrab(this);
|
||||
}
|
||||
auto from = _hiding ? 1. : 0., to = _hiding ? 0. : 1.;
|
||||
START_ANIMATION(_a_appearance, func(this, &CoverDropArea::refreshCallback), from, to, st::profileDropAreaDuration, anim::linear);
|
||||
_a_appearance.start([this]() { update(); }, from, to, st::profileDropAreaDuration);
|
||||
}
|
||||
|
||||
} // namespace Profile
|
||||
|
@ -28,7 +28,7 @@ public:
|
||||
|
||||
void showAnimated();
|
||||
|
||||
using HideFinishCallback = Function<void, CoverDropArea*>;
|
||||
using HideFinishCallback = base::lambda_unique<void(CoverDropArea*)>;
|
||||
void hideAnimated(HideFinishCallback &&callback);
|
||||
|
||||
bool hiding() const {
|
||||
@ -39,9 +39,6 @@ protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
void refreshCallback() {
|
||||
update();
|
||||
}
|
||||
void setupAnimation();
|
||||
|
||||
QString _title, _subtitle;
|
||||
|
@ -83,7 +83,9 @@ FixedBar::FixedBar(QWidget *parent, PeerData *peer) : TWidget(parent)
|
||||
|
||||
auto observeEvents = ButtonsUpdateFlags
|
||||
| UpdateFlag::MigrationChanged;
|
||||
Notify::registerPeerObserver(observeEvents, this, &FixedBar::notifyPeerUpdate);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdate(update);
|
||||
}));
|
||||
|
||||
refreshRightActions();
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace Profile {
|
||||
|
||||
class BackButton;
|
||||
|
||||
class FixedBar final : public TWidget, public Notify::Observer {
|
||||
class FixedBar final : public TWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -36,7 +36,9 @@ InfoWidget::InfoWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, pe
|
||||
| UpdateFlag::UsernameChanged
|
||||
| UpdateFlag::UserPhoneChanged
|
||||
| UpdateFlag::UserCanShareContact;
|
||||
Notify::registerPeerObserver(observeEvents, this, &InfoWidget::notifyPeerUpdated);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
refreshLabels();
|
||||
}
|
||||
@ -149,15 +151,13 @@ void InfoWidget::refreshAbout() {
|
||||
textParseEntities(aboutText, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands, &aboutEntities);
|
||||
_about->setMarkedText({ aboutText, aboutEntities });
|
||||
_about->setSelectable(true);
|
||||
_about->setClickHandlerHook(func(this, &InfoWidget::aboutClickHandlerHook));
|
||||
_about->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
BotCommandClickHandler::setPeerForCommand(peer());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool InfoWidget::aboutClickHandlerHook(const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
BotCommandClickHandler::setPeerForCommand(peer());
|
||||
return true;
|
||||
}
|
||||
|
||||
void InfoWidget::refreshMobileNumber() {
|
||||
TextWithEntities phoneText;
|
||||
if (auto user = peer()->asUser()) {
|
||||
|
@ -51,8 +51,6 @@ private:
|
||||
void refreshChannelLink();
|
||||
void refreshVisibility();
|
||||
|
||||
bool aboutClickHandlerHook(const ClickHandlerPtr &handler, Qt::MouseButton button);
|
||||
|
||||
// labelWidget may be nullptr.
|
||||
void setLabeledText(ChildWidget<FlatLabel> *labelWidget, const QString &label,
|
||||
ChildWidget<FlatLabel> *textWidget, const TextWithEntities &textWithEntities, const QString ©Text);
|
||||
|
@ -33,7 +33,9 @@ using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
|
||||
InviteLinkWidget::InviteLinkWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_invite_link_section)) {
|
||||
auto observeEvents = UpdateFlag::InviteLinkChanged | UpdateFlag::UsernameChanged;
|
||||
Notify::registerPeerObserver(observeEvents, this, &InviteLinkWidget::notifyPeerUpdated);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
refreshLink();
|
||||
refreshVisibility();
|
||||
@ -99,19 +101,17 @@ void InviteLinkWidget::refreshLink() {
|
||||
_link->setMarkedText(linkData);
|
||||
_link->setSelectable(true);
|
||||
_link->setContextCopyText(QString());
|
||||
_link->setClickHandlerHook(func(this, &InviteLinkWidget::clickHandlerHook));
|
||||
}
|
||||
}
|
||||
_link->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
auto link = getInviteLink();
|
||||
if (link.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InviteLinkWidget::clickHandlerHook(const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
auto link = getInviteLink();
|
||||
if (link.isEmpty()) {
|
||||
return true;
|
||||
QApplication::clipboard()->setText(link);
|
||||
Ui::showLayer(new InformBox(lang(lng_group_invite_copied)));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
QApplication::clipboard()->setText(link);
|
||||
Ui::showLayer(new InformBox(lang(lng_group_invite_copied)));
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Profile
|
||||
|
@ -46,8 +46,6 @@ private:
|
||||
void refreshLink();
|
||||
void refreshVisibility();
|
||||
|
||||
bool clickHandlerHook(const ClickHandlerPtr &handler, Qt::MouseButton button);
|
||||
|
||||
ChildWidget<FlatLabel> _link = { nullptr };
|
||||
|
||||
};
|
||||
|
@ -49,16 +49,14 @@ MembersWidget::MembersWidget(QWidget *parent, PeerData *peer, TitleVisibility ti
|
||||
auto observeEvents = UpdateFlag::AdminsChanged
|
||||
| UpdateFlag::MembersChanged
|
||||
| UpdateFlag::UserOnlineChanged;
|
||||
Notify::registerPeerObserver(observeEvents, this, &MembersWidget::notifyPeerUpdated);
|
||||
FileDownload::registerImageLoadedObserver(this, &MembersWidget::repaintCallback);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
|
||||
|
||||
refreshMembers();
|
||||
}
|
||||
|
||||
void MembersWidget::repaintCallback() {
|
||||
update();
|
||||
}
|
||||
|
||||
void MembersWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
if (update.peer != peer()) {
|
||||
if (update.flags & UpdateFlag::UserOnlineChanged) {
|
||||
@ -83,7 +81,7 @@ void MembersWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
}
|
||||
}
|
||||
}
|
||||
repaintCallback();
|
||||
this->update();
|
||||
}
|
||||
|
||||
void MembersWidget::refreshUserOnline(UserData *user) {
|
||||
@ -315,17 +313,15 @@ void MembersWidget::refreshLimitReached() {
|
||||
QString link = textRichPrepare(lang(lng_profile_migrate_learn_more));
|
||||
QString text = qsl("%1%2%3\n%4 [a href=\"https://telegram.org/blog/supergroups5k\"]%5[/a]").arg(textcmdStartSemibold()).arg(title).arg(textcmdStopSemibold()).arg(body).arg(link);
|
||||
_limitReachedInfo->setRichText(text);
|
||||
_limitReachedInfo->setClickHandlerHook(func(this, &MembersWidget::limitReachedHook));
|
||||
_limitReachedInfo->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
Ui::showLayer(new ConvertToSupergroupBox(peer()->asChat()));
|
||||
return false;
|
||||
});
|
||||
} else if (!limitReachedShown && _limitReachedInfo) {
|
||||
_limitReachedInfo.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
bool MembersWidget::limitReachedHook(const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
Ui::showLayer(new ConvertToSupergroupBox(peer()->asChat()));
|
||||
return false;
|
||||
}
|
||||
|
||||
void MembersWidget::checkSelfAdmin(ChatData *chat) {
|
||||
if (chat->participants.isEmpty()) return;
|
||||
|
||||
@ -608,7 +604,9 @@ ChannelMembersWidget::ChannelMembersWidget(QWidget *parent, PeerData *peer) : Bl
|
||||
| UpdateFlag::ChannelCanViewMembers
|
||||
| UpdateFlag::AdminsChanged
|
||||
| UpdateFlag::MembersChanged;
|
||||
Notify::registerPeerObserver(observeEvents, this, &ChannelMembersWidget::notifyPeerUpdated);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
refreshButtons();
|
||||
}
|
||||
|
@ -78,7 +78,6 @@ private slots:
|
||||
private:
|
||||
// Observed notifications.
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
void repaintCallback();
|
||||
|
||||
void preloadUserPhotos();
|
||||
|
||||
|
@ -51,7 +51,9 @@ SettingsWidget::SettingsWidget(QWidget *parent, PeerData *peer) : BlockWidget(pa
|
||||
observeEvents |= UpdateFlag::UsernameChanged | UpdateFlag::InviteLinkChanged;
|
||||
}
|
||||
}
|
||||
Notify::registerPeerObserver(observeEvents, this, &SettingsWidget::notifyPeerUpdated);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
refreshButtons();
|
||||
_enableNotifications->finishAnimations();
|
||||
|
@ -51,7 +51,10 @@ QString getButtonText(MediaOverviewType type, int count) {
|
||||
SharedMediaWidget::SharedMediaWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_shared_media))
|
||||
, _history(App::history(peer))
|
||||
, _migrated(peer->migrateFrom() ? App::history(peer->migrateFrom()) : nullptr) {
|
||||
Notify::registerPeerObserver(Notify::PeerUpdate::Flag::SharedMediaChanged, this, &SharedMediaWidget::notifyPeerUpdated);
|
||||
auto observeEvents = Notify::PeerUpdate::Flag::SharedMediaChanged;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
App::main()->preloadOverviews(peer);
|
||||
if (_migrated) {
|
||||
|
@ -36,8 +36,16 @@ UserpicButton::UserpicButton(QWidget *parent, PeerData *peer) : Button(parent),
|
||||
_userpic = prepareUserpicPixmap();
|
||||
}
|
||||
|
||||
Notify::registerPeerObserver(Notify::PeerUpdate::Flag::PhotoChanged, this, &UserpicButton::notifyPeerUpdated);
|
||||
FileDownload::registerImageLoadedObserver(this, &UserpicButton::notifyImageLoaded);
|
||||
auto observeEvents = Notify::PeerUpdate::Flag::PhotoChanged;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
subscribe(FileDownload::ImageLoaded(), [this] {
|
||||
if (_waiting && _peer->userpicLoaded()) {
|
||||
_waiting = false;
|
||||
startNewPhotoShowing();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void UserpicButton::showFinished() {
|
||||
@ -45,7 +53,7 @@ void UserpicButton::showFinished() {
|
||||
_notShownYet = false;
|
||||
if (!_waiting) {
|
||||
_a_appearance.finish();
|
||||
START_ANIMATION(_a_appearance, func(this, &UserpicButton::refreshCallback), 0, 1, st::profilePhotoDuration, anim::linear);
|
||||
_a_appearance.start([this] { update(); }, 0, 1, st::profilePhotoDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,13 +76,6 @@ void UserpicButton::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
this->update();
|
||||
}
|
||||
|
||||
void UserpicButton::notifyImageLoaded() {
|
||||
if (_waiting && _peer->userpicLoaded()) {
|
||||
_waiting = false;
|
||||
startNewPhotoShowing();
|
||||
}
|
||||
}
|
||||
|
||||
void UserpicButton::processPeerPhoto() {
|
||||
bool hasPhoto = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId);
|
||||
setCursor(hasPhoto ? style::cur_pointer : style::cur_default);
|
||||
@ -100,7 +101,7 @@ void UserpicButton::startNewPhotoShowing() {
|
||||
}
|
||||
|
||||
_a_appearance.finish();
|
||||
START_ANIMATION(_a_appearance, func(this, &UserpicButton::refreshCallback), 0, 1, st::profilePhotoDuration, anim::linear);
|
||||
_a_appearance.start([this] { update(); }, 0, 1, st::profilePhotoDuration);
|
||||
update();
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ struct PeerUpdate;
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class UserpicButton final : public Button, public Notify::Observer {
|
||||
class UserpicButton final : public Button, private base::Subscriber {
|
||||
public:
|
||||
UserpicButton(QWidget *parent, PeerData *peer);
|
||||
|
||||
@ -41,11 +41,6 @@ protected:
|
||||
|
||||
private:
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
void notifyImageLoaded();
|
||||
|
||||
void refreshCallback() {
|
||||
update();
|
||||
}
|
||||
|
||||
void processPeerPhoto();
|
||||
void processNewPeerPhoto();
|
||||
|
@ -163,9 +163,11 @@ void BackgroundRow::updateImage() {
|
||||
}
|
||||
|
||||
BackgroundWidget::BackgroundWidget(QWidget *parent, UserData *self) : BlockWidget(parent, self, lang(lng_settings_section_background)) {
|
||||
FileDialog::registerObserver(this, &BackgroundWidget::notifyFileQueryUpdated);
|
||||
createControls();
|
||||
|
||||
subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) {
|
||||
notifyFileQueryUpdated(update);
|
||||
});
|
||||
subscribe(Window::chatBackground(), [this](const Window::ChatBackgroundUpdate &update) {
|
||||
using Update = Window::ChatBackgroundUpdate;
|
||||
if (update.type == Update::Type::New) {
|
||||
|
@ -32,7 +32,7 @@ class WidgetSlideWrap;
|
||||
|
||||
namespace Settings {
|
||||
|
||||
class BlockWidget : public ScrolledWidget, public Notify::Observer, protected base::Subscriber {
|
||||
class BlockWidget : public ScrolledWidget, protected base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -57,8 +57,12 @@ CoverWidget::CoverWidget(QWidget *parent, UserData *self) : BlockWidget(parent,
|
||||
connect(_editNameInline, SIGNAL(clicked()), this, SLOT(onEditName()));
|
||||
|
||||
auto observeEvents = Notify::PeerUpdate::Flag::NameChanged;
|
||||
Notify::registerPeerObserver(observeEvents, this, &CoverWidget::notifyPeerUpdated);
|
||||
FileDialog::registerObserver(this, &CoverWidget::notifyFileQueryUpdated);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) {
|
||||
notifyFileQueryUpdated(update);
|
||||
});
|
||||
|
||||
connect(App::app(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUploadStatusChanged(PeerId)));
|
||||
connect(App::app(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUploadStatusChanged(PeerId)));
|
||||
@ -217,7 +221,7 @@ void CoverWidget::dragEnterEvent(QDragEnterEvent *e) {
|
||||
|
||||
void CoverWidget::dragLeaveEvent(QDragLeaveEvent *e) {
|
||||
if (_dropArea && !_dropArea->hiding()) {
|
||||
_dropArea->hideAnimated(func(this, &CoverWidget::dropAreaHidden));
|
||||
_dropArea->hideAnimated([this](Profile::CoverDropArea *area) { dropAreaHidden(area); });
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,7 +232,7 @@ void CoverWidget::dropEvent(QDropEvent *e) {
|
||||
if (mimeData->hasImage()) {
|
||||
img = qvariant_cast<QImage>(mimeData->imageData());
|
||||
} else {
|
||||
const auto &urls = mimeData->urls();
|
||||
auto urls = mimeData->urls();
|
||||
if (urls.size() == 1) {
|
||||
auto &url = urls.at(0);
|
||||
if (url.isLocalFile()) {
|
||||
@ -238,7 +242,7 @@ void CoverWidget::dropEvent(QDropEvent *e) {
|
||||
}
|
||||
|
||||
if (!_dropArea->hiding()) {
|
||||
_dropArea->hideAnimated(func(this, &CoverWidget::dropAreaHidden));
|
||||
_dropArea->hideAnimated([this](Profile::CoverDropArea *area) { dropAreaHidden(area); });
|
||||
}
|
||||
e->acceptProposedAction();
|
||||
|
||||
|
@ -171,7 +171,9 @@ GeneralWidget::GeneralWidget(QWidget *parent, UserData *self) : BlockWidget(pare
|
||||
, _changeLanguage(this, lang(lng_settings_change_lang), st::defaultBoxLinkButton) {
|
||||
connect(_changeLanguage, SIGNAL(clicked()), this, SLOT(onChangeLanguage()));
|
||||
subscribe(Global::RefChooseCustomLang(), [this]() { chooseCustomLang(); });
|
||||
FileDialog::registerObserver(this, &GeneralWidget::notifyFileQueryUpdated);
|
||||
subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) {
|
||||
notifyFileQueryUpdated(update);
|
||||
});
|
||||
refreshControls();
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,9 @@ using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
|
||||
InfoWidget::InfoWidget(QWidget *parent, UserData *self) : BlockWidget(parent, self, lang(lng_settings_section_info)) {
|
||||
auto observeEvents = UpdateFlag::UsernameChanged | UpdateFlag::UserPhoneChanged;
|
||||
Notify::registerPeerObserver(observeEvents, this, &InfoWidget::notifyPeerUpdated);
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
createControls();
|
||||
}
|
||||
@ -81,7 +83,10 @@ void InfoWidget::refreshUsername() {
|
||||
usernameText.entities.push_back(EntityInText(EntityInTextCustomUrl, 0, usernameText.text.size(), qsl("https://telegram.me/") + self()->username));
|
||||
setLabeledText(_username, lang(lng_profile_username), usernameText, TextWithEntities(), copyText);
|
||||
if (auto text = _username->entity()->textLabel()) {
|
||||
text->setClickHandlerHook(func(this, &InfoWidget::usernameClickHandlerHook));
|
||||
text->setClickHandlerHook([](const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
Ui::showLayer(new UsernameBox());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,19 +101,20 @@ void InfoWidget::refreshLink() {
|
||||
}
|
||||
setLabeledText(_link, lang(lng_profile_link), linkText, linkTextShort, QString());
|
||||
if (auto text = _link->entity()->textLabel()) {
|
||||
text->setClickHandlerHook(func(this, &InfoWidget::usernameClickHandlerHook));
|
||||
text->setClickHandlerHook([](const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
Ui::showLayer(new UsernameBox());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if (auto shortText = _link->entity()->shortTextLabel()) {
|
||||
shortText->setExpandLinksMode(ExpandLinksUrlOnly);
|
||||
shortText->setClickHandlerHook(func(this, &InfoWidget::usernameClickHandlerHook));
|
||||
shortText->setClickHandlerHook([](const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
Ui::showLayer(new UsernameBox());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool InfoWidget::usernameClickHandlerHook(const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
Ui::showLayer(new UsernameBox());
|
||||
return false;
|
||||
}
|
||||
|
||||
void InfoWidget::setLabeledText(ChildWidget<LabeledWrap> &row, const QString &label, const TextWithEntities &textWithEntities, const TextWithEntities &shortTextWithEntities, const QString ©Text) {
|
||||
if (textWithEntities.text.isEmpty()) {
|
||||
row->slideUp();
|
||||
|
@ -38,8 +38,6 @@ private:
|
||||
// Observed notifications.
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
|
||||
bool usernameClickHandlerHook(const ClickHandlerPtr &handler, Qt::MouseButton button);
|
||||
|
||||
void createControls();
|
||||
void refreshControls();
|
||||
void refreshMobileNumber();
|
||||
|
@ -59,6 +59,7 @@ void InnerWidget::selfUpdated() {
|
||||
void InnerWidget::refreshBlocks() {
|
||||
_cover.destroyDelayed();
|
||||
for_const (auto block, _blocks) {
|
||||
block->hide();
|
||||
block->deleteLater();
|
||||
}
|
||||
_blocks.clear();
|
||||
@ -81,6 +82,9 @@ void InnerWidget::refreshBlocks() {
|
||||
|
||||
if (_cover) {
|
||||
_cover->show();
|
||||
if (_showFinished) {
|
||||
_cover->showFinished();
|
||||
}
|
||||
}
|
||||
for_const (auto block, _blocks) {
|
||||
block->show();
|
||||
@ -89,6 +93,7 @@ void InnerWidget::refreshBlocks() {
|
||||
}
|
||||
|
||||
void InnerWidget::showFinished() {
|
||||
_showFinished = true;
|
||||
if (_cover) {
|
||||
_cover->showFinished();
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ private:
|
||||
UserData *_self = nullptr;
|
||||
|
||||
int _contentLeft = 0;
|
||||
bool _showFinished = false;
|
||||
|
||||
int _visibleTop = 0;
|
||||
int _visibleBottom = 0;
|
||||
|
@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
#include "styles/style_settings.h"
|
||||
#include "ui/scrollarea.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "localstorage.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "application.h"
|
||||
@ -80,6 +81,11 @@ void fillCodes() {
|
||||
});
|
||||
Ui::showLayer(box.release());
|
||||
});
|
||||
Codes.insert(qsl("getdifference"), []() {
|
||||
if (auto main = App::main()) {
|
||||
main->getDifference();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void codesFeedString(const QString &text) {
|
||||
|
@ -801,7 +801,6 @@ StickerPanInner::StickerPanInner() : ScrolledWidget()
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
||||
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(onImageLoaded()));
|
||||
connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings()));
|
||||
|
||||
_previewTimer.setSingleShot(true);
|
||||
@ -809,6 +808,11 @@ StickerPanInner::StickerPanInner() : ScrolledWidget()
|
||||
|
||||
_updateInlineItems.setSingleShot(true);
|
||||
connect(&_updateInlineItems, SIGNAL(timeout()), this, SLOT(onUpdateInlineItems()));
|
||||
|
||||
subscribe(FileDownload::ImageLoaded(), [this] {
|
||||
update();
|
||||
readVisibleSets();
|
||||
});
|
||||
}
|
||||
|
||||
void StickerPanInner::setMaxHeight(int32 h) {
|
||||
@ -855,11 +859,6 @@ void StickerPanInner::readVisibleSets() {
|
||||
}
|
||||
}
|
||||
|
||||
void StickerPanInner::onImageLoaded() {
|
||||
update();
|
||||
readVisibleSets();
|
||||
}
|
||||
|
||||
int StickerPanInner::featuredRowHeight() const {
|
||||
return st::featuredStickersHeader + st::stickerPanSize.height() + st::featuredStickersSkip;
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ struct StickerIcon {
|
||||
int pixh = 0;
|
||||
};
|
||||
|
||||
class StickerPanInner : public ScrolledWidget {
|
||||
class StickerPanInner : public ScrolledWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@ -274,7 +274,6 @@ private slots:
|
||||
void onPreview();
|
||||
void onUpdateInlineItems();
|
||||
void onSwitchPm();
|
||||
void onImageLoaded();
|
||||
|
||||
signals:
|
||||
void selected(DocumentData *sticker);
|
||||
|
@ -27,12 +27,11 @@ static const ChannelId NoChannel = 0;
|
||||
|
||||
typedef int32 MsgId;
|
||||
struct FullMsgId {
|
||||
FullMsgId() : channel(NoChannel), msg(0) {
|
||||
}
|
||||
FullMsgId() = default;
|
||||
FullMsgId(ChannelId channel, MsgId msg) : channel(channel), msg(msg) {
|
||||
}
|
||||
ChannelId channel;
|
||||
MsgId msg;
|
||||
ChannelId channel = NoChannel;
|
||||
MsgId msg = 0;
|
||||
};
|
||||
|
||||
typedef uint64 PeerId;
|
||||
|
@ -120,7 +120,7 @@ AnimationManager::AnimationManager() : _timer(this), _iterating(false) {
|
||||
|
||||
void AnimationManager::start(Animation *obj) {
|
||||
if (_iterating) {
|
||||
_starting.insert(obj, NullType());
|
||||
_starting.insert(obj);
|
||||
if (!_stopping.isEmpty()) {
|
||||
_stopping.remove(obj);
|
||||
}
|
||||
@ -128,21 +128,21 @@ void AnimationManager::start(Animation *obj) {
|
||||
if (_objects.isEmpty()) {
|
||||
_timer.start(AnimationTimerDelta);
|
||||
}
|
||||
_objects.insert(obj, NullType());
|
||||
_objects.insert(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationManager::stop(Animation *obj) {
|
||||
if (_iterating) {
|
||||
_stopping.insert(obj, NullType());
|
||||
_stopping.insert(obj);
|
||||
if (!_starting.isEmpty()) {
|
||||
_starting.remove(obj);
|
||||
}
|
||||
} else {
|
||||
AnimatingObjects::iterator i = _objects.find(obj);
|
||||
auto i = _objects.find(obj);
|
||||
if (i != _objects.cend()) {
|
||||
_objects.erase(i);
|
||||
if (_objects.isEmpty()) {
|
||||
if (_objects.empty()) {
|
||||
_timer.stop();
|
||||
}
|
||||
}
|
||||
@ -152,26 +152,26 @@ void AnimationManager::stop(Animation *obj) {
|
||||
void AnimationManager::timeout() {
|
||||
_iterating = true;
|
||||
uint64 ms = getms();
|
||||
for (AnimatingObjects::const_iterator i = _objects.begin(), e = _objects.end(); i != e; ++i) {
|
||||
if (!_stopping.contains(i.key())) {
|
||||
i.key()->step(ms, true);
|
||||
for_const (auto object, _objects) {
|
||||
if (!_stopping.contains(object)) {
|
||||
object->step(ms, true);
|
||||
}
|
||||
}
|
||||
_iterating = false;
|
||||
|
||||
if (!_starting.isEmpty()) {
|
||||
for (AnimatingObjects::iterator i = _starting.begin(), e = _starting.end(); i != e; ++i) {
|
||||
_objects.insert(i.key(), NullType());
|
||||
for_const (auto object, _starting) {
|
||||
_objects.insert(object);
|
||||
}
|
||||
_starting.clear();
|
||||
}
|
||||
if (!_stopping.isEmpty()) {
|
||||
for (AnimatingObjects::iterator i = _stopping.begin(), e = _stopping.end(); i != e; ++i) {
|
||||
_objects.remove(i.key());
|
||||
for_const (auto object, _stopping) {
|
||||
_objects.remove(object);
|
||||
}
|
||||
_stopping.clear();
|
||||
}
|
||||
if (!_objects.size()) {
|
||||
if (_objects.empty()) {
|
||||
_timer.stop();
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
#pragma once
|
||||
|
||||
#include "core/basic_types.h"
|
||||
#include "core/lambda_wrap.h"
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtGui/QColor>
|
||||
|
||||
@ -57,6 +58,7 @@ namespace anim {
|
||||
|
||||
class fvalue { // float animated value
|
||||
public:
|
||||
using ValueType = float64;
|
||||
|
||||
fvalue() {
|
||||
}
|
||||
@ -88,15 +90,14 @@ namespace anim {
|
||||
_delta = 0;
|
||||
}
|
||||
|
||||
typedef float64 Type;
|
||||
|
||||
private:
|
||||
|
||||
float64 _cur, _from, _delta;
|
||||
|
||||
};
|
||||
|
||||
class ivalue { // int animated value
|
||||
public:
|
||||
using ValueType = int32;
|
||||
|
||||
ivalue() {
|
||||
}
|
||||
@ -128,16 +129,15 @@ namespace anim {
|
||||
_delta = 0;
|
||||
}
|
||||
|
||||
typedef int32 Type;
|
||||
|
||||
private:
|
||||
|
||||
int32 _cur;
|
||||
float64 _from, _delta;
|
||||
|
||||
};
|
||||
|
||||
class cvalue { // QColor animated value
|
||||
public:
|
||||
using ValueType = QColor;
|
||||
|
||||
cvalue() {
|
||||
}
|
||||
@ -199,12 +199,10 @@ namespace anim {
|
||||
_delta_r = _delta_g = _delta_b = _delta_a = 0;
|
||||
}
|
||||
|
||||
typedef QColor Type;
|
||||
|
||||
private:
|
||||
|
||||
QColor _cur;
|
||||
float64 _from_r, _from_g, _from_b, _from_a, _delta_r, _delta_g, _delta_b, _delta_a;
|
||||
|
||||
};
|
||||
|
||||
void startManager();
|
||||
@ -380,105 +378,81 @@ AnimationCallbacks animation(Param param, Type *obj, typename AnimationCallbacks
|
||||
template <typename AnimType>
|
||||
class SimpleAnimation {
|
||||
public:
|
||||
using Callback = Function<void>;
|
||||
using ValueType = typename AnimType::ValueType;
|
||||
using Callback = base::lambda_unique<void()>;
|
||||
|
||||
SimpleAnimation() {
|
||||
bool animating() const {
|
||||
if (_data) {
|
||||
if (_data->a_animation.animating()) {
|
||||
return true;
|
||||
}
|
||||
_data.reset();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool animating(uint64 ms) {
|
||||
if (_data && _data->_a.animating()) {
|
||||
_data->_a.step(ms);
|
||||
return _data && _data->_a.animating();
|
||||
if (animating()) {
|
||||
_data->a_animation.step(ms);
|
||||
return animating();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return !_data;
|
||||
ValueType current() const {
|
||||
t_assert(_data != nullptr);
|
||||
return _data->value.current();
|
||||
}
|
||||
|
||||
typename AnimType::Type current() {
|
||||
return _data ? _data->a.current() : typename AnimType::Type();
|
||||
ValueType current(const ValueType &def) const {
|
||||
return _data ? current() : def;
|
||||
}
|
||||
|
||||
typename AnimType::Type current(const typename AnimType::Type &def) {
|
||||
return _data ? _data->a.current() : def;
|
||||
}
|
||||
|
||||
typename AnimType::Type current(uint64 ms, const typename AnimType::Type &def) {
|
||||
ValueType current(uint64 ms, const ValueType &def) {
|
||||
return animating(ms) ? current() : def;
|
||||
}
|
||||
|
||||
void setup(const typename AnimType::Type &from, Callback &&update) {
|
||||
template <typename Lambda>
|
||||
void start(Lambda &&updateCallback, const ValueType &from, const ValueType &to, float64 duration, anim::transition transition = anim::linear) {
|
||||
if (!_data) {
|
||||
_data = new Data(from, std_::move(update), animation(this, &SimpleAnimation<AnimType>::step));
|
||||
} else {
|
||||
_data->a = AnimType(from, from);
|
||||
}
|
||||
}
|
||||
|
||||
void start(const typename AnimType::Type &to, float64 duration, anim::transition transition = anim::linear) {
|
||||
if (_data) {
|
||||
_data->a.start(to);
|
||||
_data->_a.start();
|
||||
_data->duration = duration;
|
||||
_data->transition = transition;
|
||||
_data = std_::make_unique<Data>(from, std_::move(updateCallback));
|
||||
}
|
||||
_data->value.start(to);
|
||||
_data->duration = duration;
|
||||
_data->transition = transition;
|
||||
_data->a_animation.start();
|
||||
}
|
||||
|
||||
void finish() {
|
||||
if (isNull()) {
|
||||
return;
|
||||
if (_data) {
|
||||
_data->value.finish();
|
||||
_data->a_animation.stop();
|
||||
_data.reset();
|
||||
}
|
||||
|
||||
_data->a.finish();
|
||||
_data->_a.stop();
|
||||
delete _data;
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
~SimpleAnimation() {
|
||||
deleteAndMark(_data);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Data {
|
||||
Data(const typename AnimType::Type &from, Callback &&update, AnimationCallbacks &&acb)
|
||||
: a(from, from)
|
||||
, _a(std_::move(acb))
|
||||
, update(std_::move(update))
|
||||
, duration(0)
|
||||
, transition(anim::linear) {
|
||||
Data(const ValueType &from, Callback &&updateCallback)
|
||||
: value(from, from)
|
||||
, a_animation(animation(this, &Data::step))
|
||||
, updateCallback(std_::move(updateCallback)) {
|
||||
}
|
||||
AnimType a;
|
||||
Animation _a;
|
||||
Callback update;
|
||||
float64 duration;
|
||||
anim::transition transition;
|
||||
void step(float64 ms, bool timer) {
|
||||
auto dt = (ms >= duration) ? 1. : (ms / duration);
|
||||
if (dt >= 1) {
|
||||
value.finish();
|
||||
a_animation.stop();
|
||||
} else {
|
||||
value.update(dt, transition);
|
||||
}
|
||||
updateCallback();
|
||||
}
|
||||
|
||||
AnimType value;
|
||||
Animation a_animation;
|
||||
Callback updateCallback;
|
||||
float64 duration = 0.;
|
||||
anim::transition transition = anim::linear;
|
||||
};
|
||||
Data *_data = nullptr;
|
||||
|
||||
void step(float64 ms, bool timer) {
|
||||
float64 dt = (ms >= _data->duration) ? 1 : (ms / _data->duration);
|
||||
if (dt >= 1) {
|
||||
_data->a.finish();
|
||||
_data->_a.stop();
|
||||
} else {
|
||||
_data->a.update(dt, _data->transition);
|
||||
}
|
||||
|
||||
Callback callbackCache, *toCall = &_data->update;
|
||||
if (!_data->_a.animating()) {
|
||||
callbackCache = std_::move(_data->update);
|
||||
toCall = &callbackCache;
|
||||
|
||||
delete _data;
|
||||
_data = nullptr;
|
||||
}
|
||||
if (timer) {
|
||||
toCall->call();
|
||||
}
|
||||
}
|
||||
mutable std_::unique_ptr<Data> _data;
|
||||
|
||||
};
|
||||
|
||||
@ -486,18 +460,8 @@ using FloatAnimation = SimpleAnimation<anim::fvalue>;
|
||||
using IntAnimation = SimpleAnimation<anim::ivalue>;
|
||||
using ColorAnimation = SimpleAnimation<anim::cvalue>;
|
||||
|
||||
// Macro allows us to lazily create updateCallback.
|
||||
#define ENSURE_ANIMATION(animation, updateCallback, from) \
|
||||
if ((animation).isNull()) { \
|
||||
(animation).setup((from), (updateCallback)); \
|
||||
}
|
||||
|
||||
#define START_ANIMATION(animation, updateCallback, from, to, duration, transition) \
|
||||
ENSURE_ANIMATION(animation, updateCallback, from); \
|
||||
(animation).start((to), (duration), (transition))
|
||||
|
||||
class AnimationManager : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AnimationManager();
|
||||
@ -511,7 +475,7 @@ public slots:
|
||||
void clipCallback(Media::Clip::Reader *reader, qint32 threadIndex, qint32 notification);
|
||||
|
||||
private:
|
||||
typedef QMap<Animation*, NullType> AnimatingObjects;
|
||||
using AnimatingObjects = OrderedSet<Animation*>;
|
||||
AnimatingObjects _objects, _starting, _stopping;
|
||||
QTimer _timer;
|
||||
bool _iterating;
|
||||
|
@ -118,7 +118,7 @@ void HistoryDownButton::hideAnimated() {
|
||||
void HistoryDownButton::toggleAnimated() {
|
||||
_shown = !_shown;
|
||||
float64 from = _shown ? 0. : 1., to = _shown ? 1. : 0.;
|
||||
START_ANIMATION(_a_show, func(this, &HistoryDownButton::repaintCallback), from, to, st::btnAttachEmoji.duration, anim::linear);
|
||||
_a_show.start([this] { update(); }, from, to, st::btnAttachEmoji.duration);
|
||||
}
|
||||
|
||||
void HistoryDownButton::finishAnimation() {
|
||||
|
@ -47,9 +47,6 @@ protected:
|
||||
|
||||
private:
|
||||
void toggleAnimated();
|
||||
void repaintCallback() {
|
||||
update();
|
||||
}
|
||||
void step_arrowOver(float64 ms, bool timer);
|
||||
|
||||
QPixmap _cache;
|
||||
|
@ -49,7 +49,7 @@ void IconButton::onStateChanged(int oldState, ButtonStateChangeSource source) {
|
||||
if (over != (oldState & StateOver)) {
|
||||
auto from = over ? 0. : 1.;
|
||||
auto to = over ? 1. : 0.;
|
||||
START_ANIMATION(_a_over, func(this, &IconButton::updateCallback), from, to, _st.duration, anim::linear);
|
||||
_a_over.start([this] { update(); }, from, to, _st.duration);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,10 +37,6 @@ protected:
|
||||
void onStateChanged(int oldState, ButtonStateChangeSource source) override;
|
||||
|
||||
private:
|
||||
void updateCallback() {
|
||||
update();
|
||||
}
|
||||
|
||||
const style::IconButton &_st;
|
||||
const style::icon *_iconOverride = nullptr;
|
||||
|
||||
|
@ -65,7 +65,9 @@ void FadeAnimation::stopAnimation() {
|
||||
_cache = QPixmap();
|
||||
updateCallback();
|
||||
_widget->showChildren();
|
||||
_finishedCallback.call();
|
||||
if (_finishedCallback) {
|
||||
_finishedCallback();
|
||||
}
|
||||
}
|
||||
if (_visible == _widget->isHidden()) {
|
||||
_widget->setVisible(_visible);
|
||||
@ -91,7 +93,9 @@ void FadeAnimation::startAnimation(int duration) {
|
||||
_cache = myGrab(_widget);
|
||||
_widget->hideChildren();
|
||||
}
|
||||
START_ANIMATION(_animation, func(this, &FadeAnimation::updateCallback), _visible ? 0. : 1., _visible ? 1. : 0., duration, anim::linear);
|
||||
auto from = _visible ? 0. : 1.;
|
||||
auto to = _visible ? 1. : 0.;
|
||||
_animation.start([this]() { updateCallback(); }, from, to, duration);
|
||||
updateCallback();
|
||||
if (_widget->isHidden()) {
|
||||
_widget->show();
|
||||
@ -101,7 +105,9 @@ void FadeAnimation::startAnimation(int duration) {
|
||||
void FadeAnimation::updateCallback() {
|
||||
if (_animation.animating(getms())) {
|
||||
_widget->update();
|
||||
_updatedCallback.call(_animation.current());
|
||||
if (_updatedCallback) {
|
||||
_updatedCallback(_animation.current(_visible ? 1. : 0.));
|
||||
}
|
||||
} else {
|
||||
stopAnimation();
|
||||
}
|
||||
|
@ -31,10 +31,10 @@ public:
|
||||
bool paint(Painter &p);
|
||||
void refreshCache();
|
||||
|
||||
using FinishedCallback = Function<void>;
|
||||
using FinishedCallback = base::lambda_unique<void()>;
|
||||
void setFinishedCallback(FinishedCallback &&callback);
|
||||
|
||||
using UpdatedCallback = Function<void, float64>;
|
||||
using UpdatedCallback = base::lambda_unique<void(float64)>;
|
||||
void setUpdatedCallback(UpdatedCallback &&callback);
|
||||
|
||||
void show();
|
||||
|
@ -255,7 +255,7 @@ QString filedialogAllFilesFilter() {
|
||||
namespace FileDialog {
|
||||
namespace {
|
||||
|
||||
using internal::QueryUpdateHandler;
|
||||
base::Observable<QueryUpdate> QueryDoneObservable;
|
||||
|
||||
struct Query {
|
||||
enum class Type {
|
||||
@ -285,16 +285,10 @@ void StartCallback() {
|
||||
Queries.makeIfNull();
|
||||
}
|
||||
|
||||
void FinishCallback() {
|
||||
Queries.clear();
|
||||
}
|
||||
|
||||
Notify::SimpleObservedEventRegistrator<QueryUpdateHandler> creator(StartCallback, FinishCallback);
|
||||
|
||||
} // namespace
|
||||
|
||||
QueryId queryReadFile(const QString &caption, const QString &filter) {
|
||||
t_assert(creator.started());
|
||||
Queries.makeIfNull();
|
||||
|
||||
Queries->push_back(Query(Query::Type::ReadFile, caption, filter));
|
||||
Global::RefHandleFileDialogQueue().call();
|
||||
@ -302,7 +296,7 @@ QueryId queryReadFile(const QString &caption, const QString &filter) {
|
||||
}
|
||||
|
||||
QueryId queryReadFiles(const QString &caption, const QString &filter) {
|
||||
t_assert(creator.started());
|
||||
Queries.makeIfNull();
|
||||
|
||||
Queries->push_back(Query(Query::Type::ReadFiles, caption, filter));
|
||||
Global::RefHandleFileDialogQueue().call();
|
||||
@ -310,7 +304,7 @@ QueryId queryReadFiles(const QString &caption, const QString &filter) {
|
||||
}
|
||||
|
||||
QueryId queryWriteFile(const QString &caption, const QString &filter, const QString &filePath) {
|
||||
t_assert(creator.started());
|
||||
Queries.makeIfNull();
|
||||
|
||||
Queries->push_back(Query(Query::Type::WriteFile, caption, filter, filePath));
|
||||
Global::RefHandleFileDialogQueue().call();
|
||||
@ -318,7 +312,7 @@ QueryId queryWriteFile(const QString &caption, const QString &filter, const QStr
|
||||
}
|
||||
|
||||
QueryId queryReadFolder(const QString &caption) {
|
||||
t_assert(creator.started());
|
||||
Queries.makeIfNull();
|
||||
|
||||
Queries->push_back(Query(Query::Type::ReadFolder, caption));
|
||||
Global::RefHandleFileDialogQueue().call();
|
||||
@ -326,7 +320,7 @@ QueryId queryReadFolder(const QString &caption) {
|
||||
}
|
||||
|
||||
bool processQuery() {
|
||||
if (!creator.started() || !Global::started() || Queries->isEmpty()) return false;
|
||||
if (!Queries || !Global::started() || Queries->isEmpty()) return false;
|
||||
|
||||
auto query = Queries->front();
|
||||
Queries->pop_front();
|
||||
@ -374,17 +368,14 @@ bool processQuery() {
|
||||
}
|
||||
|
||||
// No one knows what happened during filedialogGet*() call in the event loop.
|
||||
if (!creator.started() || !Global::started()) return false;
|
||||
if (!Queries || !Global::started()) return false;
|
||||
|
||||
creator.notify(update);
|
||||
QueryDone().notify(std_::move(update));
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
Notify::ConnectionId plainRegisterObserver(QueryUpdateHandler &&handler) {
|
||||
return creator.registerObserver(std_::forward<QueryUpdateHandler>(handler));
|
||||
base::Observable<QueryUpdate> &QueryDone() {
|
||||
return QueryDoneObservable;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace FileDialog
|
||||
|
@ -63,17 +63,6 @@ QueryId queryReadFolder(const QString &caption);
|
||||
// NB! This function enters an event loop.
|
||||
bool processQuery();
|
||||
|
||||
namespace internal {
|
||||
|
||||
using QueryUpdateHandler = Function<void, const QueryUpdate&>;
|
||||
Notify::ConnectionId plainRegisterObserver(QueryUpdateHandler &&handler);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename ObserverType>
|
||||
void registerObserver(ObserverType *observer, void (ObserverType::*handler)(const QueryUpdate &)) {
|
||||
auto connection = internal::plainRegisterObserver(func(observer, handler));
|
||||
Notify::observerRegistered(observer, connection);
|
||||
}
|
||||
base::Observable<QueryUpdate> &QueryDone();
|
||||
|
||||
} // namespace FileDialog
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user