Replace ContactsBox with PeerListBox in two cases.

- View contacts list in PeerListBox.
- Add participants when creating group / channel in PeerListBox.
This commit is contained in:
John Preston 2017-08-14 15:53:49 +03:00
parent 17cef93ac0
commit f7359093b4
31 changed files with 825 additions and 393 deletions

View File

@ -65,7 +65,7 @@ enum class RegExOption {
DontAutomaticallyOptimize = QRegularExpression::DontAutomaticallyOptimizeOption,
#endif // OS_MAC_OLD
};
Q_DECLARE_FLAGS(RegExOptions, RegExOption);
using RegExOptions = QFlags<RegExOption>;
Q_DECLARE_OPERATORS_FOR_FLAGS(RegExOptions);
inline RegularExpressionMatch regex_match(const QString &string, const QString &subject, RegExOptions options = 0) {

View File

@ -25,9 +25,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "lang/lang_keys.h"
#include "messenger.h"
#include "mtproto/sender.h"
#include "boxes/contacts_box.h"
#include "base/flat_set.h"
#include "boxes/confirm_box.h"
#include "boxes/photo_crop_box.h"
#include "boxes/peer_list_controllers.h"
#include "core/file_utilities.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
@ -53,8 +54,203 @@ style::InputField CreateBioFieldStyle() {
return result;
}
base::flat_set<gsl::not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
if (!peer) {
return {};
}
if (auto chat = peer->asChat()) {
auto participants = chat->participants.keys();
return { participants.cbegin(), participants.cend() };
} else if (auto channel = peer->asChannel()) {
if (channel->isMegagroup()) {
auto &participants = channel->mgInfo->lastParticipants;
return { participants.cbegin(), participants.cend() };
}
}
return {};
}
class AddParticipantsBoxController : public ContactsBoxController {
public:
AddParticipantsBoxController(PeerData *peer);
AddParticipantsBoxController(
gsl::not_null<ChannelData*> channel,
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn);
using ContactsBoxController::ContactsBoxController;
void rowClicked(gsl::not_null<PeerListRow*> row) override;
void itemDeselectedHook(gsl::not_null<PeerData*> peer) override;
protected:
void prepareViewHook() override;
std::unique_ptr<PeerListRow> createRow(gsl::not_null<UserData*> user) override;
private:
int alreadyInCount() const;
bool isAlreadyIn(gsl::not_null<UserData*> user) const;
int fullCount() const;
void updateTitle();
PeerData *_peer = nullptr;
base::flat_set<gsl::not_null<UserData*>> _alreadyIn;
};
AddParticipantsBoxController::AddParticipantsBoxController(PeerData *peer)
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
, _peer(peer)
, _alreadyIn(GetAlreadyInFromPeer(peer)) {
}
AddParticipantsBoxController::AddParticipantsBoxController(
gsl::not_null<ChannelData*> channel,
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn)
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
, _peer(channel)
, _alreadyIn(std::move(alreadyIn)) {
}
void AddParticipantsBoxController::rowClicked(gsl::not_null<PeerListRow*> row) {
auto count = fullCount();
auto limit = (_peer && _peer->isMegagroup()) ? Global::MegagroupSizeMax() : Global::ChatSizeMax();
if (count < limit || row->checked()) {
delegate()->peerListSetRowChecked(row, !row->checked());
updateTitle();
} else if (auto channel = _peer ? _peer->asChannel() : nullptr) {
if (!_peer->isMegagroup()) {
Ui::show(Box<MaxInviteBox>(_peer->asChannel()), KeepOtherLayers);
}
} else if (count >= Global::ChatSizeMax() && count < Global::MegagroupSizeMax()) {
Ui::show(Box<InformBox>(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())), KeepOtherLayers);
}
}
void AddParticipantsBoxController::itemDeselectedHook(gsl::not_null<PeerData*> peer) {
updateTitle();
}
void AddParticipantsBoxController::prepareViewHook() {
updateTitle();
}
int AddParticipantsBoxController::alreadyInCount() const {
return _alreadyIn.empty() ? 1 : _alreadyIn.size(); // self
}
bool AddParticipantsBoxController::isAlreadyIn(gsl::not_null<UserData*> user) const {
if (!_peer) {
return false;
}
if (auto chat = _peer->asChat()) {
return chat->participants.contains(user);
} else if (auto channel = _peer->asChannel()) {
return _alreadyIn.contains(user)
|| (channel->isMegagroup() && channel->mgInfo->lastParticipants.contains(user));
}
Unexpected("User in AddParticipantsBoxController::isAlreadyIn");
}
int AddParticipantsBoxController::fullCount() const {
return alreadyInCount() + delegate()->peerListSelectedRowsCount();
}
std::unique_ptr<PeerListRow> AddParticipantsBoxController::createRow(gsl::not_null<UserData*> user) {
if (user->isSelf()) {
return nullptr;
}
auto result = std::make_unique<PeerListRow>(user);
if (isAlreadyIn(user)) {
result->setDisabledState(PeerListRow::State::DisabledChecked);
}
return result;
}
void AddParticipantsBoxController::updateTitle() {
auto additional = (_peer && _peer->isChannel() && !_peer->isMegagroup())
? QString() :
QString("%1 / %2").arg(fullCount()).arg(Global::MegagroupSizeMax());
delegate()->peerListSetTitle(langFactory(lng_profile_add_participant));
delegate()->peerListSetAdditionalTitle([additional] { return additional; });
}
} // namespace
QString PeerFloodErrorText(PeerFloodType type) {
auto link = textcmdLink(
Messenger::Instance().createInternalLinkFull(qsl("spambot")),
lang(lng_cant_more_info));
if (type == PeerFloodType::InviteGroup) {
return lng_cant_invite_not_contact(lt_more_info, link);
}
return lng_cant_send_to_not_contact(lt_more_info, link);
}
void ShowAddContactsToChatBox(gsl::not_null<ChatData*> chat) {
auto initBox = [chat](gsl::not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_participant_invite), [box, chat] {
auto rows = box->peerListCollectSelectedRows();
if (!rows.empty()) {
auto users = std::vector<gsl::not_null<UserData*>>();
for (auto peer : rows) {
auto user = peer->asUser();
t_assert(user != nullptr);
t_assert(!user->isSelf());
users.push_back(peer->asUser());
}
App::main()->addParticipants(chat, users);
Ui::showPeerHistory(chat, ShowAtTheEndMsgId);
}
});
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
};
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(chat), std::move(initBox)));
}
void ShowAddContactsToChannelBox(
gsl::not_null<ChannelData*> channel,
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn,
bool justCreated) {
auto initBox = [channel, justCreated](gsl::not_null<PeerListBox*> box) {
auto subscription = std::make_shared<base::Subscription>();
box->addButton(langFactory(lng_participant_invite), [box, channel, subscription] {
auto rows = box->peerListCollectSelectedRows();
if (!rows.empty()) {
auto users = std::vector<gsl::not_null<UserData*>>();
for (auto peer : rows) {
auto user = peer->asUser();
t_assert(user != nullptr);
t_assert(!user->isSelf());
users.push_back(peer->asUser());
}
App::main()->addParticipants(channel, users);
if (channel->isMegagroup()) {
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
} else {
box->closeBox();
}
}
});
box->addButton(langFactory(justCreated ? lng_create_group_skip : lng_cancel), [box] { box->closeBox(); });
if (justCreated) {
*subscription = box->boxClosing.add_subscription([channel] {
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
});
}
};
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(channel, std::move(alreadyIn)), std::move(initBox)));
}
void ShowAddContactsToChannelBox(
gsl::not_null<ChannelData*> channel,
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn) {
ShowAddContactsToChannelBox(channel, std::move(alreadyIn), false);
}
void ShowAddContactsToChannelBox(gsl::not_null<ChannelData*> channel) {
ShowAddContactsToChannelBox(channel, {}, true);
}
class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender {
public:
Inner(QWidget *parent, base::lambda<void()> revokeCallback);
@ -379,6 +575,68 @@ void GroupInfoBox::onNameSubmit() {
}
}
void GroupInfoBox::createGroup(gsl::not_null<PeerListBox*> selectUsersBox, const QString &title, const std::vector<gsl::not_null<PeerData*>> &users) {
if (_creationRequestId) return;
auto inputs = QVector<MTPInputUser>();
inputs.reserve(users.size());
for (auto peer : users) {
auto user = peer->asUser();
t_assert(user != nullptr);
if (!user->isSelf()) {
inputs.push_back(user->inputUser);
}
}
_creationRequestId = request(MTPmessages_CreateChat(MTP_vector<MTPInputUser>(inputs), MTP_string(title))).done([this](const MTPUpdates &result) {
Ui::hideLayer();
App::main()->sentUpdatesReceived(result);
auto success = base::make_optional(&result)
| [](auto updates) -> base::optional<const QVector<MTPChat>*> {
switch (updates->type()) {
case mtpc_updates:
return &updates->c_updates().vchats.v;
case mtpc_updatesCombined:
return &updates->c_updatesCombined().vchats.v;
}
LOG(("API Error: unexpected update cons %1 (GroupInfoBox::creationDone)").arg(updates->type()));
return base::none;
}
| [](auto chats) {
return (!chats->empty() && chats->front().type() == mtpc_chat)
? base::make_optional(chats)
: base::none;
}
| [](auto chats) {
return App::chat(chats->front().c_chat().vid.v);
}
| [this](gsl::not_null<ChatData*> chat) {
if (!_photoImage.isNull()) {
Messenger::Instance().uploadProfilePhoto(_photoImage, chat->id);
}
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
};
if (!success) {
LOG(("API Error: chat not found in updates (ContactsBox::creationDone)"));
}
}).fail([this, selectUsersBox](const RPCError &error) {
_creationRequestId = 0;
if (error.type() == qstr("NO_CHAT_TITLE")) {
auto guard = weak(this);
selectUsersBox->closeBox();
if (guard) {
_title->showError();
}
} else if (error.type() == qstr("USERS_TOO_FEW")) {
} else if (error.type() == qstr("PEER_FLOOD")) {
Ui::show(Box<InformBox>(PeerFloodErrorText(PeerFloodType::InviteGroup)), KeepOtherLayers);
} else if (error.type() == qstr("USER_RESTRICTED")) {
Ui::show(Box<InformBox>(lang(lng_cant_do_this)), KeepOtherLayers);
}
}).send();
}
void GroupInfoBox::onNext() {
if (_creationRequestId) return;
@ -389,64 +647,82 @@ void GroupInfoBox::onNext() {
_title->showError();
return;
}
if (_creating == CreatingGroupGroup) {
Ui::show(Box<ContactsBox>(title, _photoImage), KeepOtherLayers);
if (_creating != CreatingGroupGroup) {
createChannel(title, description);
} else {
bool mega = false;
auto flags = mega ? MTPchannels_CreateChannel::Flag::f_megagroup : MTPchannels_CreateChannel::Flag::f_broadcast;
_creationRequestId = MTP::send(MTPchannels_CreateChannel(MTP_flags(flags), MTP_string(title), MTP_string(description)), rpcDone(&GroupInfoBox::creationDone), rpcFail(&GroupInfoBox::creationFail));
auto initBox = [title, weak = weak(this)](gsl::not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_create_group_create), [box, title, weak] {
if (weak) {
auto rows = box->peerListCollectSelectedRows();
if (!rows.empty()) {
weak->createGroup(box, title, rows);
}
}
});
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
};
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(nullptr), std::move(initBox)), KeepOtherLayers);
}
}
void GroupInfoBox::creationDone(const MTPUpdates &updates) {
App::main()->sentUpdatesReceived(updates);
void GroupInfoBox::createChannel(const QString &title, const QString &description) {
bool mega = false;
auto flags = mega ? MTPchannels_CreateChannel::Flag::f_megagroup : MTPchannels_CreateChannel::Flag::f_broadcast;
_creationRequestId = request(MTPchannels_CreateChannel(MTP_flags(flags), MTP_string(title), MTP_string(description))).done([this](const MTPUpdates &result) {
App::main()->sentUpdatesReceived(result);
const QVector<MTPChat> *v = 0;
switch (updates.type()) {
case mtpc_updates: v = &updates.c_updates().vchats.v; break;
case mtpc_updatesCombined: v = &updates.c_updatesCombined().vchats.v; break;
default: LOG(("API Error: unexpected update cons %1 (GroupInfoBox::creationDone)").arg(updates.type())); break;
}
ChannelData *channel = 0;
if (v && !v->isEmpty() && v->front().type() == mtpc_channel) {
channel = App::channel(v->front().c_channel().vid.v);
if (channel) {
if (!_photoImage.isNull()) {
Messenger::Instance().uploadProfilePhoto(_photoImage, channel->id);
auto success = base::make_optional(&result)
| [](auto updates) -> base::optional<const QVector<MTPChat>*> {
switch (updates->type()) {
case mtpc_updates:
return &updates->c_updates().vchats.v;
case mtpc_updatesCombined:
return &updates->c_updatesCombined().vchats.v;
}
LOG(("API Error: unexpected update cons %1 (GroupInfoBox::createChannel)").arg(updates->type()));
return base::none;
}
_createdChannel = channel;
_creationRequestId = MTP::send(MTPchannels_ExportInvite(_createdChannel->inputChannel), rpcDone(&GroupInfoBox::exportDone));
return;
| [](auto chats) {
return (!chats->empty() && chats->front().type() == mtpc_channel)
? base::make_optional(chats)
: base::none;
}
| [](auto chats) {
return App::channel(chats->front().c_channel().vid.v);
}
| [this](gsl::not_null<ChannelData*> channel) {
if (!_photoImage.isNull()) {
Messenger::Instance().uploadProfilePhoto(
_photoImage,
channel->id);
}
_createdChannel = channel;
_creationRequestId = request(
MTPchannels_ExportInvite(_createdChannel->inputChannel)
).done([this](const MTPExportedChatInvite &result) {
_creationRequestId = 0;
if (result.type() == mtpc_chatInviteExported) {
auto link = qs(result.c_chatInviteExported().vlink);
_createdChannel->setInviteLink(link);
}
Ui::show(Box<SetupChannelBox>(_createdChannel));
}).send();
};
if (!success) {
LOG(("API Error: channel not found in updates (GroupInfoBox::creationDone)"));
closeBox();
}
} else {
LOG(("API Error: channel not found in updates (GroupInfoBox::creationDone)"));
}
closeBox();
}
bool GroupInfoBox::creationFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_creationRequestId = 0;
if (error.type() == "NO_CHAT_TITLE") {
_title->setFocus();
_title->showError();
return true;
} else if (error.type() == qstr("USER_RESTRICTED")) {
Ui::show(Box<InformBox>(lang(lng_cant_do_this)));
return true;
}
return false;
}
void GroupInfoBox::exportDone(const MTPExportedChatInvite &result) {
_creationRequestId = 0;
if (result.type() == mtpc_chatInviteExported) {
_createdChannel->setInviteLink(qs(result.c_chatInviteExported().vlink));
}
Ui::show(Box<SetupChannelBox>(_createdChannel));
}).fail([this](const RPCError &error) {
_creationRequestId = 0;
if (error.type() == "NO_CHAT_TITLE") {
_title->setFocus();
_title->showError();
} else if (error.type() == qstr("USER_RESTRICTED")) {
Ui::show(Box<InformBox>(lang(lng_cant_do_this)));
} else if (error.type() == qstr("CHANNELS_TOO_MUCH")) {
Ui::show(Box<InformBox>(lang(lng_cant_do_this))); // TODO
}
}).send();
}
void GroupInfoBox::onDescriptionResized() {
@ -503,17 +779,13 @@ void SetupChannelBox::prepare() {
}));
subscribe(boxClosing, [this] {
if (!_existing) {
showAddContactsToChannelBox();
ShowAddContactsToChannelBox(_channel);
}
});
updateMaxHeight();
}
void SetupChannelBox::showAddContactsToChannelBox() const {
Ui::show(Box<ContactsBox>(_channel));
}
void SetupChannelBox::setInnerFocus() {
if (_link->isHidden()) {
setFocus();

View File

@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "mtproto/sender.h"
class ConfirmBox;
class PeerListBox;
namespace Ui {
class FlatLabel;
@ -40,6 +41,18 @@ class LinkButton;
class NewAvatarButton;
} // namespace Ui
enum class PeerFloodType {
Send,
InviteGroup,
InviteChannel,
};
QString PeerFloodErrorText(PeerFloodType type);
void ShowAddContactsToChatBox(gsl::not_null<ChatData*> chat);
void ShowAddContactsToChannelBox(
gsl::not_null<ChannelData*> channel,
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn);
class AddContactBox : public BoxContent, public RPCSender {
Q_OBJECT
@ -83,7 +96,7 @@ private:
};
class GroupInfoBox : public BoxContent, public RPCSender {
class GroupInfoBox : public BoxContent, private MTP::Sender {
Q_OBJECT
public:
@ -107,10 +120,8 @@ private slots:
private:
void setupPhotoButton();
void creationDone(const MTPUpdates &updates);
bool creationFail(const RPCError &e);
void exportDone(const MTPExportedChatInvite &result);
void createChannel(const QString &title, const QString &description);
void createGroup(gsl::not_null<PeerListBox*> selectUsersBox, const QString &title, const std::vector<gsl::not_null<PeerData*>> &users);
void updateMaxHeight();
void updateSelected(const QPoint &cursorGlobalPosition);
@ -124,7 +135,7 @@ private:
QImage _photoImage;
// channel creation
// group / channel creation
mtpRequestId _creationRequestId = 0;
ChannelData *_createdChannel = nullptr;

View File

@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_profile.h"
#include "lang/lang_keys.h"
#include "boxes/add_contact_box.h"
#include "boxes/peer_list_box.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "messenger.h"
@ -74,14 +75,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
// return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
//}
QString PeerFloodErrorText(PeerFloodType type) {
auto link = textcmdLink(Messenger::Instance().createInternalLinkFull(qsl("spambot")), lang(lng_cant_more_info));
if (type == PeerFloodType::InviteGroup) {
return lng_cant_invite_not_contact(lt_more_info, link);
}
return lng_cant_send_to_not_contact(lt_more_info, link);
}
ContactsBox::ContactsBox(QWidget*, ChatData *chat, MembersFilter filter)
: _chat(chat)
, _membersFilter(filter)

View File

@ -61,19 +61,6 @@ template <typename Widget>
class WidgetSlideWrap;
} // namespace Ui
enum class PeerFloodType {
Send,
InviteGroup,
InviteChannel,
};
QString PeerFloodErrorText(PeerFloodType type);
inline Ui::RoundImageCheckbox::PaintRoundImage PaintUserpicCallback(PeerData *peer) {
return [peer](Painter &p, int x, int y, int outerWidth, int size) {
peer->paintUserpicLeft(p, x, y, outerWidth, size);
};
}
class ContactsBox : public BoxContent, public RPCSender {
Q_OBJECT

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/effects/widget_slide_wrap.h"
#include "boxes/peer_list_box.h"
#include "boxes/peer_list_controllers.h"
#include "apiwrap.h"
#include "auth_session.h"
#include "lang/lang_keys.h"
@ -178,7 +178,7 @@ void EditPrivacyBox::editExceptionUsers(Exception exception) {
auto controller = std::make_unique<PrivacyExceptionsBoxController>(base::lambda_guarded(this, [this, exception] {
return _controller->exceptionBoxTitle(exception);
}), exceptionUsers(exception));
auto initBox = [this, exception, controller = controller.get()](PeerListBox *box) {
auto initBox = [this, exception, controller = controller.get()](gsl::not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_settings_save), base::lambda_guarded(this, [this, box, exception, controller] {
exceptionUsers(exception) = controller->getResult();
exceptionLink(exception)->entity()->setText(exceptionLinkText(exception));

View File

@ -22,22 +22,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
#include "ui/widgets/scroll_area.h"
#include "ui/effects/ripple_animation.h"
#include "dialogs/dialogs_indexed_list.h"
#include "observer_peer.h"
#include "auth_session.h"
#include "mainwidget.h"
#include "storage/file_download.h"
#include "ui/widgets/multi_select.h"
#include "ui/widgets/labels.h"
#include "ui/effects/round_checkbox.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/widget_slide_wrap.h"
#include "lang/lang_keys.h"
#include "ui/effects/round_checkbox.h"
#include "boxes/contacts_box.h"
#include "storage/file_download.h"
#include "window/themes/window_theme.h"
PeerListBox::PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(PeerListBox*)> init)
PeerListBox::PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(gsl::not_null<PeerListBox*>)> init)
: _controller(std::move(controller))
, _init(std::move(init)) {
Expects(_controller != nullptr);
@ -221,6 +217,7 @@ void PeerListBox::peerListSetSearchMode(PeerListSearchMode mode) {
_inner->changeCheckState(row, false, PeerListRow::SetStyle::Animated);
update();
}
_controller->itemDeselectedHook(peer);
}
});
_select->resizeToWidth(st::boxWideWidth);
@ -319,8 +316,12 @@ bool PeerListBox::peerListIsRowSelected(gsl::not_null<PeerData*> peer) {
return _select->entity()->hasItem(peer->id);
}
std::vector<gsl::not_null<PeerData*>> PeerListBox::peerListCollectSelectedRows()
{
int PeerListBox::peerListSelectedRowsCount() {
Expects(_select != nullptr);
return _select->entity()->getItemsCount();
}
std::vector<gsl::not_null<PeerData*>> PeerListBox::peerListCollectSelectedRows() {
Expects(_select != nullptr);
auto result = std::vector<gsl::not_null<PeerData*>>();
auto items = _select->entity()->getItems();
@ -340,7 +341,6 @@ PeerListRow::PeerListRow(gsl::not_null<PeerData*> peer, PeerListRowId id)
: _id(id)
, _peer(peer)
, _initialized(false)
, _disabled(false)
, _isSearchResult(false) {
}
@ -416,12 +416,10 @@ void PeerListRow::paintRipple(Painter &p, TimeMs ms, int x, int y, int outerWidt
}
void PeerListRow::paintUserpic(Painter &p, TimeMs ms, int x, int y, int outerWidth) {
if (_checkbox) {
if (disabled() && checked()) {
paintDisabledCheckUserpic(p, x, y, outerWidth);
} else {
_checkbox->paint(p, ms, x, y, outerWidth);
}
if (_disabledState == State::DisabledChecked) {
paintDisabledCheckUserpic(p, x, y, outerWidth);
} else if (_checkbox) {
_checkbox->paint(p, ms, x, y, outerWidth);
} else {
peer()->paintUserpicLeft(p, x, y, outerWidth, st::contactsPhotoSize);
}
@ -898,7 +896,8 @@ void PeerListBox::Inner::paintRow(Painter &p, TimeMs ms, RowIndex index) {
namew -= icon->width();
icon->paint(p, namex + qMin(name.maxWidth(), namew), st::contactsPadding.top() + st::contactsNameTop, width());
}
p.setPen(anim::pen(st::contactsNameFg, st::contactsNameCheckedFg, row->checkedRatio()));
auto nameCheckedRatio = row->disabled() ? 0. : row->checkedRatio();
p.setPen(anim::pen(st::contactsNameFg, st::contactsNameCheckedFg, nameCheckedRatio));
name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width());
if (!actionSize.isEmpty()) {
@ -1263,177 +1262,3 @@ void PeerListBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names
}
}
}
void PeerListRowWithLink::setActionLink(const QString &action) {
_action = action;
refreshActionLink();
}
void PeerListRowWithLink::refreshActionLink() {
if (!isInitialized()) return;
_actionWidth = _action.isEmpty() ? 0 : st::normalFont->width(_action);
}
void PeerListRowWithLink::lazyInitialize() {
PeerListRow::lazyInitialize();
refreshActionLink();
}
QSize PeerListRowWithLink::actionSize() const {
return QSize(_actionWidth, st::normalFont->height);
}
QMargins PeerListRowWithLink::actionMargins() const {
return QMargins(st::contactsCheckPosition.x(), (st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom() - st::normalFont->height) / 2, st::contactsCheckPosition.x(), 0);
}
void PeerListRowWithLink::paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) {
p.setFont(actionSelected ? st::linkOverFont : st::linkFont);
p.setPen(actionSelected ? st::defaultLinkButton.overColor : st::defaultLinkButton.color);
p.drawTextLeft(x, y, outerWidth, _action, _actionWidth);
}
PeerListGlobalSearchController::PeerListGlobalSearchController() {
_timer.setCallback([this] { searchOnServer(); });
}
void PeerListGlobalSearchController::searchQuery(const QString &query) {
if (_query != query) {
_query = query;
_requestId = 0;
if (_query.size() >= MinUsernameLength && !searchInCache()) {
_timer.callOnce(AutoSearchTimeout);
} else {
_timer.cancel();
}
}
}
bool PeerListGlobalSearchController::searchInCache() {
auto it = _cache.find(_query);
if (it != _cache.cend()) {
_requestId = 0;
searchDone(it->second, _requestId);
return true;
}
return false;
}
void PeerListGlobalSearchController::searchOnServer() {
_requestId = request(MTPcontacts_Search(MTP_string(_query), MTP_int(SearchPeopleLimit))).done([this](const MTPcontacts_Found &result, mtpRequestId requestId) {
searchDone(result, requestId);
}).fail([this](const RPCError &error, mtpRequestId requestId) {
if (_requestId == requestId) {
_requestId = 0;
delegate()->peerListSearchRefreshRows();
}
}).send();
_queries.emplace(_requestId, _query);
}
void PeerListGlobalSearchController::searchDone(const MTPcontacts_Found &result, mtpRequestId requestId) {
Expects(result.type() == mtpc_contacts_found);
auto &contacts = result.c_contacts_found();
auto query = _query;
if (requestId) {
App::feedUsers(contacts.vusers);
App::feedChats(contacts.vchats);
auto it = _queries.find(requestId);
if (it != _queries.cend()) {
query = it->second;
_cache[query] = result;
_queries.erase(it);
}
}
if (_requestId == requestId) {
_requestId = 0;
for_const (auto &mtpPeer, contacts.vresults.v) {
if (auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) {
delegate()->peerListSearchAddRow(peer);
}
}
delegate()->peerListSearchRefreshRows();
}
}
bool PeerListGlobalSearchController::isLoading() {
return _timer.isActive() || _requestId;
}
ChatsListBoxController::ChatsListBoxController(std::unique_ptr<PeerListSearchController> searchController) : PeerListController(std::move(searchController)) {
}
void ChatsListBoxController::prepare() {
setSearchNoResultsText(lang(lng_blocked_list_not_found));
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
prepareViewHook();
rebuildRows();
auto &sessionData = Auth().data();
subscribe(sessionData.contactsLoaded(), [this](bool loaded) {
rebuildRows();
});
subscribe(sessionData.moreChatsLoaded(), [this] {
rebuildRows();
});
subscribe(sessionData.allChatsLoaded(), [this](bool loaded) {
checkForEmptyRows();
});
}
void ChatsListBoxController::rebuildRows() {
auto ms = getms();
auto wasEmpty = !delegate()->peerListFullRowsCount();
auto appendList = [this](auto chats) {
auto count = 0;
for_const (auto row, chats->all()) {
auto history = row->history();
if (history->peer->isUser()) {
if (appendRow(history)) {
++count;
}
}
}
return count;
};
auto added = appendList(App::main()->dialogsList());
added += appendList(App::main()->contactsNoDialogsList());
if (!wasEmpty && added > 0) {
// Place dialogs list before contactsNoDialogs list.
delegate()->peerListPartitionRows([](PeerListRow &a) {
auto history = static_cast<Row&>(a).history();
return history->inChatList(Dialogs::Mode::All);
});
}
checkForEmptyRows();
delegate()->peerListRefreshRows();
}
void ChatsListBoxController::checkForEmptyRows() {
if (delegate()->peerListFullRowsCount()) {
setDescriptionText(QString());
} else {
auto &sessionData = Auth().data();
auto loaded = sessionData.contactsLoaded().value() && sessionData.allChatsLoaded().value();
setDescriptionText(lang(loaded ? lng_contacts_not_found : lng_contacts_loading));
}
}
std::unique_ptr<PeerListRow> ChatsListBoxController::createSearchRow(gsl::not_null<PeerData*> peer) {
return createRow(App::history(peer));
}
bool ChatsListBoxController::appendRow(History *history) {
if (auto row = delegate()->peerListFindRow(history->peer->id)) {
updateRowHook(static_cast<Row*>(row));
return false;
}
if (auto row = createRow(history)) {
delegate()->peerListAppendRow(std::move(row));
return true;
}
return false;
}

View File

@ -33,14 +33,25 @@ class WidgetSlideWrap;
class FlatLabel;
} // namespace Ui
inline auto PaintUserpicCallback(PeerData *peer) {
return [peer](Painter &p, int x, int y, int outerWidth, int size) {
peer->paintUserpicLeft(p, x, y, outerWidth, size);
};
}
using PeerListRowId = uint64;
class PeerListRow {
public:
PeerListRow(gsl::not_null<PeerData*> peer);
PeerListRow(gsl::not_null<PeerData*> peer, PeerListRowId id);
void setDisabled(bool disabled) {
_disabled = disabled;
enum class State {
Active,
Disabled,
DisabledChecked,
};
void setDisabledState(State state) {
_disabledState = state;
}
// Checked state is controlled by the box with multiselect,
@ -97,7 +108,7 @@ public:
return _absoluteIndex;
}
bool disabled() const {
return _disabled;
return (_disabledState != State::Active);
}
bool isSearchResult() const {
return _isSearchResult;
@ -156,8 +167,8 @@ private:
StatusType _statusType = StatusType::Online;
OrderedSet<QChar> _nameFirstChars;
int _absoluteIndex = -1;
State _disabledState = State::Active;
bool _initialized : 1;
bool _disabled : 1;
bool _isSearchResult : 1;
};
@ -170,6 +181,7 @@ enum class PeerListSearchMode {
class PeerListDelegate {
public:
virtual void peerListSetTitle(base::lambda<QString()> title) = 0;
virtual void peerListSetAdditionalTitle(base::lambda<QString()> title) = 0;
virtual void peerListSetDescription(object_ptr<Ui::FlatLabel> description) = 0;
virtual void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) = 0;
virtual void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) = 0;
@ -200,6 +212,7 @@ public:
peerListFinishSelectedRowsBunch();
}
virtual int peerListSelectedRowsCount() = 0;
virtual std::vector<gsl::not_null<PeerData*>> peerListCollectSelectedRows() = 0;
virtual ~PeerListDelegate() = default;
@ -254,6 +267,8 @@ public:
}
virtual void loadMoreRows() {
}
virtual void itemDeselectedHook(gsl::not_null<PeerData*> peer) {
}
bool isSearchLoading() const {
return _searchController ? _searchController->isLoading() : false;
}
@ -305,11 +320,14 @@ private:
class PeerListBox : public BoxContent, public PeerListDelegate {
public:
PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(PeerListBox*)> init);
PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(gsl::not_null<PeerListBox*>)> init);
void peerListSetTitle(base::lambda<QString()> title) override {
setTitle(std::move(title));
}
void peerListSetAdditionalTitle(base::lambda<QString()> title) override {
setAdditionalTitle(std::move(title));
}
void peerListSetDescription(object_ptr<Ui::FlatLabel> description) override;
void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) override;
void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) override;
@ -325,6 +343,7 @@ public:
void peerListSetRowChecked(gsl::not_null<PeerListRow*> row, bool checked) override;
gsl::not_null<PeerListRow*> peerListRowAt(int index) override;
bool peerListIsRowSelected(gsl::not_null<PeerData*> peer) override;
int peerListSelectedRowsCount() override;
std::vector<gsl::not_null<PeerData*>> peerListCollectSelectedRows() override;
void peerListRefreshRows() override;
void peerListScrollToTop() override;
@ -530,77 +549,3 @@ private:
std::vector<std::unique_ptr<PeerListRow>> _searchRows;
};
class PeerListRowWithLink : public PeerListRow {
public:
using PeerListRow::PeerListRow;
void setActionLink(const QString &action);
void lazyInitialize() override;
private:
void refreshActionLink();
QSize actionSize() const override;
QMargins actionMargins() const override;
void paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) override;
QString _action;
int _actionWidth = 0;
};
class PeerListGlobalSearchController : public PeerListSearchController, private MTP::Sender {
public:
PeerListGlobalSearchController();
void searchQuery(const QString &query) override;
bool isLoading() override;
bool loadMoreRows() override {
return false;
}
private:
bool searchInCache();
void searchOnServer();
void searchDone(const MTPcontacts_Found &result, mtpRequestId requestId);
base::Timer _timer;
QString _query;
mtpRequestId _requestId = 0;
std::map<QString, MTPcontacts_Found> _cache;
std::map<mtpRequestId, QString> _queries;
};
class ChatsListBoxController : public PeerListController, protected base::Subscriber {
public:
ChatsListBoxController(std::unique_ptr<PeerListSearchController> searchController = std::make_unique<PeerListGlobalSearchController>());
void prepare() override final;
std::unique_ptr<PeerListRow> createSearchRow(gsl::not_null<PeerData*> peer) override final;
protected:
class Row : public PeerListRow {
public:
Row(gsl::not_null<History*> history) : PeerListRow(history->peer), _history(history) {
}
gsl::not_null<History*> history() const {
return _history;
}
private:
gsl::not_null<History*> _history;
};
virtual std::unique_ptr<Row> createRow(gsl::not_null<History*> history) = 0;
virtual void prepareViewHook() = 0;
virtual void updateRowHook(Row *row) {
}
private:
void rebuildRows();
void checkForEmptyRows();
bool appendRow(History *history);
};

