New profile cover actions by buttons done.

Two new types of Observers: image loaded and async file dialog.
This commit is contained in:
John Preston 2016-05-25 20:59:21 +03:00
parent a510bb54ec
commit 46ad43bb1e
29 changed files with 578 additions and 139 deletions

View File

@ -97,6 +97,10 @@ void ApiWrap::resolveMessageDatas() {
} }
} }
void ApiWrap::updatesReceived(const MTPUpdates &updates) {
App::main()->sentUpdatesReceived(updates);
}
void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) { void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
switch (msgs.type()) { switch (msgs.type()) {
case mtpc_messages_messages: { case mtpc_messages_messages: {
@ -679,6 +683,45 @@ void ApiWrap::requestStickerSets() {
} }
} }
void ApiWrap::joinChannel(ChannelData *channel) {
if (channel->amIn()) {
channelAmInUpdated(channel);
} else if (!_channelAmInRequests.contains(channel)) {
auto requestId = MTP::send(MTPchannels_JoinChannel(channel->inputChannel), rpcDone(&ApiWrap::channelAmInDone, channel), rpcFail(&ApiWrap::channelAmInFail, channel));
_channelAmInRequests.insert(channel, requestId);
}
}
void ApiWrap::leaveChannel(ChannelData *channel) {
if (!channel->amIn()) {
channelAmInUpdated(channel);
} else if (!_channelAmInRequests.contains(channel)) {
auto requestId = MTP::send(MTPchannels_LeaveChannel(channel->inputChannel), rpcDone(&ApiWrap::channelAmInDone, channel), rpcFail(&ApiWrap::channelAmInFail, channel));
_channelAmInRequests.insert(channel, requestId);
}
}
void ApiWrap::channelAmInUpdated(ChannelData *channel) {
Notify::PeerUpdate update(channel);
update.flags |= Notify::PeerUpdateFlag::ChannelAmIn;
Notify::peerUpdatedDelayed(update);
Notify::peerUpdatedSendDelayed();
}
void ApiWrap::channelAmInDone(ChannelData *channel, const MTPUpdates &updates) {
_channelAmInRequests.remove(channel);
updatesReceived(updates);
}
bool ApiWrap::channelAmInFail(ChannelData *channel, const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_channelAmInRequests.remove(channel);
return true;
}
void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) { void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
_stickerSetRequests.remove(setId); _stickerSetRequests.remove(setId);

View File

@ -28,7 +28,7 @@ public:
ApiWrap(QObject *parent); ApiWrap(QObject *parent);
void init(); void init();
typedef SharedCallback<void, ChannelData*, MsgId> RequestMessageDataCallback; using RequestMessageDataCallback = SharedCallback<void, ChannelData*, MsgId>;
void requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr<RequestMessageDataCallback> callback); void requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr<RequestMessageDataCallback> callback);
void requestFullPeer(PeerData *peer); void requestFullPeer(PeerData *peer);
@ -50,6 +50,9 @@ public:
void scheduleStickerSetRequest(uint64 setId, uint64 access); void scheduleStickerSetRequest(uint64 setId, uint64 access);
void requestStickerSets(); void requestStickerSets();
void joinChannel(ChannelData *channel);
void leaveChannel(ChannelData *channel);
~ApiWrap(); ~ApiWrap();
signals: signals:
@ -65,6 +68,8 @@ public slots:
private: private:
void updatesReceived(const MTPUpdates &updates);
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req); void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
struct MessageDataRequest { struct MessageDataRequest {
MessageDataRequest() : req(0) { MessageDataRequest() : req(0) {
@ -120,4 +125,9 @@ private:
void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result); void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result);
bool gotStickerSetFail(uint64 setId, const RPCError &error); bool gotStickerSetFail(uint64 setId, const RPCError &error);
QMap<ChannelData*, mtpRequestId> _channelAmInRequests;
void channelAmInUpdated(ChannelData *channel);
void channelAmInDone(ChannelData *channel, const MTPUpdates &updates);
bool channelAmInFail(ChannelData *channel, const RPCError &error);
}; };

View File

@ -369,7 +369,7 @@ namespace {
UserData *result = nullptr; UserData *result = nullptr;
for_const (auto &user, users.c_vector().v) { for_const (auto &user, users.c_vector().v) {
UserData *data = nullptr; UserData *data = nullptr;
bool wasContact = false, canShareContact = false, minimal = false; bool wasContact = false, minimal = false;
const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty(); const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
Notify::PeerUpdate update; Notify::PeerUpdate update;
@ -548,12 +548,13 @@ namespace {
switch (chat.type()) { switch (chat.type()) {
case mtpc_chat: { case mtpc_chat: {
const auto &d(chat.c_chat()); auto &d(chat.c_chat());
data = App::chat(peerFromChat(d.vid.v)); data = App::chat(peerFromChat(d.vid.v));
data->input = MTP_inputPeerChat(d.vid); auto cdata = data->asChat();
auto canEdit = cdata->canEdit();
ChatData *cdata = data->asChat(); data->input = MTP_inputPeerChat(d.vid);
cdata->setNameDelayed(qs(d.vtitle)); cdata->setNameDelayed(qs(d.vtitle));
cdata->setPhoto(d.vphoto); cdata->setPhoto(d.vphoto);
cdata->date = d.vdate.v; cdata->date = d.vdate.v;
@ -604,14 +605,18 @@ namespace {
cdata->version = d.vversion.v; cdata->version = d.vversion.v;
cdata->invalidateParticipants(); cdata->invalidateParticipants();
} }
if (canEdit != cdata->canEdit()) {
update.flags |= UpdateFlag::ChatCanEdit;
}
} break; } break;
case mtpc_chatForbidden: { case mtpc_chatForbidden: {
const auto &d(chat.c_chatForbidden()); auto &d(chat.c_chatForbidden());
data = App::chat(peerFromChat(d.vid.v)); data = App::chat(peerFromChat(d.vid.v));
data->input = MTP_inputPeerChat(d.vid); auto cdata = data->asChat();
auto canEdit = cdata->canEdit();
ChatData *cdata = data->asChat(); data->input = MTP_inputPeerChat(d.vid);
cdata->setNameDelayed(qs(d.vtitle)); cdata->setNameDelayed(qs(d.vtitle));
cdata->setPhoto(MTP_chatPhotoEmpty()); cdata->setPhoto(MTP_chatPhotoEmpty());
cdata->date = 0; cdata->date = 0;
@ -619,27 +624,32 @@ namespace {
cdata->invalidateParticipants(); cdata->invalidateParticipants();
cdata->flags = 0; cdata->flags = 0;
cdata->isForbidden = true; cdata->isForbidden = true;
if (canEdit != cdata->canEdit()) {
update.flags |= UpdateFlag::ChatCanEdit;
}
} break; } break;
case mtpc_channel: { case mtpc_channel: {
const auto &d(chat.c_channel()); auto &d(chat.c_channel());
PeerId peer(peerFromChannel(d.vid.v)); auto peerId = peerFromChannel(d.vid.v);
minimal = d.is_min(); minimal = d.is_min();
if (minimal) { if (minimal) {
data = App::channelLoaded(peer); data = App::channelLoaded(peerId);
if (!data) { if (!data) {
continue; // minimal is not loaded, need to make getDifference continue; // minimal is not loaded, need to make getDifference
} }
} else { } else {
data = App::channel(peer); data = App::channel(peerId);
data->input = MTP_inputPeerChannel(d.vid, d.has_access_hash() ? d.vaccess_hash : MTP_long(0)); data->input = MTP_inputPeerChannel(d.vid, d.has_access_hash() ? d.vaccess_hash : MTP_long(0));
} }
ChannelData *cdata = data->asChannel(); auto cdata = data->asChannel();
auto wasInChannel = cdata->amIn(); auto wasInChannel = cdata->amIn();
auto canEditPhoto = cdata->canEditPhoto();
auto canAddMembers = cdata->canAddParticipants();
if (minimal) { if (minimal) {
int32 mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy; MTPDchannel::Flags mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy;
cdata->flags = (cdata->flags & ~mask) | (d.vflags.v & mask); cdata->flags = (cdata->flags & ~mask) | (d.vflags.v & mask);
} else { } else {
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash); cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
@ -662,19 +672,27 @@ namespace {
cdata->flagsUpdated(); cdata->flagsUpdated();
cdata->setPhoto(d.vphoto); cdata->setPhoto(d.vphoto);
if (wasInChannel != cdata->amIn() && !cdata->isMegagroup()) { if (wasInChannel != cdata->amIn()) {
update.flags |= UpdateFlag::ChannelAmIn; update.flags |= UpdateFlag::ChannelAmIn;
} }
if (canEditPhoto != cdata->canEditPhoto()) {
update.flags |= UpdateFlag::ChannelCanEditPhoto;
}
if (canAddMembers != cdata->canAddParticipants()) {
update.flags |= UpdateFlag::ChannelCanAddMembers;
}
} break; } break;
case mtpc_channelForbidden: { case mtpc_channelForbidden: {
const auto &d(chat.c_channelForbidden()); auto &d(chat.c_channelForbidden());
PeerId peer(peerFromChannel(d.vid.v)); auto peerId = peerFromChannel(d.vid.v);
data = App::channel(peer); data = App::channel(peerId);
data->input = MTP_inputPeerChannel(d.vid, d.vaccess_hash); data->input = MTP_inputPeerChannel(d.vid, d.vaccess_hash);
ChannelData *cdata = data->asChannel(); auto cdata = data->asChannel();
auto wasInChannel = cdata->amIn(); auto wasInChannel = cdata->amIn();
auto canEditPhoto = cdata->canEditPhoto();
auto canAddMembers = cdata->canAddParticipants();
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash); cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
@ -686,9 +704,15 @@ namespace {
cdata->count = 0; cdata->count = 0;
cdata->isForbidden = true; cdata->isForbidden = true;
if (wasInChannel != cdata->amIn() && !cdata->isMegagroup()) { if (wasInChannel != cdata->amIn()) {
update.flags |= UpdateFlag::ChannelAmIn; update.flags |= UpdateFlag::ChannelAmIn;
} }
if (canEditPhoto != cdata->canEditPhoto()) {
update.flags |= UpdateFlag::ChannelCanEditPhoto;
}
if (canAddMembers != cdata->canAddParticipants()) {
update.flags |= UpdateFlag::ChannelCanAddMembers;
}
} break; } break;
} }
if (!data) continue; if (!data) continue;

View File

@ -27,10 +27,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwidget.h" #include "mainwidget.h"
#include "lang.h" #include "lang.h"
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
#include "ui/filedialog.h"
#include "langloaderplain.h" #include "langloaderplain.h"
#include "localstorage.h" #include "localstorage.h"
#include "autoupdater.h" #include "autoupdater.h"
#include "core/observer.h" #include "core/observer.h"
#include "observer_peer.h"
namespace { namespace {
void mtpStateChanged(int32 dc, int32 state) { void mtpStateChanged(int32 dc, int32 state) {
@ -817,6 +819,7 @@ void AppClass::doMtpUnpause() {
void AppClass::selfPhotoCleared(const MTPUserProfilePhoto &result) { void AppClass::selfPhotoCleared(const MTPUserProfilePhoto &result) {
if (!App::self()) return; if (!App::self()) return;
App::self()->setPhoto(result); App::self()->setPhoto(result);
Notify::peerUpdatedSendDelayed();
emit peerPhotoDone(App::self()->id); emit peerPhotoDone(App::self()->id);
} }
@ -906,6 +909,14 @@ void AppClass::call_handleUnreadCounterUpdate() {
} }
} }
void AppClass::call_handleFileDialogQueue() {
while (true) {
if (!FileDialog::processQuery()) {
return;
}
}
}
void AppClass::killDownloadSessions() { void AppClass::killDownloadSessions() {
uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout; uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout;
for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) { for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {

View File

@ -203,6 +203,7 @@ public slots:
void call_handleHistoryUpdate(); void call_handleHistoryUpdate();
void call_handleUnreadCounterUpdate(); void call_handleUnreadCounterUpdate();
void call_handleFileDialogQueue();
private: private:

View File

@ -2003,11 +2003,11 @@ MembersFilter MembersInner::filter() const {
return _filter; return _filter;
} }
QMap<UserData*, bool> MembersInner::already() const { MembersAlreadyIn MembersInner::already() const {
MembersAlreadyIn result; MembersAlreadyIn result;
for (int32 i = 0, l = _rows.size(); i < l; ++i) { for_const (auto peer, _rows) {
if (_rows.at(i)->isUser()) { if (peer->isUser()) {
result.insert(_rows.at(i)->asUser(), true); result.insert(peer->asUser());
} }
} }
return result; return result;

View File

@ -31,7 +31,7 @@ enum MembersFilter {
MembersFilterRecent, MembersFilterRecent,
MembersFilterAdmins, MembersFilterAdmins,
}; };
typedef QMap<UserData*, bool> MembersAlreadyIn; using MembersAlreadyIn = OrderedSet<UserData*>;
QString cantInviteError(); QString cantInviteError();
@ -318,7 +318,7 @@ public:
} }
void clearSel(); void clearSel();
QMap<UserData*, bool> already() const; MembersAlreadyIn already() const;
~MembersInner(); ~MembersInner();

View File

@ -33,6 +33,13 @@ T *getPointerAndReset(T *&ptr) {
return result; return result;
} }
template <typename T>
T createAndSwap(T &value) {
T result;
std::swap(result, value);
return result;
}
struct NullType { struct NullType {
}; };

View File

@ -30,6 +30,8 @@ NeverFreedPointer<StartCallbacksList> StartCallbacks;
NeverFreedPointer<FinishCallbacksList> FinishCallbacks; NeverFreedPointer<FinishCallbacksList> FinishCallbacks;
UnregisterObserverCallback UnregisterCallbacks[256]/* = { nullptr }*/; UnregisterObserverCallback UnregisterCallbacks[256]/* = { nullptr }*/;
ObservedEvent LastRegisteredEvent/* = 0*/;
} // namespace } // namespace
void startObservers() { void startObservers() {
@ -50,17 +52,18 @@ void finishObservers() {
FinishCallbacks.clear(); FinishCallbacks.clear();
} }
ObservedEventRegistrator::ObservedEventRegistrator(ObservedEvent event ObservedEventRegistrator::ObservedEventRegistrator(StartObservedEventCallback startCallback
, StartObservedEventCallback startCallback
, FinishObservedEventCallback finishCallback , FinishObservedEventCallback finishCallback
, UnregisterObserverCallback unregisterCallback) { , UnregisterObserverCallback unregisterCallback) {
_event = LastRegisteredEvent++;
StartCallbacks.makeIfNull(); StartCallbacks.makeIfNull();
StartCallbacks->push_back(startCallback); StartCallbacks->push_back(startCallback);
FinishCallbacks.makeIfNull(); FinishCallbacks.makeIfNull();
FinishCallbacks->push_back(finishCallback); FinishCallbacks->push_back(finishCallback);
UnregisterCallbacks[event] = unregisterCallback; UnregisterCallbacks[_event] = unregisterCallback;
} }
// Observer base interface. // Observer base interface.

View File

@ -42,10 +42,16 @@ using FinishObservedEventCallback = void(*)();
// unregisterCallback will be used to destroy connections. // unregisterCallback will be used to destroy connections.
class ObservedEventRegistrator { class ObservedEventRegistrator {
public: public:
ObservedEventRegistrator(ObservedEvent event ObservedEventRegistrator(StartObservedEventCallback startCallback,
, StartObservedEventCallback startCallback FinishObservedEventCallback finishCallback,
, FinishObservedEventCallback finishCallback UnregisterObserverCallback unregisterCallback);
, UnregisterObserverCallback unregisterCallback);
inline ObservedEvent event() const {
return _event;
}
private:
ObservedEvent _event;
}; };
@ -88,8 +94,12 @@ struct ObserversList {
QVector<int> freeIndices; QVector<int> freeIndices;
}; };
// If no filtering by flags is done, you can use this value in both
// Notify::registerObserver() and Notify::notifyObservers()
constexpr int UniversalFlag = 0x01;
template <typename Flags, typename Handler> template <typename Flags, typename Handler>
int registerObserver(ObserversList<Flags, Handler> &list, Flags flags, Handler &&handler) { ConnectionId registerObserver(ObservedEvent event, ObserversList<Flags, Handler> &list, Flags flags, Handler &&handler) {
while (!list.freeIndices.isEmpty()) { while (!list.freeIndices.isEmpty()) {
auto freeIndex = list.freeIndices.back(); auto freeIndex = list.freeIndices.back();
list.freeIndices.pop_back(); list.freeIndices.pop_back();
@ -100,7 +110,8 @@ int registerObserver(ObserversList<Flags, Handler> &list, Flags flags, Handler &
} }
} }
list.entries.push_back({ flags, std_::move(handler) }); list.entries.push_back({ flags, std_::move(handler) });
return list.entries.size() - 1; int connectionIndex = list.entries.size() - 1;
return (static_cast<uint32>(event) << 24) | static_cast<uint32>(connectionIndex + 1);
} }
template <typename Flags, typename Handler> template <typename Flags, typename Handler>
@ -131,17 +142,26 @@ namespace internal {
template <typename ObserverType, int> template <typename ObserverType, int>
struct ObserverRegisteredGeneric { struct ObserverRegisteredGeneric {
static void call(ObserverType *observer, ConnectionId connection) { static inline void call(ObserverType *observer, ConnectionId connection) {
observer->observerRegistered(connection); observer->observerRegistered(connection);
} }
}; };
template<typename ObserverType> template <typename ObserverType>
struct ObserverRegisteredGeneric<ObserverType, true> { struct ObserverRegisteredGeneric<ObserverType, true> {
static void call(ObserverType *observer, ConnectionId connection) { static inline void call(ObserverType *observer, ConnectionId connection) {
observerRegisteredDefault(observer, connection); observerRegisteredDefault(observer, connection);
} }
}; };
} // namespace internal } // 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 Notify

View File

@ -519,6 +519,7 @@ struct Data {
uint64 LaunchId = 0; uint64 LaunchId = 0;
SingleDelayedCall HandleHistoryUpdate = { App::app(), "call_handleHistoryUpdate" }; SingleDelayedCall HandleHistoryUpdate = { App::app(), "call_handleHistoryUpdate" };
SingleDelayedCall HandleUnreadCounterUpdate = { App::app(), "call_handleUnreadCounterUpdate" }; SingleDelayedCall HandleUnreadCounterUpdate = { App::app(), "call_handleUnreadCounterUpdate" };
SingleDelayedCall HandleFileDialogQueue = { App::app(), "call_handleFileDialogQueue" };
Adaptive::Layout AdaptiveLayout = Adaptive::NormalLayout; Adaptive::Layout AdaptiveLayout = Adaptive::NormalLayout;
bool AdaptiveForWide = true; bool AdaptiveForWide = true;
@ -582,6 +583,7 @@ void finish() {
DefineReadOnlyVar(Global, uint64, LaunchId); DefineReadOnlyVar(Global, uint64, LaunchId);
DefineRefVar(Global, SingleDelayedCall, HandleHistoryUpdate); DefineRefVar(Global, SingleDelayedCall, HandleHistoryUpdate);
DefineRefVar(Global, SingleDelayedCall, HandleUnreadCounterUpdate); DefineRefVar(Global, SingleDelayedCall, HandleUnreadCounterUpdate);
DefineRefVar(Global, SingleDelayedCall, HandleFileDialogQueue);
DefineVar(Global, Adaptive::Layout, AdaptiveLayout); DefineVar(Global, Adaptive::Layout, AdaptiveLayout);
DefineVar(Global, bool, AdaptiveForWide); DefineVar(Global, bool, AdaptiveForWide);

View File

@ -204,6 +204,7 @@ void finish();
DeclareReadOnlyVar(uint64, LaunchId); DeclareReadOnlyVar(uint64, LaunchId);
DeclareRefVar(SingleDelayedCall, HandleHistoryUpdate); DeclareRefVar(SingleDelayedCall, HandleHistoryUpdate);
DeclareRefVar(SingleDelayedCall, HandleUnreadCounterUpdate); DeclareRefVar(SingleDelayedCall, HandleUnreadCounterUpdate);
DeclareRefVar(SingleDelayedCall, HandleFileDialogQueue);
DeclareVar(Adaptive::Layout, AdaptiveLayout); DeclareVar(Adaptive::Layout, AdaptiveLayout);
DeclareVar(bool, AdaptiveForWide); DeclareVar(bool, AdaptiveForWide);

View File

@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mtproto/core_types.h" #include "mtproto/core_types.h"
#include "mtproto/session.h" #include "mtproto/session.h"
#include "mtproto/file_download.h"
namespace MTP { namespace MTP {

View File

@ -208,6 +208,7 @@ void FileLoader::localLoaded(const StorageImageSaved &result, const QByteArray &
} }
emit App::wnd()->imageLoaded(); emit App::wnd()->imageLoaded();
emit progress(this); emit progress(this);
FileDownload::internal::notifyImageLoaded();
loadNext(); loadNext();
} }
@ -549,6 +550,9 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
if (DebugLogging::FileLoader() && _id) DEBUG_LOG(("FileLoader(%1): not done yet, _lastComplete=%2, _size=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(Logs::b(_lastComplete)).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_requests))); if (DebugLogging::FileLoader() && _id) DEBUG_LOG(("FileLoader(%1): not done yet, _lastComplete=%2, _size=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(Logs::b(_lastComplete)).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_requests)));
} }
emit progress(this); emit progress(this);
if (_complete) {
FileDownload::internal::notifyImageLoaded();
}
loadNext(); loadNext();
} }
@ -678,6 +682,7 @@ void webFileLoader::onFinished(const QByteArray &data) {
Local::writeWebFile(_url, _data); Local::writeWebFile(_url, _data);
} }
emit progress(this); emit progress(this);
FileDownload::internal::notifyImageLoaded();
loadNext(); loadNext();
} }
@ -1089,3 +1094,45 @@ namespace MTP {
++GlobalPriority; ++GlobalPriority;
} }
} }
namespace FileDownload {
namespace {
using internal::ImageLoadedHandler;
using ImageLoadedObserversList = Notify::ObserversList<int, ImageLoadedHandler>;
NeverFreedPointer<ImageLoadedObserversList> ImageLoadedObservers;
void StartCallback() {
ImageLoadedObservers.makeIfNull();
}
void FinishCallback() {
ImageLoadedObservers.clear();
}
void UnregisterCallback(int connectionIndex) {
t_assert(!ImageLoadedObservers.isNull());
Notify::unregisterObserver(*ImageLoadedObservers, connectionIndex);
}
Notify::ObservedEventRegistrator creator(StartCallback, FinishCallback, UnregisterCallback);
bool Started() {
return !ImageLoadedObservers.isNull();
}
} // namespace
namespace internal {
Notify::ConnectionId plainRegisterImageLoadedObserver(ImageLoadedHandler &&handler) {
t_assert(Started());
auto connectionId = Notify::registerObserver(creator.event(), *ImageLoadedObservers
, Notify::UniversalFlag, std_::forward<ImageLoadedHandler>(handler));
return connectionId;
}
void notifyImageLoaded() {
Notify::notifyObservers(*ImageLoadedObservers, Notify::UniversalFlag);
}
} // namespace internal
}