View File

@ -0,0 +1,270 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/peer_list_controllers.h"
#include "styles/style_boxes.h"
#include "auth_session.h"
#include "mainwidget.h"
#include "lang/lang_keys.h"
#include "dialogs/dialogs_indexed_list.h"
void PeerListRowWithLink::setActionLink(const QString &action) {
_action = action;
refreshActionLink();
}
void PeerListRowWithLink::refreshActionLink() {
if (!isInitialized()) return;
_actionWidth = _action.isEmpty() ? 0 : st::normalFont->width(_action);
}
void PeerListRowWithLink::lazyInitialize() {
PeerListRow::lazyInitialize();
refreshActionLink();
}
QSize PeerListRowWithLink::actionSize() const {
return QSize(_actionWidth, st::normalFont->height);
}
QMargins PeerListRowWithLink::actionMargins() const {
return QMargins(st::contactsCheckPosition.x(), (st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom() - st::normalFont->height) / 2, st::contactsCheckPosition.x(), 0);
}
void PeerListRowWithLink::paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) {
p.setFont(actionSelected ? st::linkOverFont : st::linkFont);
p.setPen(actionSelected ? st::defaultLinkButton.overColor : st::defaultLinkButton.color);
p.drawTextLeft(x, y, outerWidth, _action, _actionWidth);
}
PeerListGlobalSearchController::PeerListGlobalSearchController() {
_timer.setCallback([this] { searchOnServer(); });
}
void PeerListGlobalSearchController::searchQuery(const QString &query) {
if (_query != query) {
_query = query;
_requestId = 0;
if (_query.size() >= MinUsernameLength && !searchInCache()) {
_timer.callOnce(AutoSearchTimeout);
} else {
_timer.cancel();
}
}
}
bool PeerListGlobalSearchController::searchInCache() {
auto it = _cache.find(_query);
if (it != _cache.cend()) {
_requestId = 0;
searchDone(it->second, _requestId);
return true;
}
return false;
}
void PeerListGlobalSearchController::searchOnServer() {
_requestId = request(MTPcontacts_Search(MTP_string(_query), MTP_int(SearchPeopleLimit))).done([this](const MTPcontacts_Found &result, mtpRequestId requestId) {
searchDone(result, requestId);
}).fail([this](const RPCError &error, mtpRequestId requestId) {
if (_requestId == requestId) {
_requestId = 0;
delegate()->peerListSearchRefreshRows();
}
}).send();
_queries.emplace(_requestId, _query);
}
void PeerListGlobalSearchController::searchDone(const MTPcontacts_Found &result, mtpRequestId requestId) {
Expects(result.type() == mtpc_contacts_found);
auto &contacts = result.c_contacts_found();
auto query = _query;
if (requestId) {
App::feedUsers(contacts.vusers);
App::feedChats(contacts.vchats);
auto it = _queries.find(requestId);
if (it != _queries.cend()) {
query = it->second;
_cache[query] = result;
_queries.erase(it);
}
}
if (_requestId == requestId) {
_requestId = 0;
for_const (auto &mtpPeer, contacts.vresults.v) {
if (auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) {
delegate()->peerListSearchAddRow(peer);
}
}
delegate()->peerListSearchRefreshRows();
}
}
bool PeerListGlobalSearchController::isLoading() {
return _timer.isActive() || _requestId;
}
ChatsListBoxController::ChatsListBoxController(std::unique_ptr<PeerListSearchController> searchController) : PeerListController(std::move(searchController)) {
}
void ChatsListBoxController::prepare() {
setSearchNoResultsText(lang(lng_blocked_list_not_found));
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
prepareViewHook();
rebuildRows();
auto &sessionData = Auth().data();
subscribe(sessionData.contactsLoaded(), [this](bool loaded) {
rebuildRows();
});
subscribe(sessionData.moreChatsLoaded(), [this] {
rebuildRows();
});
subscribe(sessionData.allChatsLoaded(), [this](bool loaded) {
checkForEmptyRows();
});
}
void ChatsListBoxController::rebuildRows() {
auto wasEmpty = !delegate()->peerListFullRowsCount();
auto appendList = [this](auto chats) {
auto count = 0;
for_const (auto row, chats->all()) {
auto history = row->history();
if (history->peer->isUser()) {
if (appendRow(history)) {
++count;
}
}
}
return count;
};
auto added = appendList(App::main()->dialogsList());
added += appendList(App::main()->contactsNoDialogsList());
if (!wasEmpty && added > 0) {
// Place dialogs list before contactsNoDialogs list.
delegate()->peerListPartitionRows([](PeerListRow &a) {
auto history = static_cast<Row&>(a).history();
return history->inChatList(Dialogs::Mode::All);
});
}
checkForEmptyRows();
delegate()->peerListRefreshRows();
}
void ChatsListBoxController::checkForEmptyRows() {
if (delegate()->peerListFullRowsCount()) {
setDescriptionText(QString());
} else {
auto &sessionData = Auth().data();
auto loaded = sessionData.contactsLoaded().value() && sessionData.allChatsLoaded().value();
setDescriptionText(lang(loaded ? lng_contacts_not_found : lng_contacts_loading));
}
}
std::unique_ptr<PeerListRow> ChatsListBoxController::createSearchRow(gsl::not_null<PeerData*> peer) {
return createRow(App::history(peer));
}
bool ChatsListBoxController::appendRow(gsl::not_null<History*> history) {
if (auto row = delegate()->peerListFindRow(history->peer->id)) {
updateRowHook(static_cast<Row*>(row));
return false;
}
if (auto row = createRow(history)) {
delegate()->peerListAppendRow(std::move(row));
return true;
}
return false;
}
ContactsBoxController::ContactsBoxController(std::unique_ptr<PeerListSearchController> searchController) : PeerListController(std::move(searchController)) {
}
void ContactsBoxController::prepare() {
setSearchNoResultsText(lang(lng_blocked_list_not_found));
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
delegate()->peerListSetTitle(langFactory(lng_contacts_header));
prepareViewHook();
rebuildRows();
auto &sessionData = Auth().data();
subscribe(sessionData.contactsLoaded(), [this](bool loaded) {
rebuildRows();
});
}
void ContactsBoxController::rebuildRows() {
auto appendList = [this](auto chats) {
auto count = 0;
for_const (auto row, chats->all()) {
auto history = row->history();
if (auto user = history->peer->asUser()) {
if (appendRow(user)) {
++count;
}
}
}
return count;
};
appendList(App::main()->contactsList());
checkForEmptyRows();
delegate()->peerListRefreshRows();
}
void ContactsBoxController::checkForEmptyRows() {
if (delegate()->peerListFullRowsCount()) {
setDescriptionText(QString());
} else {
auto &sessionData = Auth().data();
auto loaded = sessionData.contactsLoaded().value();
setDescriptionText(lang(loaded ? lng_contacts_not_found : lng_contacts_loading));
}
}
std::unique_ptr<PeerListRow> ContactsBoxController::createSearchRow(gsl::not_null<PeerData*> peer) {
return createRow(peer->asUser());
}
void ContactsBoxController::rowClicked(gsl::not_null<PeerListRow*> row) {
Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId);
}
bool ContactsBoxController::appendRow(gsl::not_null<UserData*> user) {
if (auto row = delegate()->peerListFindRow(user->id)) {
updateRowHook(row);
return false;
}
if (auto row = createRow(user)) {
delegate()->peerListAppendRow(std::move(row));
return true;
}
return false;
}
std::unique_ptr<PeerListRow> ContactsBoxController::createRow(gsl::not_null<UserData*> user) {
return std::make_unique<PeerListRow>(user);
}