View File

@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "core/observer.h"
namespace MTP { namespace MTP {
void clearLoaderPriorities(); void clearLoaderPriorities();
} }
@ -391,3 +393,21 @@ static WebLoadManager * const FinishedWebLoadManager = SharedMemoryLocation<WebL
void reinitWebLoadManager(); void reinitWebLoadManager();
void stopWebLoadManager(); 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);
}
} // namespace FileDownload

View File

@ -32,7 +32,6 @@ namespace Notify {
namespace internal { namespace internal {
namespace { namespace {
constexpr ObservedEvent PeerUpdateEvent = 0x01;
using PeerObserversList = ObserversList<PeerUpdateFlags, PeerUpdateHandler>; using PeerObserversList = ObserversList<PeerUpdateFlags, PeerUpdateHandler>;
NeverFreedPointer<PeerObserversList> PeerUpdateObservers; NeverFreedPointer<PeerObserversList> PeerUpdateObservers;
@ -55,7 +54,7 @@ void UnregisterCallback(int connectionIndex) {
t_assert(!PeerUpdateObservers.isNull()); t_assert(!PeerUpdateObservers.isNull());
unregisterObserver(*PeerUpdateObservers, connectionIndex); unregisterObserver(*PeerUpdateObservers, connectionIndex);
} }
ObservedEventRegistrator creator(PeerUpdateEvent, StartCallback, FinishCallback, UnregisterCallback); ObservedEventRegistrator creator(StartCallback, FinishCallback, UnregisterCallback);
bool Started() { bool Started() {
return !PeerUpdateObservers.isNull(); return !PeerUpdateObservers.isNull();
@ -65,9 +64,9 @@ bool Started() {
ConnectionId plainRegisterPeerObserver(PeerUpdateFlags events, PeerUpdateHandler &&handler) { ConnectionId plainRegisterPeerObserver(PeerUpdateFlags events, PeerUpdateHandler &&handler) {
t_assert(Started()); t_assert(Started());
auto connectionId = registerObserver(*PeerUpdateObservers, events, std_::forward<PeerUpdateHandler>(handler)); auto connectionId = registerObserver(creator.event(), *PeerUpdateObservers
t_assert(connectionId >= 0 && connectionId < 0x01000000); , events, std_::forward<PeerUpdateHandler>(handler));
return (static_cast<uint32>(PeerUpdateEvent) << 24) | static_cast<uint32>(connectionId + 1); return connectionId;
} }
void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) { void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) {
@ -116,10 +115,8 @@ void peerUpdatedSendDelayed() {
if (internal::SmallUpdates->isEmpty()) return; if (internal::SmallUpdates->isEmpty()) return;
internal::SmallUpdatesList smallList; auto smallList = createAndSwap(*internal::SmallUpdates);
internal::AllUpdatesList allList; auto allList = createAndSwap(*internal::AllUpdates);
std::swap(smallList, *internal::SmallUpdates);
std::swap(allList, *internal::AllUpdates);
for_const (auto &update, smallList) { for_const (auto &update, smallList) {
notifyObservers(*internal::PeerUpdateObservers, update.flags, update); notifyObservers(*internal::PeerUpdateObservers, update.flags, update);
} }

View File

@ -30,16 +30,17 @@ namespace Notify {
// 0xFFFF0000U for specific peer updates (valid for user / chat / channel). // 0xFFFF0000U for specific peer updates (valid for user / chat / channel).
enum class PeerUpdateFlag { enum class PeerUpdateFlag {
NameChanged = 0x00000001U, NameChanged = 0x00000001U,
UsernameChanged = 0x00000002U, UsernameChanged = 0x00000002U,
PhotoChanged = 0x00000004U,
UserCanShareContact = 0x00010000U, UserCanShareContact = 0x00010000U,
ChatCanEdit = 0x00010000U, ChatCanEdit = 0x00010000U,
ChannelAmIn = 0x00010000U, ChannelAmIn = 0x00010000U,
MegagroupCanEditPhoto = 0x00020000U, ChannelCanEditPhoto = 0x00020000U,
MegagroupCanAddMembers = 0x00040000U, ChannelCanAddMembers = 0x00040000U,
}; };
Q_DECLARE_FLAGS(PeerUpdateFlags, PeerUpdateFlag); Q_DECLARE_FLAGS(PeerUpdateFlags, PeerUpdateFlag);
Q_DECLARE_OPERATORS_FOR_FLAGS(PeerUpdateFlags); Q_DECLARE_OPERATORS_FOR_FLAGS(PeerUpdateFlags);
@ -68,11 +69,7 @@ ConnectionId plainRegisterPeerObserver(PeerUpdateFlags events, PeerUpdateHandler
template <typename ObserverType> template <typename ObserverType>
void registerPeerObserver(PeerUpdateFlags events, ObserverType *observer, void (ObserverType::*handler)(const PeerUpdate &)) { void registerPeerObserver(PeerUpdateFlags events, ObserverType *observer, void (ObserverType::*handler)(const PeerUpdate &)) {
auto connection = internal::plainRegisterPeerObserver(events, func(observer, handler)); auto connection = internal::plainRegisterPeerObserver(events, func(observer, handler));
observerRegistered(observer, 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 Notify

View File

@ -23,8 +23,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_profile.h" #include "styles/style_profile.h"
#include "ui/buttons/round_button.h" #include "ui/buttons/round_button.h"
#include "ui/filedialog.h"
#include "observer_peer.h" #include "observer_peer.h"
#include "boxes/confirmbox.h"
#include "boxes/contactsbox.h" #include "boxes/contactsbox.h"
#include "boxes/photocropbox.h"
#include "lang.h" #include "lang.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "mainwidget.h" #include "mainwidget.h"
@ -62,31 +65,61 @@ private:
const Notify::PeerUpdateFlags ButtonsUpdateFlags = Notify::PeerUpdateFlag::UserCanShareContact const Notify::PeerUpdateFlags ButtonsUpdateFlags = Notify::PeerUpdateFlag::UserCanShareContact
| Notify::PeerUpdateFlag::ChatCanEdit | Notify::PeerUpdateFlag::ChatCanEdit
| Notify::PeerUpdateFlag::MegagroupCanEditPhoto | Notify::PeerUpdateFlag::ChannelCanEditPhoto
| Notify::PeerUpdateFlag::MegagroupCanAddMembers | Notify::PeerUpdateFlag::ChannelCanAddMembers
| Notify::PeerUpdateFlag::ChannelAmIn; | Notify::PeerUpdateFlag::ChannelAmIn;
} // namespace } // namespace
class PhotoButton final : public Button { class PhotoButton final : public Button, public Notify::Observer {
public: public:
PhotoButton(QWidget *parent, PeerData *peer) : Button(parent), _peer(peer) { PhotoButton(QWidget *parent, PeerData *peer) : Button(parent), _peer(peer) {
resize(st::profilePhotoSize, st::profilePhotoSize); resize(st::profilePhotoSize, st::profilePhotoSize);
}
void photoUpdated() { processNewPeerPhoto();
bool hasPhoto = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId);
setCursor(hasPhoto ? style::cur_pointer : style::cur_default); Notify::registerPeerObserver(Notify::PeerUpdateFlag::PhotoChanged, this, &PhotoButton::notifyPeerUpdated);
FileDownload::registerImageLoadedObserver(this, &PhotoButton::notifyImageLoaded);
} }
protected: protected:
void paintEvent(QPaintEvent *e) { void paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
p.drawPixmap(0, 0, _userpic);
_peer->paintUserpic(p, st::profilePhotoSize, 0, 0);
} }
private: private:
void notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.peer != _peer) {
return;
}
processNewPeerPhoto();
this->update();
}
void notifyImageLoaded() {
if (_waiting && _peer->userpicLoaded()) {
_waiting = false;
_userpic = _peer->genUserpic(st::profilePhotoSize);
update();
}
}
void processNewPeerPhoto() {
bool hasPhoto = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId);
setCursor(hasPhoto ? style::cur_pointer : style::cur_default);
_waiting = !_peer->userpicLoaded();
if (_waiting) {
_peer->loadUserpic(true);
} else {
_userpic = _peer->genUserpic(st::profilePhotoSize);
}
}
PeerData *_peer; PeerData *_peer;
bool _waiting = false;
QPixmap _userpic;
}; };
@ -101,8 +134,8 @@ CoverWidget::CoverWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
auto observeEvents = ButtonsUpdateFlags | Notify::PeerUpdateFlag::NameChanged; auto observeEvents = ButtonsUpdateFlags | Notify::PeerUpdateFlag::NameChanged;
Notify::registerPeerObserver(observeEvents, this, &CoverWidget::notifyPeerUpdated); Notify::registerPeerObserver(observeEvents, this, &CoverWidget::notifyPeerUpdated);
FileDialog::registerObserver(this, &CoverWidget::notifyFileQueryUpdated);
_photoButton->photoUpdated();
connect(_photoButton, SIGNAL(clicked()), this, SLOT(onPhotoShow())); connect(_photoButton, SIGNAL(clicked()), this, SLOT(onPhotoShow()));
refreshNameText(); refreshNameText();
@ -132,12 +165,9 @@ void CoverWidget::resizeToWidth(int newWidth) {
_statusPosition = QPoint(infoLeft + st::profileStatusLeft, _photoButton->y() + st::profileStatusTop); _statusPosition = QPoint(infoLeft + st::profileStatusLeft, _photoButton->y() + st::profileStatusTop);
int buttonLeft = st::profilePhotoLeft + _photoButton->width() + st::profileButtonLeft; int buttonLeft = st::profilePhotoLeft + _photoButton->width() + st::profileButtonLeft;
if (_primaryButton) { for_const (auto button, _buttons) {
_primaryButton->moveToLeft(buttonLeft, st::profileButtonTop); button->moveToLeft(buttonLeft, st::profileButtonTop);
buttonLeft += _primaryButton->width() + st::profileButtonSkip; buttonLeft += button->width() + st::profileButtonSkip;
}
if (_secondaryButton) {
_secondaryButton->moveToLeft(buttonLeft, st::profileButtonTop);
} }
newHeight += st::profilePhotoSize; newHeight += st::profilePhotoSize;
@ -178,13 +208,14 @@ void CoverWidget::paintDivider(Painter &p) {
} }
void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) { void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.peer == _peer) { if (update.peer != _peer) {
if ((update.flags & ButtonsUpdateFlags) != 0) { return;
refreshButtons(); }
} if ((update.flags & ButtonsUpdateFlags) != 0) {
if (update.flags & Notify::PeerUpdateFlag::NameChanged) { refreshButtons();
refreshNameText(); }
} if (update.flags & Notify::PeerUpdateFlag::NameChanged) {
refreshNameText();
} }
} }
@ -247,6 +278,7 @@ bool CoverWidget::isUsingMegagroupOnlineCount() const {
} }
void CoverWidget::refreshButtons() { void CoverWidget::refreshButtons() {
clearButtons();
if (_peerUser) { if (_peerUser) {
setUserButtons(); setUserButtons();
} else if (_peerChat) { } else if (_peerChat) {
@ -260,65 +292,51 @@ void CoverWidget::refreshButtons() {
} }
void CoverWidget::setUserButtons() { void CoverWidget::setUserButtons() {
setPrimaryButton(lang(lng_profile_send_message), SLOT(onSendMessage())); addButton(lang(lng_profile_send_message), SLOT(onSendMessage()));
if (_peerUser->canShareThisContact()) { if (_peerUser->canShareThisContact()) {
setSecondaryButton(lang(lng_profile_share_contact), SLOT(onShareContact())); addButton(lang(lng_profile_share_contact), SLOT(onShareContact()));
} else {
clearSecondaryButton();
} }
} }
void CoverWidget::setChatButtons() { void CoverWidget::setChatButtons() {
if (_peerChat->canEdit()) { if (_peerChat->canEdit()) {
setPrimaryButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto())); addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
setSecondaryButton(lang(lng_profile_add_participant), SLOT(onAddMember())); addButton(lang(lng_profile_add_participant), SLOT(onAddMember()));
} else {
clearPrimaryButton();
clearSecondaryButton();
} }
} }
void CoverWidget::setMegagroupButtons() { void CoverWidget::setMegagroupButtons() {
if (_peerMegagroup->canEditPhoto()) { if (_peerMegagroup->canEditPhoto()) {
setPrimaryButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto())); addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
} else {
clearPrimaryButton();
} }
if (_peerMegagroup->canAddParticipants()) { if (_peerMegagroup->canAddParticipants()) {
setSecondaryButton(lang(lng_profile_add_participant), SLOT(onAddMember())); addButton(lang(lng_profile_add_participant), SLOT(onAddMember()));
} else {
clearSecondaryButton();
} }
} }
void CoverWidget::setChannelButtons() { void CoverWidget::setChannelButtons() {
if (_peerChannel->amCreator()) { if (_peerChannel->amCreator()) {
setPrimaryButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto())); addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
} else if (_peerChannel->amIn()) { } else if (_peerChannel->amIn()) {
setPrimaryButton(lang(lng_profile_view_channel), SLOT(onViewChannel())); addButton(lang(lng_profile_view_channel), SLOT(onViewChannel()));
} else { } else {
setPrimaryButton(lang(lng_profile_join_channel), SLOT(onJoin())); addButton(lang(lng_profile_join_channel), SLOT(onJoin()));
}
clearSecondaryButton();
}
void CoverWidget::setPrimaryButton(const QString &text, const char *slot) {
delete _primaryButton;
_primaryButton = nullptr;
if (!text.isEmpty()) {
_primaryButton = new Ui::RoundButton(this, text, st::profilePrimaryButton);
connect(_primaryButton, SIGNAL(clicked()), this, slot);
_primaryButton->show();
} }
} }
void CoverWidget::setSecondaryButton(const QString &text, const char *slot) { void CoverWidget::clearButtons() {
delete _secondaryButton; auto buttons = createAndSwap(_buttons);
_secondaryButton = nullptr; for_const (auto button, buttons) {
delete button;
}
}
void CoverWidget::addButton(const QString &text, const char *slot) {
if (!text.isEmpty()) { if (!text.isEmpty()) {
_secondaryButton = new Ui::RoundButton(this, text, st::profileSecondaryButton); auto &buttonStyle = _buttons.isEmpty() ? st::profilePrimaryButton : st::profileSecondaryButton;
connect(_secondaryButton, SIGNAL(clicked()), this, slot); _buttons.push_back(new Ui::RoundButton(this, text, buttonStyle));
_secondaryButton->show(); connect(_buttons.back(), SIGNAL(clicked()), this, slot);
_buttons.back()->show();
} }
} }
@ -331,7 +349,37 @@ void CoverWidget::onShareContact() {
} }
void CoverWidget::onSetPhoto() { void CoverWidget::onSetPhoto() {
QStringList imgExtensions(cImgExtensions());
QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;All files (*.*)"));
_setPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter);
}
void CoverWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update) {
if (_setPhotoFileQueryId != update.queryId) {
return;
}
_setPhotoFileQueryId = 0;
if (update.filePaths.isEmpty() && update.remoteContent.isEmpty()) {
return;
}
QImage img;
if (!update.remoteContent.isEmpty()) {
img = App::readImage(update.remoteContent);
} else {
img = App::readImage(update.filePaths.front());
}
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
Ui::showLayer(new InformBox(lang(lng_bad_photo)));
return;
}
auto box = new PhotoCropBox(img, _peer);
connect(box, SIGNAL(closed()), this, SLOT(onPhotoUpdateStart()));
Ui::showLayer(box);
} }
void CoverWidget::onAddMember() { void CoverWidget::onAddMember() {
@ -339,15 +387,17 @@ void CoverWidget::onAddMember() {
Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent));
} else if (_peerChannel && _peerChannel->mgInfo) { } else if (_peerChannel && _peerChannel->mgInfo) {
MembersAlreadyIn already; MembersAlreadyIn already;
for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) { for_const (auto user, _peerChannel->mgInfo->lastParticipants) {
already.insert(*i, true); already.insert(user);
} }
Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already)); Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already));
} }
} }
void CoverWidget::onJoin() { void CoverWidget::onJoin() {
if (!_peerChannel) return;
App::api()->joinChannel(_peerChannel);
} }
void CoverWidget::onViewChannel() { void CoverWidget::onViewChannel() {

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once #pragma once
#include "core/observer.h" #include "core/observer.h"
#include "ui/filedialog.h"
namespace Ui { namespace Ui {
class RoundButton; class RoundButton;
@ -60,6 +61,7 @@ protected:
private: private:
// Observed notifications. // Observed notifications.
void notifyPeerUpdated(const Notify::PeerUpdate &update); void notifyPeerUpdated(const Notify::PeerUpdate &update);
void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update);
void refreshNameText(); void refreshNameText();
void refreshStatusText(); void refreshStatusText();
@ -71,14 +73,8 @@ private:
void setMegagroupButtons(); void setMegagroupButtons();
void setChannelButtons(); void setChannelButtons();
void setPrimaryButton(const QString &text, const char *slot); void clearButtons();
void setSecondaryButton(const QString &text, const char *slot); void addButton(const QString &text, const char *slot);
void clearPrimaryButton() {
setPrimaryButton(QString(), nullptr);
}
void clearSecondaryButton() {
setSecondaryButton(QString(), nullptr);
}
void paintDivider(Painter &p); void paintDivider(Painter &p);
@ -97,10 +93,11 @@ private:
QPoint _statusPosition; QPoint _statusPosition;
QString _statusText; QString _statusText;
int _dividerTop; QList<Ui::RoundButton*> _buttons;
ChildWidget<Ui::RoundButton> _primaryButton = { nullptr }; int _dividerTop = 0;
ChildWidget<Ui::RoundButton> _secondaryButton = { nullptr };
FileDialog::QueryId _setPhotoFileQueryId = 0;
}; };

View File

@ -38,7 +38,7 @@ public:
} }
protected: protected:
void paintEvent(QPaintEvent *e) { void paintEvent(QPaintEvent *e) override {
Painter p(this); Painter p(this);
p.fillRect(e->rect(), st::profileBg); p.fillRect(e->rect(), st::profileBg);
@ -48,6 +48,11 @@ protected:
p.setPen(st::profileTopBarBackFg); p.setPen(st::profileTopBarBackFg);
p.drawTextLeft(st::profileTopBarBackPosition.x(), st::profileTopBarBackPosition.y(), width(), lang(lng_menu_back)); p.drawTextLeft(st::profileTopBarBackPosition.x(), st::profileTopBarBackPosition.y(), width(), lang(lng_menu_back));
} }
void onStateChanged(int oldState, ButtonStateChangeSource source) override {
if ((_state & Button::StateDown) && !(oldState & Button::StateDown)) {
emit clicked();
}
}
private: private:

View File

@ -45,7 +45,7 @@ void InnerWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) {
int notDisplayedAtBottom = height() - _visibleBottom; int notDisplayedAtBottom = height() - _visibleBottom;
if (notDisplayedAtBottom > 0) { if (notDisplayedAtBottom > 0) {
// decreaseAdditionalHeight(notDisplayedAtBottom); decreaseAdditionalHeight(notDisplayedAtBottom);
} }
//loadProfilePhotos(_visibleTop); //loadProfilePhotos(_visibleTop);
@ -66,8 +66,10 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
p.fillRect(e->rect(), st::profileBg); p.fillRect(e->rect(), st::profileBg);
} }
void InnerWidget::mousePressEvent(QMouseEvent *e) { // TEMP for testing void InnerWidget::keyPressEvent(QKeyEvent *e) {
Ui::showPeerOverview(_peer, OverviewPhotos); if (e->key() == Qt::Key_Escape) {
emit cancelled();
}
} }
int InnerWidget::resizeGetHeight(int newWidth) { int InnerWidget::resizeGetHeight(int newWidth) {

View File

@ -41,9 +41,12 @@ public:
// Updates the area that is visible inside the scroll container. // Updates the area that is visible inside the scroll container.
void setVisibleTopBottom(int visibleTop, int visibleBottom); void setVisibleTopBottom(int visibleTop, int visibleBottom);
signals:
void cancelled();
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override; // TEMP for testing void keyPressEvent(QKeyEvent *e) override;
private: private:
// Resizes content and counts natural widget height for the desired width. // Resizes content and counts natural widget height for the desired width.

View File

@ -48,6 +48,7 @@ Widget::Widget(QWidget *parent, PeerData *peer) : Window::SectionWidget(parent)
connect(_scroll, SIGNAL(scrolled()), _inner, SLOT(updateSelected())); connect(_scroll, SIGNAL(scrolled()), _inner, SLOT(updateSelected()));
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(_inner, SIGNAL(cancelled()), _fixedBar, SLOT(onBack()));
} }
void Widget::updateAdaptiveLayout() { void Widget::updateAdaptiveLayout() {

View File

@ -396,8 +396,8 @@ void ProfileInner::onAddParticipant() {
Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent));
} else if (_peerChannel && _peerChannel->mgInfo) { } else if (_peerChannel && _peerChannel->mgInfo) {
MembersAlreadyIn already; MembersAlreadyIn already;
for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) { for_const (auto user, _peerChannel->mgInfo->lastParticipants) {
already.insert(*i, true); already.insert(user);
} }
Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already)); Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already));
} }