View File

@ -0,0 +1,119 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "peer_list_box.h"
class PeerListRowWithLink : public PeerListRow {
public:
using PeerListRow::PeerListRow;
void setActionLink(const QString &action);
void lazyInitialize() override;
private:
void refreshActionLink();
QSize actionSize() const override;
QMargins actionMargins() const override;
void paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) override;
QString _action;
int _actionWidth = 0;
};
class PeerListGlobalSearchController : public PeerListSearchController, private MTP::Sender {
public:
PeerListGlobalSearchController();
void searchQuery(const QString &query) override;
bool isLoading() override;
bool loadMoreRows() override {
return false;
}
private:
bool searchInCache();
void searchOnServer();
void searchDone(const MTPcontacts_Found &result, mtpRequestId requestId);
base::Timer _timer;
QString _query;
mtpRequestId _requestId = 0;
std::map<QString, MTPcontacts_Found> _cache;
std::map<mtpRequestId, QString> _queries;
};
class ChatsListBoxController : public PeerListController, protected base::Subscriber {
public:
ChatsListBoxController(std::unique_ptr<PeerListSearchController> searchController = std::make_unique<PeerListGlobalSearchController>());
void prepare() override final;
std::unique_ptr<PeerListRow> createSearchRow(gsl::not_null<PeerData*> peer) override final;
protected:
class Row : public PeerListRow {
public:
Row(gsl::not_null<History*> history) : PeerListRow(history->peer), _history(history) {
}
gsl::not_null<History*> history() const {
return _history;
}
private:
gsl::not_null<History*> _history;
};
virtual std::unique_ptr<Row> createRow(gsl::not_null<History*> history) = 0;
virtual void prepareViewHook() = 0;
virtual void updateRowHook(gsl::not_null<Row*> row) {
}
private:
void rebuildRows();
void checkForEmptyRows();
bool appendRow(gsl::not_null<History*> history);
};
class ContactsBoxController : public PeerListController, protected base::Subscriber {
public:
ContactsBoxController(std::unique_ptr<PeerListSearchController> searchController = std::make_unique<PeerListGlobalSearchController>());
void prepare() override final;
std::unique_ptr<PeerListRow> createSearchRow(gsl::not_null<PeerData*> peer) override final;
void rowClicked(gsl::not_null<PeerListRow*> row) override;
protected:
virtual std::unique_ptr<PeerListRow> createRow(gsl::not_null<UserData*> user);
virtual void prepareViewHook() {
}
virtual void updateRowHook(gsl::not_null<PeerListRow*> row) {
}
private:
void rebuildRows();
void checkForEmptyRows();
bool appendRow(gsl::not_null<UserData*> user);
};