View File

@ -229,6 +229,10 @@ void UserData::setPhoto(const MTPUserProfilePhoto &p) { // see Local::readPeer a
if (App::main()) { if (App::main()) {
emit App::main()->peerPhotoChanged(this); emit App::main()->peerPhotoChanged(this);
} }
Notify::PeerUpdate update(this);
update.flags = Notify::PeerUpdateFlag::PhotoChanged;
Notify::peerUpdatedDelayed(update);
} }
} }
@ -401,6 +405,10 @@ void ChatData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { // see Loc
setUserpic(newPhoto); setUserpic(newPhoto);
photoLoc = newPhotoLoc; photoLoc = newPhotoLoc;
emit App::main()->peerPhotoChanged(this); emit App::main()->peerPhotoChanged(this);
Notify::PeerUpdate update(this);
update.flags = Notify::PeerUpdateFlag::PhotoChanged;
Notify::peerUpdatedDelayed(update);
} }
} }
@ -439,6 +447,10 @@ void ChannelData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { // see
setUserpic(newPhoto); setUserpic(newPhoto);
photoLoc = newPhotoLoc; photoLoc = newPhotoLoc;
if (App::main()) emit App::main()->peerPhotoChanged(this); if (App::main()) emit App::main()->peerPhotoChanged(this);
Notify::PeerUpdate update(this);
update.flags = Notify::PeerUpdateFlag::PhotoChanged;
Notify::peerUpdatedDelayed(update);
} }
} }

View File

@ -309,6 +309,9 @@ public:
void loadUserpic(bool loadFirst = false, bool prior = true) { void loadUserpic(bool loadFirst = false, bool prior = true) {
_userpic->load(loadFirst, prior); _userpic->load(loadFirst, prior);
} }
bool userpicLoaded() const {
return _userpic->loaded();
}
StorageKey userpicUniqueKey() const; StorageKey userpicUniqueKey() const;
void saveUserpic(const QString &path) const; void saveUserpic(const QString &path) const;
QPixmap genUserpic(int size) const; QPixmap genUserpic(int size) const;

View File

@ -234,3 +234,150 @@ QString filedialogNextFilename(const QString &name, const QString &cur, const QS
} }
return result; return result;
} }
namespace FileDialog {
namespace {
using internal::QueryUpdateHandler;
using QueryObserversList = Notify::ObserversList<int, QueryUpdateHandler>;
NeverFreedPointer<QueryObserversList> QueryUpdateObservers;
struct Query {
enum class Type {
ReadFile,
ReadFiles,
WriteFile,
ReadFolder,
};
Query(Type type
, const QString &caption = QString()
, const QString &filter = QString()
, const QString &filePath = QString()) : id(rand_value<QueryId>())
, type(type)
, caption(caption)
, filter(filter)
, filePath(filePath) {
}
QueryId id;
Type type;
QString caption, filter, filePath;
};
using QueryList = QList<Query>;
NeverFreedPointer<QueryList> Queries;
void StartCallback() {
QueryUpdateObservers.makeIfNull();
Queries.makeIfNull();
}
void FinishCallback() {
QueryUpdateObservers.clear();
Queries.clear();
}
void UnregisterCallback(int connectionIndex) {
t_assert(!QueryUpdateObservers.isNull());
Notify::unregisterObserver(*QueryUpdateObservers, connectionIndex);
}
Notify::ObservedEventRegistrator creator(StartCallback, FinishCallback, UnregisterCallback);
bool Started() {
return !QueryUpdateObservers.isNull();
}
} // namespace
QueryId queryReadFile(const QString &caption, const QString &filter) {
t_assert(Started());
Queries->push_back(Query(Query::Type::ReadFile, caption, filter));
Global::RefHandleFileDialogQueue().call();
return Queries->back().id;
}
QueryId queryReadFiles(const QString &caption, const QString &filter) {
t_assert(Started());
Queries->push_back(Query(Query::Type::ReadFiles, caption, filter));
Global::RefHandleFileDialogQueue().call();
return Queries->back().id;
}
QueryId queryWriteFile(const QString &caption, const QString &filter, const QString &filePath) {
t_assert(Started());
Queries->push_back(Query(Query::Type::WriteFile, caption, filter, filePath));
Global::RefHandleFileDialogQueue().call();
return Queries->back().id;
}
QueryId queryReadFolder(const QString &caption) {
t_assert(Started());
Queries->push_back(Query(Query::Type::ReadFolder, caption));
Global::RefHandleFileDialogQueue().call();
return Queries->back().id;
}
bool processQuery() {
if (!Started() || !Global::started() || Queries->isEmpty()) return false;
auto query = Queries->front();
Queries->pop_front();
QueryUpdate update(query.id);
switch (query.type) {
case Query::Type::ReadFile: {
QString file;
QByteArray remoteContent;
if (filedialogGetOpenFile(file, remoteContent, query.caption, query.filter)) {
if (!file.isEmpty()) {
update.filePaths.push_back(file);
}
update.remoteContent = remoteContent;
}
} break;
case Query::Type::ReadFiles: {
QStringList files;
QByteArray remoteContent;
if (filedialogGetOpenFiles(files, remoteContent, query.caption, query.filter)) {
update.filePaths = files;
update.remoteContent = remoteContent;
}
} break;
case Query::Type::WriteFile: {
QString file;
if (filedialogGetSaveFile(file, query.caption, query.filter, query.filePath)) {
if (!file.isEmpty()) {
update.filePaths.push_back(file);
}
}
} break;
case Query::Type::ReadFolder: {
QString folder;
if (filedialogGetDir(folder, query.caption)) {
if (!folder.isEmpty()) {
update.filePaths.push_back(folder);
}
}
} break;
}
// No one know what happened during filedialogGet*() call in the event loop.
if (!Started() || !Global::started()) return false;
Notify::notifyObservers(*QueryUpdateObservers, Notify::UniversalFlag, update);
return true;
}
namespace internal {
Notify::ConnectionId plainRegisterObserver(QueryUpdateHandler &&handler) {
t_assert(Started());
auto connectionId = Notify::registerObserver(creator.event(), *QueryUpdateObservers
, Notify::UniversalFlag, std_::forward<QueryUpdateHandler>(handler));
return connectionId;
}
} // namespace internal
} // namespace FileDialog