View File

@ -38,7 +38,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "window/themes/window_theme.h"
#include "boxes/contacts_box.h"
#include "boxes/peer_list_box.h"
#include "auth_session.h"
#include "messenger.h"

View File

@ -617,7 +617,7 @@ enum ShowLayerOption {
AnimatedShowLayer = 0x00,
ForceFastShowLayer = 0x04,
};
Q_DECLARE_FLAGS(ShowLayerOptions, ShowLayerOption);
using ShowLayerOptions = QFlags<ShowLayerOption>;
Q_DECLARE_OPERATORS_FOR_FLAGS(ShowLayerOptions);
static int32 FullArcLength = 360 * 16;

View File

@ -181,7 +181,7 @@ private:
MessageSearch = 0x08,
All = 0x0F,
};
Q_DECLARE_FLAGS(UpdateRowSections, UpdateRowSection);
using UpdateRowSections = QFlags<UpdateRowSection>;
Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(UpdateRowSections);
void updateDialogRow(PeerData *peer, MsgId msgId, QRect updateRect, UpdateRowSections sections = UpdateRowSection::All);

View File

@ -545,7 +545,7 @@ private:
f_has_pending_resized_items = (1 << 0),
f_pending_resize = (1 << 1),
};
Q_DECLARE_FLAGS(Flags, Flag);
using Flags = QFlags<Flag>;
Q_DECL_CONSTEXPR friend inline QFlags<Flags::enum_type> operator|(Flags::enum_type f1, Flags::enum_type f2) noexcept {
return QFlags<Flags::enum_type>(f1) | f2;
}