View File

@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "core/observer.h"
void filedialogInit(); void filedialogInit();
bool filedialogGetOpenFiles(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter); bool filedialogGetOpenFiles(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter);
bool filedialogGetOpenFile(QString &file, QByteArray &remoteContent, const QString &caption, const QString &filter); bool filedialogGetOpenFile(QString &file, QByteArray &remoteContent, const QString &caption, const QString &filter);
@ -28,3 +30,38 @@ bool filedialogGetDir(QString &dir, const QString &caption);
QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path = QString(), bool skipExistance = false); QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path = QString(), bool skipExistance = false);
QString filedialogNextFilename(const QString &name, const QString &cur, const QString &path = QString()); QString filedialogNextFilename(const QString &name, const QString &cur, const QString &path = QString());
namespace FileDialog {
using QueryId = uint64;
struct QueryUpdate {
QueryUpdate(QueryId id) : queryId(id) {
}
QueryId queryId;
QStringList filePaths;
QByteArray remoteContent;
};
QueryId queryReadFile(const QString &caption, const QString &filter);
QueryId queryReadFiles(const QString &caption, const QString &filter);
QueryId queryWriteFile(const QString &caption, const QString &filter, const QString &filePath);
QueryId queryReadFolder(const QString &caption);
// Returns false if no need to call it anymore right now.
// 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);
}
} // namespace FileDialog

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include <QtGui/QPixmap> #include "mtproto/file_download.h"
QImage imageBlur(QImage img); QImage imageBlur(QImage img);
void imageRound(QImage &img); void imageRound(QImage &img);