View File

@ -174,7 +174,7 @@ struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply> {
PaintInBubble = 0x01,
PaintSelected = 0x02,
};
Q_DECLARE_FLAGS(PaintFlags, PaintFlag);
using PaintFlags = QFlags<PaintFlag>;
void paint(Painter &p, const HistoryItem *holder, int x, int y, int w, PaintFlags flags) const;
MsgId replyToId() const {

View File

@ -689,7 +689,7 @@ private:
SaveDraft = 0x01,
SendTyping = 0x02,
};
Q_DECLARE_FLAGS(TextUpdateEvents, TextUpdateEvent);
using TextUpdateEvents = QFlags<TextUpdateEvent>;
Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(TextUpdateEvents);
void writeDrafts(Data::Draft **localDraft, Data::Draft **editDraft);

View File

@ -72,7 +72,7 @@ struct PeerUpdate {
ChannelRightsChanged = 0x00020000U,
ChannelStickersChanged = 0x00040000U,
};
Q_DECLARE_FLAGS(Flags, Flag);
using Flags = QFlags<Flag>;
Flags flags = 0;
// NameChanged data

View File

@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "window/notifications_manager_default.h"
#include "platform/platform_notifications_manager.h"
#include "boxes/contacts_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/about_box.h"
#include "lang/lang_keys.h"
#include "platform/mac/mac_utilities.h"
@ -437,7 +438,10 @@ void MainWindow::createGlobalMenu() {
if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray();
if (!App::self()) return;
Ui::show(Box<ContactsBox>());
Ui::show(Box<PeerListBox>(std::make_unique<ContactsBoxController>(), [](gsl::not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
box->addLeftButton(langFactory(lng_profile_add_contact), [] { App::wnd()->onShowAddContact(); });
}));
});
psAddContact = window->addAction(lang(lng_mac_menu_add_contact), App::wnd(), SLOT(onShowAddContact()));
window->addSeparator();

View File

@ -60,7 +60,7 @@ public:
Hidden = 0x08,
Activate = 0x10,
};
Q_DECLARE_FLAGS(ShadowsChanges, ShadowsChange);
using ShadowsChanges = QFlags<ShadowsChange>;
bool shadowsWorking() const {
return _shadowsWorking;

View File

@ -20,8 +20,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "profile/profile_channel_controllers.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/edit_participant_box.h"
#include "boxes/confirm_box.h"
#include "boxes/add_contact_box.h"
#include "boxes/contacts_box.h"
#include "auth_session.h"
#include "apiwrap.h"
@ -55,7 +57,7 @@ std::unique_ptr<PeerListSearchController> ParticipantsBoxController::CreateSearc
void ParticipantsBoxController::Start(gsl::not_null<ChannelData*> channel, Role role) {
auto controller = std::make_unique<ParticipantsBoxController>(channel, role);
auto initBox = [role, channel, controller = controller.get()](PeerListBox *box) {
auto initBox = [role, channel, controller = controller.get()](gsl::not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
auto canAddNewItem = [role, channel] {
switch (role) {
@ -87,12 +89,12 @@ void ParticipantsBoxController::addNewItem() {
if (_channel->membersCount() >= Global::ChatSizeMax()) {
Ui::show(Box<MaxInviteBox>(_channel), KeepOtherLayers);
} else {
auto already = MembersAlreadyIn();
auto already = std::vector<gsl::not_null<UserData*>>();
already.reserve(delegate()->peerListFullRowsCount());
for (auto i = 0, count = delegate()->peerListFullRowsCount(); i != count; ++i) {
auto user = delegate()->peerListRowAt(i)->peer()->asUser();
already.insert(user);
already.push_back(delegate()->peerListRowAt(i)->peer()->asUser());
}
Ui::show(Box<ContactsBox>(_channel, MembersFilter::Recent, already));
ShowAddContactsToChannelBox(_channel, { already.begin(), already.end() });
}
return;
}
@ -105,7 +107,7 @@ void ParticipantsBoxController::addNewItem() {
if (weak) {
weak->editRestrictedDone(user, rights);
}
}), [](PeerListBox *box) {
}), [](gsl::not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
}), KeepOtherLayers);
}

View File

@ -31,6 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "boxes/confirm_box.h"
#include "boxes/contacts_box.h"
#include "boxes/photo_crop_box.h"
#include "boxes/add_contact_box.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "auth_session.h"
@ -538,14 +539,11 @@ void CoverWidget::onAddMember() {
if (_peerChat->count >= Global::ChatSizeMax() && _peerChat->amCreator()) {
Ui::show(Box<ConvertToSupergroupBox>(_peerChat));
} else {
Ui::show(Box<ContactsBox>(_peerChat, MembersFilter::Recent));
ShowAddContactsToChatBox(_peerChat);
}
} else if (_peerChannel && _peerChannel->mgInfo) {
MembersAlreadyIn already;
for_const (auto user, _peerChannel->mgInfo->lastParticipants) {
already.insert(user);
}
Ui::show(Box<ContactsBox>(_peerChannel, MembersFilter::Recent, already));
auto &participants = _peerChannel->mgInfo->lastParticipants;
ShowAddContactsToChannelBox(_peerChannel, { participants.cbegin(), participants.cend() });
}
}

View File

@ -26,6 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "auth_session.h"
#include "storage/localstorage.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/confirm_box.h"
namespace Settings {
@ -44,7 +45,7 @@ public:
protected:
void prepareViewHook() override;
std::unique_ptr<Row> createRow(gsl::not_null<History*> history) override;
void updateRowHook(Row *row) override {
void updateRowHook(gsl::not_null<Row*> row) override {
updateIsBlocked(row, row->peer()->asUser());
delegate()->peerListUpdateRow(row);
}
@ -70,7 +71,7 @@ void BlockUserBoxController::prepareViewHook() {
void BlockUserBoxController::updateIsBlocked(gsl::not_null<PeerListRow*> row, UserData *user) const {
auto blocked = user->isBlocked();
row->setDisabled(blocked);
row->setDisabledState(blocked ? PeerListRow::State::DisabledChecked : PeerListRow::State::Active);
if (blocked) {
row->setCustomStatus(lang(lng_blocked_list_already_blocked));
} else {
@ -186,7 +187,7 @@ void BlockedBoxController::handleBlockedEvent(UserData *user) {
void BlockedBoxController::BlockNewUser() {
auto controller = std::make_unique<BlockUserBoxController>();
auto initBox = [controller = controller.get()](PeerListBox *box) {
auto initBox = [controller = controller.get()](gsl::not_null<PeerListBox*> box) {
controller->setBlockUserCallback([box](gsl::not_null<UserData*> user) {
Auth().api().blockUser(user);
box->closeBox();

View File

@ -208,7 +208,7 @@ void PrivacyWidget::autoLockUpdated() {
}
void PrivacyWidget::onBlockedUsers() {
Ui::show(Box<PeerListBox>(std::make_unique<BlockedBoxController>(), [](PeerListBox *box) {
Ui::show(Box<PeerListBox>(std::make_unique<BlockedBoxController>(), [](gsl::not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
box->addLeftButton(langFactory(lng_blocked_list_add), [box] { BlockedBoxController::BlockNewUser(); });
}));

View File

@ -78,7 +78,7 @@ enum class FileOption {
User = 0x01,
Safe = 0x02,
};
Q_DECLARE_FLAGS(FileOptions, FileOption);
using FileOptions = QFlags<FileOption>;
Q_DECLARE_OPERATORS_FOR_FLAGS(FileOptions);
bool keyAlreadyUsed(QString &name, FileOptions options = FileOption::User | FileOption::Safe) {

View File

@ -80,7 +80,7 @@ protected:
Down = 0x02,
Disabled = 0x04,
};
Q_DECLARE_FLAGS(State, StateFlag);
using State = QFlags<StateFlag>;
Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(State);
State state() const {

View File

@ -47,7 +47,7 @@ enum class ImageRoundCorner {
BottomRight = 0x08,
All = 0x0f,
};
Q_DECLARE_FLAGS(ImageRoundCorners, ImageRoundCorner);
using ImageRoundCorners = QFlags<ImageRoundCorner>;
Q_DECLARE_OPERATORS_FOR_FLAGS(ImageRoundCorners);
inline uint32 packInt(int32 a) {
@ -199,7 +199,7 @@ enum class Option {
Colored = 0x200,
TransparentBackground = 0x400,
};
Q_DECLARE_FLAGS(Options, Option);
using Options = QFlags<Option>;
Q_DECLARE_OPERATORS_FOR_FLAGS(Options);
QImage prepare(QImage img, int w, int h, Options options, int outerw, int outerh, const style::color *colored = nullptr);

View File

@ -132,7 +132,7 @@ public:
LookupSymbol = 0x02,
LookupLink = 0x04,
};
Q_DECLARE_FLAGS(Flags, Flag);
using Flags = QFlags<Flag>;
StateRequest() {
}

View File

@ -57,7 +57,7 @@ enum class RectPart {
Full = FullTop | NoTop,
};
Q_DECLARE_FLAGS(RectParts, RectPart);
using RectParts = QFlags<RectPart>;
Q_DECLARE_OPERATORS_FOR_FLAGS(RectParts);
inline bool IsTopCorner(RectPart corner) {

View File

@ -30,7 +30,7 @@ enum class GifPauseReason {
RoundPlaying = (1 << 3),
MediaPreview = (1 << 4),
};
Q_DECLARE_FLAGS(GifPauseReasons, GifPauseReason);
using GifPauseReasons = QFlags<GifPauseReason>;
Q_DECLARE_OPERATORS_FOR_FLAGS(GifPauseReasons);
class MainWindow;

View File

@ -31,7 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/localstorage.h"
#include "boxes/contacts_box.h"
#include "boxes/about_box.h"
#include "boxes/peer_list_box.h"
#include "boxes/peer_list_controllers.h"
#include "calls/calls_box_controller.h"
#include "lang/lang_keys.h"
#include "core/click_handler_types.h"
@ -97,11 +97,14 @@ void MainMenu::refreshMenu() {
App::wnd()->onShowNewChannel();
}, &st::mainMenuNewChannel, &st::mainMenuNewChannelOver);
_menu->addAction(lang(lng_menu_contacts), [] {
Ui::show(Box<ContactsBox>());
Ui::show(Box<PeerListBox>(std::make_unique<ContactsBoxController>(), [](gsl::not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
box->addLeftButton(langFactory(lng_profile_add_contact), [] { App::wnd()->onShowAddContact(); });
}));
}, &st::mainMenuContacts, &st::mainMenuContactsOver);
if (Global::PhoneCallsEnabled()) {
_menu->addAction(lang(lng_menu_calls), [] {
Ui::show(Box<PeerListBox>(std::make_unique<Calls::BoxController>(), [](PeerListBox *box) {
Ui::show(Box<PeerListBox>(std::make_unique<Calls::BoxController>(), [](gsl::not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
}));
}, &st::mainMenuCalls, &st::mainMenuCallsOver);

View File

@ -66,6 +66,8 @@
<(src_loc)/boxes/notifications_box.h
<(src_loc)/boxes/peer_list_box.cpp
<(src_loc)/boxes/peer_list_box.h
<(src_loc)/boxes/peer_list_controllers.cpp
<(src_loc)/boxes/peer_list_controllers.h
<(src_loc)/boxes/passcode_box.cpp
<(src_loc)/boxes/passcode_box.h
<(src_loc)/boxes/photo_crop_box.cpp