From e5a5273b3a9f46db38824b6d8b01fac08a0b4992 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 20 Oct 2016 19:32:15 +0300 Subject: [PATCH] Scrollable boxes reorganized: _scroll always owns _inner. Also some boxes moved to separate modules: MembersBox, StickersBox. --- Telegram/Resources/art/sprite.png | Bin 40270 -> 40118 bytes Telegram/Resources/art/sprite_200x.png | Bin 85777 -> 85439 bytes Telegram/Resources/basic.style | 42 - .../Resources/icons/box_search_cancel.png | Bin 0 -> 187 bytes .../Resources/icons/box_search_cancel@2x.png | Bin 0 -> 296 bytes Telegram/SourceFiles/boxes/abstractbox.cpp | 10 +- Telegram/SourceFiles/boxes/abstractbox.h | 3 +- Telegram/SourceFiles/boxes/backgroundbox.cpp | 78 +- Telegram/SourceFiles/boxes/backgroundbox.h | 40 +- Telegram/SourceFiles/boxes/boxes.style | 32 + Telegram/SourceFiles/boxes/contactsbox.cpp | 1656 ++++++----------- Telegram/SourceFiles/boxes/contactsbox.h | 417 ++--- Telegram/SourceFiles/boxes/members_box.cpp | 607 ++++++ Telegram/SourceFiles/boxes/members_box.h | 178 ++ Telegram/SourceFiles/boxes/sessionsbox.cpp | 398 ++-- Telegram/SourceFiles/boxes/sessionsbox.h | 100 +- Telegram/SourceFiles/boxes/sharebox.cpp | 71 +- Telegram/SourceFiles/boxes/sharebox.h | 24 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 1254 +++++++++++++ Telegram/SourceFiles/boxes/stickers_box.h | 239 +++ Telegram/SourceFiles/boxes/stickersetbox.cpp | 1500 ++------------- Telegram/SourceFiles/boxes/stickersetbox.h | 293 +-- .../SourceFiles/codegen/style/parsed_file.cpp | 2 +- .../SourceFiles/profile/profile_cover.cpp | 4 +- .../profile/profile_members_widget.cpp | 4 +- .../profile/profile_settings_widget.cpp | 4 +- .../settings_chat_settings_widget.cpp | 2 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 1 + Telegram/SourceFiles/stickers/stickers.cpp | 2 +- Telegram/SourceFiles/ui/countryinput.cpp | 190 +- Telegram/SourceFiles/ui/countryinput.h | 77 +- .../SourceFiles/ui/widgets/filled_slider.h | 5 +- .../SourceFiles/ui/widgets/media_slider.h | 5 +- Telegram/gyp/Telegram.gyp | 4 + 34 files changed, 3676 insertions(+), 3566 deletions(-) create mode 100644 Telegram/Resources/icons/box_search_cancel.png create mode 100644 Telegram/Resources/icons/box_search_cancel@2x.png create mode 100644 Telegram/SourceFiles/boxes/members_box.cpp create mode 100644 Telegram/SourceFiles/boxes/members_box.h create mode 100644 Telegram/SourceFiles/boxes/stickers_box.cpp create mode 100644 Telegram/SourceFiles/boxes/stickers_box.h diff --git a/Telegram/Resources/art/sprite.png b/Telegram/Resources/art/sprite.png index c51595ddf239d09e8ef602dda9247a3f7a35d9ca..fb2205f25c5e60782bde38a2360e85983c42e40b 100644 GIT binary patch literal 40118 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 Set~^AqSmHr9}Eyvxw#x@a^%2qd#7i= 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 Iy4{oi2YD1u`v3p{ delta 66201 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 Y!$SyZ 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 diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 7e75dd058..f2c34e362 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -343,48 +343,6 @@ boxScroll: flatScroll(solidScroll) { boxScrollSkip: 6px; boxScrollShadowBg: #00000012; -boxSearchField: InputField(defaultInputField) { - textMargins: margins(41px, 16px, 41px, 0px); - - placeholderFg: #999; - placeholderFgActive: #aaa; - placeholderMargins: margins(4px, 0px, 4px, 0px); - - border: 0px; - borderActive: 0px; - borderError: 0px; - - height: 48px; - - iconSprite: sprite(227px, 21px, 24px, 24px); - iconPosition: point(15px, 14px); - - font: normalFont; -} -boxSearchCancel: iconedButton { - color: white; - bgColor: white; - overBgColor: white; - font: font(fsize); - - opacity: 0.3; - overOpacity: 0.4; - - textPos: point(0px, 0px); - downTextPos: point(0px, 0px); - - duration: 150; - cursor: cursor(pointer); - - icon: sprite(133px, 108px, 12px, 12px); - iconPos: point(8px, 18px); - downIcon: sprite(133px, 108px, 12px, 12px); - downIconPos: point(8px, 18px); - - width: 41px; - height: 48px; -} - titleBg: #6389a8; titleHeight: 39px; titleIconPos: point(7px, 7px); diff --git a/Telegram/Resources/icons/box_search_cancel.png b/Telegram/Resources/icons/box_search_cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..7fa112a33b2e9cef41f273abc62eb07b4a1839b4 GIT binary patch literal 187 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a)4CQlc~5RHkYlP+?y8gj5qzuvy= zzucZ6AMIlK<9me-ofKa1om^E^uEb>Qe5vA0Psf3~7fWLqQl4?ll$^=w;G$5T{@&Ip zbk5B4+>>T3X#T+%w}OlB5&w$|j%WN;8NNysPd-?ce|Proi?)9k#;vHATWGZ-Z-cX7 m-C&U>J17(8A5T-G@yGywp}X-3=t literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/box_search_cancel@2x.png b/Telegram/Resources/icons/box_search_cancel@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..40d3def3d2c56edb57dc818be15c1add75a32652 GIT binary patch literal 296 zcmV+@0oVSCP)CU0sydW8>EyV#t0+joP&9uVc+-f zo-niILPYsU&C5CWy6=MV< us`fKmy!Tx`KwVn9@0000setWidget(inner); - _scroll->setFocusPolicy(Qt::NoFocus); - ScrollableBox::resizeEvent(nullptr); -} - -void ScrollableBox::initOwned(QWidget *inner, int bottomSkip, int topSkip) { +void ScrollableBox::init(ScrolledWidget *inner, int bottomSkip, int topSkip) { _bottomSkip = bottomSkip; _topSkip = topSkip; _scroll->setOwnedWidget(inner); diff --git a/Telegram/SourceFiles/boxes/abstractbox.h b/Telegram/SourceFiles/boxes/abstractbox.h index c8a7a7f2f..7368bcbc2 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.h +++ b/Telegram/SourceFiles/boxes/abstractbox.h @@ -105,8 +105,7 @@ public: ScrollableBox(const style::flatScroll &scroll, int w = st::boxWideWidth); protected: - void init(QWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight); - void initOwned(QWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight); + void init(ScrolledWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight); void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp index 9f2700dea..181ff3d49 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.cpp +++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp @@ -27,11 +27,41 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/chat_background.h" #include "styles/style_overview.h" -BackgroundInner::BackgroundInner() : -_bgCount(0), _rows(0), _over(-1), _overDown(-1) { +BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll) +, _inner(this) { + init(_inner); + + connect(_inner, SIGNAL(backgroundChosen(int)), this, SLOT(onBackgroundChosen(int))); + + prepare(); +} + +void BackgroundBox::paintEvent(QPaintEvent *e) { + Painter p(this); + if (paint(p)) return; + + paintTitle(p, lang(lng_backgrounds_header)); +} + +void BackgroundBox::onBackgroundChosen(int index) { + if (index >= 0 && index < App::cServerBackgrounds().size()) { + const App::WallPaper &paper(App::cServerBackgrounds().at(index)); + if (App::main()) App::main()->setChatBackground(paper); + + using Update = Window::ChatBackgroundUpdate; + Window::chatBackground()->notify(Update(Update::Type::Start, !paper.id)); + } + onClose(); +} + +BackgroundBox::Inner::Inner(QWidget *parent) : ScrolledWidget(parent) +, _bgCount(0) +, _rows(0) +, _over(-1) +, _overDown(-1) { if (App::cServerBackgrounds().isEmpty()) { resize(BackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding); - MTP::send(MTPaccount_GetWallPapers(), rpcDone(&BackgroundInner::gotWallpapers)); + MTP::send(MTPaccount_GetWallPapers(), rpcDone(&Inner::gotWallpapers)); } else { updateWallpapers(); } @@ -40,7 +70,7 @@ _bgCount(0), _rows(0), _over(-1), _overDown(-1) { setMouseTracking(true); } -void BackgroundInner::gotWallpapers(const MTPVector &result) { +void BackgroundBox::Inner::gotWallpapers(const MTPVector &result) { App::WallPapers wallpapers; wallpapers.push_back(App::WallPaper(0, ImagePtr(st::msgBG0), ImagePtr(st::msgBG0))); @@ -98,7 +128,7 @@ void BackgroundInner::gotWallpapers(const MTPVector &result) { updateWallpapers(); } -void BackgroundInner::updateWallpapers() { +void BackgroundBox::Inner::updateWallpapers() { _bgCount = App::cServerBackgrounds().size(); _rows = _bgCount / BackgroundsInRow; if (_bgCount % BackgroundsInRow) ++_rows; @@ -111,7 +141,7 @@ void BackgroundInner::updateWallpapers() { } } -void BackgroundInner::paintEvent(QPaintEvent *e) { +void BackgroundBox::Inner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); Painter p(this); @@ -145,7 +175,7 @@ void BackgroundInner::paintEvent(QPaintEvent *e) { } } -void BackgroundInner::mouseMoveEvent(QMouseEvent *e) { +void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) { int x = e->pos().x(), y = e->pos().y(); int row = int((y - st::backgroundPadding) / (st::backgroundSize.height() + st::backgroundPadding)); if (y - row * (st::backgroundSize.height() + st::backgroundPadding) > st::backgroundPadding + st::backgroundSize.height()) row = _rows + 1; @@ -161,11 +191,11 @@ void BackgroundInner::mouseMoveEvent(QMouseEvent *e) { } } -void BackgroundInner::mousePressEvent(QMouseEvent *e) { +void BackgroundBox::Inner::mousePressEvent(QMouseEvent *e) { _overDown = _over; } -void BackgroundInner::mouseReleaseEvent(QMouseEvent *e) { +void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) { if (_overDown == _over && _over >= 0) { emit backgroundChosen(_over); } else if (_over < 0) { @@ -173,33 +203,5 @@ void BackgroundInner::mouseReleaseEvent(QMouseEvent *e) { } } -void BackgroundInner::resizeEvent(QResizeEvent *e) { -} - -BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll) -, _inner() { - - init(&_inner); - - connect(&_inner, SIGNAL(backgroundChosen(int)), this, SLOT(onBackgroundChosen(int))); - - prepare(); -} - -void BackgroundBox::paintEvent(QPaintEvent *e) { - Painter p(this); - if (paint(p)) return; - - paintTitle(p, lang(lng_backgrounds_header)); -} - -void BackgroundBox::onBackgroundChosen(int index) { - if (index >= 0 && index < App::cServerBackgrounds().size()) { - const App::WallPaper &paper(App::cServerBackgrounds().at(index)); - if (App::main()) App::main()->setChatBackground(paper); - - using Update = Window::ChatBackgroundUpdate; - Window::chatBackground()->notify(Update(Update::Type::Start, !paper.id)); - } - onClose(); +void BackgroundBox::Inner::resizeEvent(QResizeEvent *e) { } diff --git a/Telegram/SourceFiles/boxes/backgroundbox.h b/Telegram/SourceFiles/boxes/backgroundbox.h index ebdcab053..798593658 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.h +++ b/Telegram/SourceFiles/boxes/backgroundbox.h @@ -23,11 +23,30 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" #include "core/lambda_wrap.h" -class BackgroundInner : public TWidget, public RPCSender, private base::Subscriber { +class BackgroundBox : public ItemListBox { Q_OBJECT public: - BackgroundInner(); + BackgroundBox(); + +public slots: + void onBackgroundChosen(int index); + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + class Inner; + ChildWidget _inner; + +}; + +// This class is hold in header because it requires Qt preprocessing. +class BackgroundBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { + Q_OBJECT + +public: + Inner(QWidget *parent); signals: void backgroundChosen(int index); @@ -47,20 +66,3 @@ private: int32 _over, _overDown; }; - -class BackgroundBox : public ItemListBox { - Q_OBJECT - -public: - BackgroundBox(); - -public slots: - void onBackgroundChosen(int index); - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - BackgroundInner _inner; - -}; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 1a6e30fed..72c27d234 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -68,6 +68,38 @@ aboutRevokePublicLabel: flatLabel(labelDefFlat) { textFg: windowTextFg; } +boxSearchField: InputField(defaultInputField) { + textMargins: margins(41px, 16px, 41px, 0px); + + placeholderFg: #999; + placeholderFgActive: #aaa; + placeholderMargins: margins(4px, 0px, 4px, 0px); + + border: 0px; + borderActive: 0px; + borderError: 0px; + + height: 48px; + + iconSprite: sprite(227px, 21px, 24px, 24px); + iconPosition: point(15px, 14px); + + font: normalFont; +} +boxSearchCancel: IconButton { + width: 41px; + height: 48px; + + opacity: 0.3; + overOpacity: 0.4; + + icon: icon {{ "box_search_cancel", #000000 }}; + iconPosition: point(8px, 18px); + downIconPosition: point(8px, 18px); + + duration: 150; +} + contactsPhotoCheckbox: RoundImageCheckbox { imageRadius: 21px; imageSmallRadius: 18px; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index ab1470576..41915c6a2 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "application.h" #include "ui/filedialog.h" +#include "ui/buttons/icon_button.h" #include "boxes/photocropbox.h" #include "boxes/confirmbox.h" #include "observer_peer.h" @@ -39,15 +40,481 @@ QString cantInviteError() { return lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.me/spambot"), lang(lng_cant_more_info))); } -ContactsInner::ContactData::ContactData() : name(st::boxWideWidth) { +ContactsBox::ContactsBox() : ItemListBox(st::contactsScroll) +, _inner(this, CreatingGroupNone) +, _filter(this, st::boxSearchField, lang(lng_participant_filter)) +, _filterCancel(this, st::boxSearchCancel) +, _next(this, lang(lng_create_group_next), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _topShadow(this) { + init(); } -ContactsInner::ContactData::ContactData(PeerData *peer, Ui::RoundImageCheckbox::UpdateCallback &&updateCallback) +ContactsBox::ContactsBox(const QString &name, const QImage &photo) : ItemListBox(st::boxScroll) +, _inner(this, CreatingGroupGroup) +, _filter(this, st::boxSearchField, lang(lng_participant_filter)) +, _filterCancel(this, st::boxSearchCancel) +, _next(this, lang(lng_create_group_create), st::defaultBoxButton) +, _cancel(this, lang(lng_create_group_back), st::cancelBoxButton) +, _topShadow(this) +, _creationName(name) +, _creationPhoto(photo) { + init(); +} + +ContactsBox::ContactsBox(ChannelData *channel) : ItemListBox(st::boxScroll) +, _inner(this, channel, MembersFilter::Recent, MembersAlreadyIn()) +, _filter(this, st::boxSearchField, lang(lng_participant_filter)) +, _filterCancel(this, st::boxSearchCancel) +, _next(this, lang(lng_participant_invite), st::defaultBoxButton) +, _cancel(this, lang(lng_create_group_skip), st::cancelBoxButton) +, _topShadow(this) { + init(); +} + +ContactsBox::ContactsBox(ChannelData *channel, MembersFilter filter, const MembersAlreadyIn &already) : ItemListBox((filter == MembersFilter::Admins) ? st::contactsScroll : st::boxScroll) +, _inner(this, channel, filter, already) +, _filter(this, st::boxSearchField, lang(lng_participant_filter)) +, _filterCancel(this, st::boxSearchCancel) +, _next(this, lang(lng_participant_invite), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _topShadow(this) { + init(); +} + +ContactsBox::ContactsBox(ChatData *chat, MembersFilter filter) : ItemListBox(st::boxScroll) +, _inner(this, chat, filter) +, _filter(this, st::boxSearchField, lang(lng_participant_filter)) +, _filterCancel(this, st::boxSearchCancel) +, _next(this, lang((filter == MembersFilter::Admins) ? lng_settings_save : lng_participant_invite), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _topShadow(this) { + init(); +} + +ContactsBox::ContactsBox(UserData *bot) : ItemListBox(st::contactsScroll) +, _inner(this, bot) +, _filter(this, st::boxSearchField, lang(lng_participant_filter)) +, _filterCancel(this, st::boxSearchCancel) +, _next(this, lang(lng_create_group_next), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _topShadow(this) { + init(); +} + +void ContactsBox::init() { + bool inviting = (_inner->creating() == CreatingGroupGroup) || (_inner->channel() && _inner->membersFilter() == MembersFilter::Recent) || _inner->chat(); + int32 topSkip = st::boxTitleHeight + _filter->height(); + int32 bottomSkip = inviting ? (st::boxButtonPadding.top() + _next.height() + st::boxButtonPadding.bottom()) : st::boxScrollSkip; + ItemListBox::init(_inner, bottomSkip, topSkip); + + connect(_inner, SIGNAL(chosenChanged()), this, SLOT(onChosenChanged())); + connect(_inner, SIGNAL(addRequested()), App::wnd(), SLOT(onShowAddContact())); + if (_inner->channel() && _inner->membersFilter() == MembersFilter::Admins) { + _next.hide(); + _cancel.hide(); + } else if (_inner->chat() && _inner->membersFilter() == MembersFilter::Admins) { + connect(&_next, SIGNAL(clicked()), this, SLOT(onSaveAdmins())); + _bottomShadow = new ScrollableBoxShadow(this); + } else if (_inner->chat() || _inner->channel()) { + connect(&_next, SIGNAL(clicked()), this, SLOT(onInvite())); + _bottomShadow = new ScrollableBoxShadow(this); + } else if (_inner->creating() != CreatingGroupNone) { + connect(&_next, SIGNAL(clicked()), this, SLOT(onCreate())); + _bottomShadow = new ScrollableBoxShadow(this); + } else { + _next.hide(); + _cancel.hide(); + } + connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); + connect(_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate())); + connect(_filter, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_filterCancel, SIGNAL(clicked()), this, SLOT(onFilterCancel())); + connect(_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int))); + connect(_inner, SIGNAL(selectAllQuery()), _filter, SLOT(selectAll())); + connect(_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername())); + connect(_inner, SIGNAL(adminAdded()), this, SIGNAL(adminAdded())); + + _filterCancel->setAttribute(Qt::WA_OpaquePaintEvent); + + _searchTimer.setSingleShot(true); + connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchByUsername())); + + prepare(); +} + +bool ContactsBox::onSearchByUsername(bool searchCache) { + QString q = _filter->getLastText().trimmed(); + if (q.isEmpty()) { + if (_peopleRequest) { + _peopleRequest = 0; + } + return true; + } + if (q.size() >= MinUsernameLength) { + if (searchCache) { + PeopleCache::const_iterator i = _peopleCache.constFind(q); + if (i != _peopleCache.cend()) { + _peopleQuery = q; + _peopleRequest = 0; + peopleReceived(i.value(), 0); + return true; + } + } else if (_peopleQuery != q) { + _peopleQuery = q; + _peopleFull = false; + _peopleRequest = MTP::send(MTPcontacts_Search(MTP_string(_peopleQuery), MTP_int(SearchPeopleLimit)), rpcDone(&ContactsBox::peopleReceived), rpcFail(&ContactsBox::peopleFailed)); + _peopleQueries.insert(_peopleRequest, _peopleQuery); + } + } + return false; +} + +void ContactsBox::onNeedSearchByUsername() { + if (!onSearchByUsername(true)) { + _searchTimer.start(AutoSearchTimeout); + } +} + +void ContactsBox::peopleReceived(const MTPcontacts_Found &result, mtpRequestId req) { + QString q = _peopleQuery; + + PeopleQueries::iterator i = _peopleQueries.find(req); + if (i != _peopleQueries.cend()) { + q = i.value(); + _peopleCache[q] = result; + _peopleQueries.erase(i); + } + + if (_peopleRequest == req) { + switch (result.type()) { + case mtpc_contacts_found: { + App::feedUsers(result.c_contacts_found().vusers); + App::feedChats(result.c_contacts_found().vchats); + _inner->peopleReceived(q, result.c_contacts_found().vresults.c_vector().v); + } break; + } + + _peopleRequest = 0; + _inner->updateSel(); + onScroll(); + } +} + +bool ContactsBox::peopleFailed(const RPCError &error, mtpRequestId req) { + if (MTP::isDefaultHandledError(error)) return false; + + if (_peopleRequest == req) { + _peopleRequest = 0; + _peopleFull = true; + } + return true; +} + +void ContactsBox::showAll() { + _filter->show(); + if (_filter->getLastText().isEmpty()) { + _filterCancel->hide(); + } else { + _filterCancel->show(); + } + if (_inner->channel() && _inner->membersFilter() == MembersFilter::Admins) { + _next.hide(); + _cancel.hide(); + } else if (_inner->chat() || _inner->channel()) { + _next.show(); + _cancel.show(); + } else if (_inner->creating() != CreatingGroupNone) { + _next.show(); + _cancel.show(); + } else { + _next.hide(); + _cancel.hide(); + } + _topShadow.show(); + if (_bottomShadow) _bottomShadow->show(); + ItemListBox::showAll(); +} + +void ContactsBox::doSetInnerFocus() { + _filter->setFocus(); +} + +void ContactsBox::onSubmit() { + _inner->chooseParticipant(); +} + +void ContactsBox::keyPressEvent(QKeyEvent *e) { + if (_filter->hasFocus()) { + if (e->key() == Qt::Key_Down) { + _inner->selectSkip(1); + } else if (e->key() == Qt::Key_Up) { + _inner->selectSkip(-1); + } else if (e->key() == Qt::Key_PageDown) { + _inner->selectSkipPage(scrollArea()->height(), 1); + } else if (e->key() == Qt::Key_PageUp) { + _inner->selectSkipPage(scrollArea()->height(), -1); + } else { + ItemListBox::keyPressEvent(e); + } + } else { + ItemListBox::keyPressEvent(e); + } +} + +void ContactsBox::paintEvent(QPaintEvent *e) { + Painter p(this); + if (paint(p)) return; + + bool addingAdmin = _inner->channel() && _inner->membersFilter() == MembersFilter::Admins; + if (_inner->chat() && _inner->membersFilter() == MembersFilter::Admins) { + paintTitle(p, lang(lng_channel_admins)); + } else if (_inner->chat() || _inner->creating() != CreatingGroupNone) { + QString title(lang(addingAdmin ? lng_channel_add_admin : lng_profile_add_participant)); + QString additional((addingAdmin || (_inner->channel() && !_inner->channel()->isMegagroup())) ? QString() : QString("%1 / %2").arg(_inner->selectedCount()).arg(Global::MegagroupSizeMax())); + paintTitle(p, title, additional); + } else if (_inner->sharingBotGame()) { + paintTitle(p, lang(lng_bot_choose_chat)); + } else if (_inner->bot()) { + paintTitle(p, lang(lng_bot_choose_group)); + } else { + paintTitle(p, lang(lng_contacts_header)); + } +} + +void ContactsBox::resizeEvent(QResizeEvent *e) { + ItemListBox::resizeEvent(e); + _filter->resize(width(), _filter->height()); + _filter->moveToLeft(0, st::boxTitleHeight); + _filterCancel->moveToRight(0, st::boxTitleHeight); + _inner->resize(width(), _inner->height()); + _next.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next.height()); + _cancel.moveToRight(st::boxButtonPadding.right() + _next.width() + st::boxButtonPadding.left(), _next.y()); + _topShadow.setGeometry(0, st::boxTitleHeight + _filter->height(), width(), st::lineWidth); + if (_bottomShadow) _bottomShadow->setGeometry(0, height() - st::boxButtonPadding.bottom() - _next.height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); +} + +void ContactsBox::closePressed() { + if (_inner->channel() && !_inner->hasAlreadyMembersInChannel()) { + Ui::showPeerHistory(_inner->channel(), ShowAtTheEndMsgId); + } +} + +void ContactsBox::onFilterCancel() { + _filter->setText(QString()); +} + +void ContactsBox::onFilterUpdate() { + scrollArea()->scrollToY(0); + if (_filter->getLastText().isEmpty()) { + _filterCancel->hide(); + } else { + _filterCancel->show(); + } + _inner->updateFilter(_filter->getLastText()); +} + +void ContactsBox::onChosenChanged() { + update(); +} + +void ContactsBox::onInvite() { + QVector users(_inner->selected()); + if (users.isEmpty()) { + _filter->setFocus(); + _filter->showError(); + return; + } + + App::main()->addParticipants(_inner->chat() ? (PeerData*)_inner->chat() : _inner->channel(), users); + if (_inner->chat()) { + Ui::hideLayer(); + Ui::showPeerHistory(_inner->chat(), ShowAtTheEndMsgId); + } else { + onClose(); + } +} + +void ContactsBox::onCreate() { + if (_saveRequestId) return; + + MTPVector users(MTP_vector(_inner->selectedInputs())); + const auto &v(users.c_vector().v); + if (v.isEmpty() || (v.size() == 1 && v.at(0).type() == mtpc_inputUserSelf)) { + _filter->setFocus(); + _filter->showError(); + return; + } + _saveRequestId = MTP::send(MTPmessages_CreateChat(MTP_vector(v), MTP_string(_creationName)), rpcDone(&ContactsBox::creationDone), rpcFail(&ContactsBox::creationFail)); +} + +void ContactsBox::onSaveAdmins() { + if (_saveRequestId) return; + + _inner->saving(true); + _saveRequestId = MTP::send(MTPmessages_ToggleChatAdmins(_inner->chat()->inputChat, MTP_bool(!_inner->allAdmins())), rpcDone(&ContactsBox::saveAdminsDone), rpcFail(&ContactsBox::saveAdminsFail)); +} + +void ContactsBox::saveAdminsDone(const MTPUpdates &result) { + App::main()->sentUpdatesReceived(result); + saveSelectedAdmins(); +} + +void ContactsBox::saveSelectedAdmins() { + if (_inner->allAdmins() && !_inner->chat()->participants.isEmpty()) { + onClose(); + } else { + _saveRequestId = MTP::send(MTPmessages_GetFullChat(_inner->chat()->inputChat), rpcDone(&ContactsBox::getAdminsDone), rpcFail(&ContactsBox::saveAdminsFail)); + } +} + +void ContactsBox::getAdminsDone(const MTPmessages_ChatFull &result) { + App::api()->processFullPeer(_inner->chat(), result); + if (_inner->allAdmins()) { + onClose(); + return; + } + ChatData::Admins curadmins = _inner->chat()->admins; + QVector newadmins = _inner->selected(), appoint; + if (!newadmins.isEmpty()) { + appoint.reserve(newadmins.size()); + for (int32 i = 0, l = newadmins.size(); i < l; ++i) { + ChatData::Admins::iterator c = curadmins.find(newadmins.at(i)); + if (c == curadmins.cend()) { + if (newadmins.at(i)->id != peerFromUser(_inner->chat()->creator)) { + appoint.push_back(newadmins.at(i)); + } + } else { + curadmins.erase(c); + } + } + } + _saveRequestId = 0; + + for_const (UserData *user, curadmins) { + MTP::send(MTPmessages_EditChatAdmin(_inner->chat()->inputChat, user->inputUser, MTP_boolFalse()), rpcDone(&ContactsBox::removeAdminDone, user), rpcFail(&ContactsBox::editAdminFail), 0, 10); + } + for_const (UserData *user, appoint) { + MTP::send(MTPmessages_EditChatAdmin(_inner->chat()->inputChat, user->inputUser, MTP_boolTrue()), rpcDone(&ContactsBox::setAdminDone, user), rpcFail(&ContactsBox::editAdminFail), 0, 10); + } + MTP::sendAnything(); + + _saveRequestId = curadmins.size() + appoint.size(); + if (!_saveRequestId) { + onClose(); + } +} + +void ContactsBox::setAdminDone(UserData *user, const MTPBool &result) { + if (mtpIsTrue(result)) { + if (_inner->chat()->noParticipantInfo()) { + App::api()->requestFullPeer(_inner->chat()); + } else { + _inner->chat()->admins.insert(user); + } + } + --_saveRequestId; + if (!_saveRequestId) { + emit App::main()->peerUpdated(_inner->chat()); + onClose(); + } +} + +void ContactsBox::removeAdminDone(UserData *user, const MTPBool &result) { + if (mtpIsTrue(result)) { + _inner->chat()->admins.remove(user); + } + --_saveRequestId; + if (!_saveRequestId) { + emit App::main()->peerUpdated(_inner->chat()); + onClose(); + } +} + +bool ContactsBox::saveAdminsFail(const RPCError &error) { + if (MTP::isDefaultHandledError(error)) return true; + _saveRequestId = 0; + _inner->saving(false); + if (error.type() == qstr("CHAT_NOT_MODIFIED")) { + saveSelectedAdmins(); + } + return false; +} + +bool ContactsBox::editAdminFail(const RPCError &error) { + if (MTP::isDefaultHandledError(error)) return true; + --_saveRequestId; + _inner->chat()->invalidateParticipants(); + if (!_saveRequestId) { + if (error.type() == qstr("USER_RESTRICTED")) { + Ui::showLayer(new InformBox(lang(lng_cant_do_this))); + return true; + } + onClose(); + } + return false; +} + +void ContactsBox::onScroll() { + _inner->loadProfilePhotos(scrollArea()->scrollTop()); +} + +void ContactsBox::creationDone(const MTPUpdates &updates) { + Ui::hideLayer(); + + App::main()->sentUpdatesReceived(updates); + const QVector *v = 0; + switch (updates.type()) { + case mtpc_updates: v = &updates.c_updates().vchats.c_vector().v; break; + case mtpc_updatesCombined: v = &updates.c_updatesCombined().vchats.c_vector().v; break; + default: LOG(("API Error: unexpected update cons %1 (ContactsBox::creationDone)").arg(updates.type())); break; + } + + PeerData *peer = 0; + if (v && !v->isEmpty() && v->front().type() == mtpc_chat) { + peer = App::chat(v->front().c_chat().vid.v); + if (peer) { + if (!_creationPhoto.isNull()) { + App::app()->uploadProfilePhoto(_creationPhoto, peer->id); + } + Ui::showPeerHistory(peer, ShowAtUnreadMsgId); + } + } else { + LOG(("API Error: chat not found in updates (ContactsBox::creationDone)")); + } +} + +bool ContactsBox::creationFail(const RPCError &error) { + if (MTP::isDefaultHandledError(error)) return false; + + _saveRequestId = 0; + if (error.type() == "NO_CHAT_TITLE") { + onClose(); + return true; + } else if (error.type() == "USERS_TOO_FEW") { + _filter->setFocus(); + _filter->showError(); + return true; + } else if (error.type() == "PEER_FLOOD") { + Ui::showLayer(new InformBox(cantInviteError()), KeepOtherLayers); + return true; + } else if (error.type() == qstr("USER_RESTRICTED")) { + Ui::showLayer(new InformBox(lang(lng_cant_do_this))); + return true; + } + return false; +} + +ContactsBox::Inner::ContactData::ContactData() : name(st::boxWideWidth) { +} + +ContactsBox::Inner::ContactData::ContactData(PeerData *peer, Ui::RoundImageCheckbox::UpdateCallback &&updateCallback) : checkbox(std_::make_unique(st::contactsPhotoCheckbox, std_::move(updateCallback), PaintUserpicCallback(peer))) , name(st::boxWideWidth) { } -ContactsInner::ContactsInner(CreatingGroupType creating) : TWidget() +ContactsBox::Inner::Inner(QWidget *parent, CreatingGroupType creating) : ScrolledWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _newItemHeight(creating == CreatingGroupNone ? st::contactsNewItemHeight : 0) , _creating(creating) @@ -57,7 +524,7 @@ ContactsInner::ContactsInner(CreatingGroupType creating) : TWidget() init(); } -ContactsInner::ContactsInner(ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already) : TWidget() +ContactsBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already) : ScrolledWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _channel(channel) , _membersFilter(membersFilter) @@ -75,7 +542,7 @@ namespace { } } -ContactsInner::ContactsInner(ChatData *chat, MembersFilter membersFilter) : TWidget() +ContactsBox::Inner::Inner(QWidget *parent, ChatData *chat, MembersFilter membersFilter) : ScrolledWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _chat(chat) , _membersFilter(membersFilter) @@ -83,11 +550,11 @@ ContactsInner::ContactsInner(ChatData *chat, MembersFilter membersFilter) : TWid , _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right()) , _aboutAllAdmins(st::boxTextFont, lang(lng_chat_about_all_admins), _defaultOptions, _aboutWidth) , _aboutAdmins(st::boxTextFont, lang(lng_chat_about_admins), _defaultOptions, _aboutWidth) -, _customList((membersFilter == MembersFilterRecent) ? std_::unique_ptr() : std_::make_unique(Dialogs::SortMode::Add)) -, _contacts((membersFilter == MembersFilterRecent) ? App::main()->contactsList() : _customList.get()) +, _customList((membersFilter == MembersFilter::Recent) ? std_::unique_ptr() : std_::make_unique(Dialogs::SortMode::Add)) +, _contacts((membersFilter == MembersFilter::Recent) ? App::main()->contactsList() : _customList.get()) , _addContactLnk(this, lang(lng_add_contact_button)) { initList(); - if (membersFilter == MembersFilterAdmins) { + if (membersFilter == MembersFilter::Admins) { _newItemHeight = st::contactsNewItemHeight + qMax(_aboutAllAdmins.countHeight(_aboutWidth), _aboutAdmins.countHeight(_aboutWidth)) + st::contactsAboutHeight; if (_contacts->isEmpty()) { App::api()->requestFullPeer(_chat); @@ -97,7 +564,7 @@ ContactsInner::ContactsInner(ChatData *chat, MembersFilter membersFilter) : TWid } template -void ContactsInner::addDialogsToList(FilterCallback callback) { +void ContactsBox::Inner::addDialogsToList(FilterCallback callback) { auto v = App::main()->dialogsList(); for_const (auto row, *v) { auto peer = row->history()->peer; @@ -107,7 +574,7 @@ void ContactsInner::addDialogsToList(FilterCallback callback) { } } -ContactsInner::ContactsInner(UserData *bot) : TWidget() +ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : ScrolledWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _bot(bot) , _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox) @@ -137,7 +604,7 @@ ContactsInner::ContactsInner(UserData *bot) : TWidget() init(); } -void ContactsInner::init() { +void ContactsBox::Inner::init() { subscribe(FileDownload::ImageLoaded(), [this] { update(); }); connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); connect(&_allAdmins, SIGNAL(changed()), this, SLOT(onAllAdminsChanged())); @@ -157,7 +624,7 @@ void ContactsInner::init() { connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*))); } -void ContactsInner::initList() { +void ContactsBox::Inner::initList() { if (!usingMultiSelect()) return; QList admins, others; @@ -190,14 +657,14 @@ void ContactsInner::initList() { } } -void ContactsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { +void ContactsBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { if (bot()) { _contacts->peerNameChanged(peer, oldNames, oldChars); } peerUpdated(peer); } -void ContactsInner::onAddBot() { +void ContactsBox::Inner::onAddBot() { if (auto &info = _bot->botInfo) { if (!info->shareGameShortName.isEmpty()) { MTPmessages_SendMedia::Flags sendFlags = 0; @@ -221,18 +688,18 @@ void ContactsInner::onAddBot() { Ui::showPeerHistory(_addToPeer, ShowAtUnreadMsgId); } -void ContactsInner::onAddAdmin() { +void ContactsBox::Inner::onAddAdmin() { if (_addAdminRequestId) return; - _addAdminRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _addAdmin->inputUser, MTP_channelRoleEditor()), rpcDone(&ContactsInner::addAdminDone), rpcFail(&ContactsInner::addAdminFail)); + _addAdminRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _addAdmin->inputUser, MTP_channelRoleEditor()), rpcDone(&Inner::addAdminDone), rpcFail(&Inner::addAdminFail)); } -void ContactsInner::onNoAddAdminBox(QObject *obj) { +void ContactsBox::Inner::onNoAddAdminBox(QObject *obj) { if (obj == _addAdminBox) { _addAdminBox = 0; } } -void ContactsInner::onAllAdminsChanged() { +void ContactsBox::Inner::onAllAdminsChanged() { if (_saving) { if (_allAdmins.checked() != _allAdminsChecked) { _allAdmins.setChecked(_allAdminsChecked); @@ -241,7 +708,7 @@ void ContactsInner::onAllAdminsChanged() { update(); } -void ContactsInner::addAdminDone(const MTPUpdates &result, mtpRequestId req) { +void ContactsBox::Inner::addAdminDone(const MTPUpdates &result, mtpRequestId req) { if (App::main()) App::main()->sentUpdatesReceived(result); if (req != _addAdminRequestId) return; @@ -266,7 +733,7 @@ void ContactsInner::addAdminDone(const MTPUpdates &result, mtpRequestId req) { emit adminAdded(); } -bool ContactsInner::addAdminFail(const RPCError &error, mtpRequestId req) { +bool ContactsBox::Inner::addAdminFail(const RPCError &error, mtpRequestId req) { if (MTP::isDefaultHandledError(error)) return false; if (req != _addAdminRequestId) return true; @@ -285,16 +752,16 @@ bool ContactsInner::addAdminFail(const RPCError &error, mtpRequestId req) { return true; } -void ContactsInner::saving(bool flag) { +void ContactsBox::Inner::saving(bool flag) { _saving = flag; _allAdminsChecked = _allAdmins.checked(); update(); } -void ContactsInner::peerUpdated(PeerData *peer) { +void ContactsBox::Inner::peerUpdated(PeerData *peer) { if (_chat && (!peer || peer == _chat)) { bool inited = false; - if (_membersFilter == MembersFilterAdmins && _contacts->isEmpty() && !_chat->participants.isEmpty()) { + if (_membersFilter == MembersFilter::Admins && _contacts->isEmpty() && !_chat->participants.isEmpty()) { initList(); inited = true; } @@ -342,7 +809,7 @@ void ContactsInner::peerUpdated(PeerData *peer) { } } -void ContactsInner::loadProfilePhotos(int32 yFrom) { +void ContactsBox::Inner::loadProfilePhotos(int32 yFrom) { int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5; MTP::clearLoaderPriorities(); @@ -373,7 +840,7 @@ void ContactsInner::loadProfilePhotos(int32 yFrom) { } } -ContactsInner::ContactData *ContactsInner::contactData(Dialogs::Row *row) { +ContactsBox::Inner::ContactData *ContactsBox::Inner::contactData(Dialogs::Row *row) { ContactData *data = (ContactData*)row->attached; if (!data) { PeerData *peer = row->history()->peer; @@ -383,7 +850,7 @@ ContactsInner::ContactData *ContactsInner::contactData(Dialogs::Row *row) { _contactsData.insert(peer, data); if (peer->isUser()) { if (_chat) { - if (_membersFilter == MembersFilterRecent) { + if (_membersFilter == MembersFilter::Recent) { data->disabledChecked = _chat->participants.contains(peer->asUser()); } } else if (_creating == CreatingGroupGroup) { @@ -419,10 +886,10 @@ ContactsInner::ContactData *ContactsInner::contactData(Dialogs::Row *row) { return data; } -void ContactsInner::paintDialog(Painter &p, PeerData *peer, ContactData *data, bool sel) { +void ContactsBox::Inner::paintDialog(Painter &p, PeerData *peer, ContactData *data, bool sel) { UserData *user = peer->asUser(); - if (_chat && _membersFilter == MembersFilterAdmins) { + if (_chat && _membersFilter == MembersFilter::Admins) { if (_allAdmins.checked() || peer->id == peerFromUser(_chat->creator) || _saving) { sel = false; } @@ -433,7 +900,7 @@ void ContactsInner::paintDialog(Painter &p, PeerData *peer, ContactData *data, b } auto paintDisabledCheck = data->disabledChecked; - if (_chat && _membersFilter == MembersFilterAdmins) { + if (_chat && _membersFilter == MembersFilter::Admins) { if (peer->id == peerFromUser(_chat->creator) || _allAdmins.checked()) { paintDisabledCheck = true; } @@ -496,7 +963,7 @@ void ContactsInner::paintDialog(Painter &p, PeerData *peer, ContactData *data, b } // Emulates Ui::RoundImageCheckbox::paint() in a checked state. -void ContactsInner::paintDisabledCheckUserpic(Painter &p, PeerData *peer, int x, int y, int outerWidth) const { +void ContactsBox::Inner::paintDisabledCheckUserpic(Painter &p, PeerData *peer, int x, int y, int outerWidth) const { auto userpicRadius = st::contactsPhotoCheckbox.imageSmallRadius; auto userpicShift = st::contactsPhotoCheckbox.imageRadius - userpicRadius; auto userpicDiameter = st::contactsPhotoCheckbox.imageRadius * 2; @@ -530,7 +997,7 @@ void ContactsInner::paintDisabledCheckUserpic(Painter &p, PeerData *peer, int x, st::contactsPhotoCheckbox.checkIcon.paint(p, iconEllipse.topLeft(), outerWidth); } -void ContactsInner::paintEvent(QPaintEvent *e) { +void ContactsBox::Inner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); Painter p(this); @@ -596,7 +1063,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) { int32 skip = 0; if (bot()) { text = lang((cDialogsReceived() && !_searching) ? (sharingBotGame() ? lng_bot_no_chats : lng_bot_no_groups) : lng_contacts_loading); - } else if (_chat && _membersFilter == MembersFilterAdmins) { + } else if (_chat && _membersFilter == MembersFilter::Admins) { text = lang(lng_contacts_loading); p.fillRect(0, 0, width(), _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg); p.fillRect(0, _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, width(), st::lineWidth, st::shadowColor); @@ -622,7 +1089,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) { QString text; if (bot()) { text = lang((cDialogsReceived() && !_searching) ? (sharingBotGame() ? lng_bot_chats_not_found : lng_bot_groups_not_found) : lng_contacts_loading); - } else if (_chat && _membersFilter == MembersFilterAdmins) { + } else if (_chat && _membersFilter == MembersFilter::Admins) { text = lang(_chat->participants.isEmpty() ? lng_contacts_loading : lng_contacts_not_found); } else { text = lang((cContactsReceived() && !_searching) ? lng_contacts_not_found : lng_contacts_loading); @@ -659,11 +1126,11 @@ void ContactsInner::paintEvent(QPaintEvent *e) { } } -void ContactsInner::enterEvent(QEvent *e) { +void ContactsBox::Inner::enterEvent(QEvent *e) { setMouseTracking(true); } -int ContactsInner::getSelectedRowTop() const { +int ContactsBox::Inner::getSelectedRowTop() const { if (_filter.isEmpty()) { if (_sel) { return _newItemHeight + (_sel->pos() * _rowHeight); @@ -680,7 +1147,7 @@ int ContactsInner::getSelectedRowTop() const { return -1; } -void ContactsInner::updateSelectedRow() { +void ContactsBox::Inner::updateSelectedRow() { if (_filter.isEmpty() && _newItemSel) { update(0, 0, width(), st::contactsNewItemHeight); } else { @@ -691,11 +1158,11 @@ void ContactsInner::updateSelectedRow() { } } -void ContactsInner::updateRowWithTop(int rowTop) { +void ContactsBox::Inner::updateRowWithTop(int rowTop) { update(0, rowTop, width(), _rowHeight); } -int ContactsInner::getRowTopWithPeer(PeerData *peer) const { +int ContactsBox::Inner::getRowTopWithPeer(PeerData *peer) const { if (_filter.isEmpty()) { for (auto i = _contacts->cbegin(), end = _contacts->cend(); i != end; ++i) { if ((*i)->history()->peer == peer) { @@ -722,14 +1189,14 @@ int ContactsInner::getRowTopWithPeer(PeerData *peer) const { return -1; } -void ContactsInner::updateRowWithPeer(PeerData *peer) { +void ContactsBox::Inner::updateRowWithPeer(PeerData *peer) { auto rowTop = getRowTopWithPeer(peer); if (rowTop >= 0) { updateRowWithTop(rowTop); } } -void ContactsInner::leaveEvent(QEvent *e) { +void ContactsBox::Inner::leaveEvent(QEvent *e) { _mouseSel = false; setMouseTracking(false); if (_newItemSel || _sel || _filteredSel >= 0 || _byUsernameSel >= 0) { @@ -740,13 +1207,13 @@ void ContactsInner::leaveEvent(QEvent *e) { } } -void ContactsInner::mouseMoveEvent(QMouseEvent *e) { +void ContactsBox::Inner::mouseMoveEvent(QMouseEvent *e) { _mouseSel = true; _lastMousePos = e->globalPos(); updateSel(); } -void ContactsInner::mousePressEvent(QMouseEvent *e) { +void ContactsBox::Inner::mousePressEvent(QMouseEvent *e) { _mouseSel = true; _lastMousePos = e->globalPos(); updateSel(); @@ -755,9 +1222,9 @@ void ContactsInner::mousePressEvent(QMouseEvent *e) { } } -void ContactsInner::chooseParticipant() { +void ContactsBox::Inner::chooseParticipant() { if (_saving) return; - bool addingAdmin = (_channel && _membersFilter == MembersFilterAdmins); + bool addingAdmin = (_channel && _membersFilter == MembersFilter::Admins); if (!addingAdmin && usingMultiSelect()) { _time = unixtime(); if (_filter.isEmpty()) { @@ -855,11 +1322,11 @@ void ContactsInner::chooseParticipant() { update(); } -void ContactsInner::changeCheckState(Dialogs::Row *row) { +void ContactsBox::Inner::changeCheckState(Dialogs::Row *row) { changeCheckState(contactData(row), row->history()->peer); } -void ContactsInner::changeCheckState(ContactData *data, PeerData *peer) { +void ContactsBox::Inner::changeCheckState(ContactData *data, PeerData *peer) { t_assert(usingMultiSelect()); int32 cnt = _selCount; @@ -879,7 +1346,7 @@ void ContactsInner::changeCheckState(ContactData *data, PeerData *peer) { if (cnt != _selCount) emit chosenChanged(); } -int32 ContactsInner::selectedCount() const { +int32 ContactsBox::Inner::selectedCount() const { int32 result = _selCount; if (_chat) { result += qMax(_chat->count, 1); @@ -891,7 +1358,7 @@ int32 ContactsInner::selectedCount() const { return result; } -void ContactsInner::updateSel() { +void ContactsBox::Inner::updateSel() { if (!_mouseSel) return; QPoint p(mapFromGlobal(_lastMousePos)); @@ -899,7 +1366,7 @@ void ContactsInner::updateSel() { if (_filter.isEmpty()) { bool newItemSel = false; if (_newItemHeight) { - if (in && (p.y() >= 0) && (p.y() < _newItemHeight) && !(_chat && _membersFilter == MembersFilterAdmins)) { + if (in && (p.y() >= 0) && (p.y() < _newItemHeight) && !(_chat && _membersFilter == MembersFilter::Admins)) { newItemSel = true; } p.setY(p.y() - _newItemHeight); @@ -927,7 +1394,7 @@ void ContactsInner::updateSel() { } } -void ContactsInner::updateFilter(QString filter) { +void ContactsBox::Inner::updateFilter(QString filter) { _lastQuery = filter.toLower().trimmed(); filter = textSearchKey(filter); @@ -1040,7 +1507,7 @@ void ContactsInner::updateFilter(QString filter) { _mouseSel = false; refresh(); - if ((!bot() || sharingBotGame()) && (!_chat || _membersFilter != MembersFilterAdmins)) { + if ((!bot() || sharingBotGame()) && (!_chat || _membersFilter != MembersFilter::Admins)) { _searching = true; emit searchByUsername(); } @@ -1050,7 +1517,7 @@ void ContactsInner::updateFilter(QString filter) { } } -void ContactsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow) { +void ContactsBox::Inner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow) { if (!_filter.isEmpty()) { for (FilteredDialogs::iterator i = _filtered.begin(), e = _filtered.end(); i != e;) { if (*i == oldRow) { // this row is shown in filtered and maybe is in contacts! @@ -1078,7 +1545,7 @@ void ContactsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newR resize(width(), newh); } -void ContactsInner::peopleReceived(const QString &query, const QVector &people) { +void ContactsBox::Inner::peopleReceived(const QString &query, const QVector &people) { _lastQuery = query.toLower().trimmed(); if (_lastQuery.at(0) == '@') _lastQuery = _lastQuery.mid(1); int32 already = _byUsernameFiltered.size(); @@ -1101,7 +1568,7 @@ void ContactsInner::peopleReceived(const QString &query, const QVector if (peer->asUser()->botInfo->cantJoinGroups) continue; } if (_channel) { - if (!_channel->isMegagroup() && _membersFilter != MembersFilterAdmins) continue; + if (!_channel->isMegagroup() && _membersFilter != MembersFilter::Admins) continue; } } } else { @@ -1135,9 +1602,9 @@ void ContactsInner::peopleReceived(const QString &query, const QVector refresh(); } -void ContactsInner::refresh() { +void ContactsBox::Inner::refresh() { if (_filter.isEmpty()) { - if (_chat && _membersFilter == MembersFilterAdmins) { + if (_chat && _membersFilter == MembersFilter::Admins) { if (_allAdmins.isHidden()) _allAdmins.show(); } else { if (!_allAdmins.isHidden()) _allAdmins.hide(); @@ -1145,7 +1612,7 @@ void ContactsInner::refresh() { if (!_contacts->isEmpty() || !_byUsername.isEmpty()) { if (!_addContactLnk.isHidden()) _addContactLnk.hide(); resize(width(), _newItemHeight + (_contacts->size() * _rowHeight) + (_byUsername.isEmpty() ? 0 : (st::searchedBarHeight + _byUsername.size() * _rowHeight))); - } else if (_chat && _membersFilter == MembersFilterAdmins) { + } else if (_chat && _membersFilter == MembersFilter::Admins) { if (!_addContactLnk.isHidden()) _addContactLnk.hide(); resize(width(), _newItemHeight + st::noContactsHeight); } else { @@ -1168,33 +1635,33 @@ void ContactsInner::refresh() { update(); } -ChatData *ContactsInner::chat() const { +ChatData *ContactsBox::Inner::chat() const { return _chat; } -ChannelData *ContactsInner::channel() const { +ChannelData *ContactsBox::Inner::channel() const { return _channel; } -MembersFilter ContactsInner::membersFilter() const { +MembersFilter ContactsBox::Inner::membersFilter() const { return _membersFilter; } -UserData *ContactsInner::bot() const { +UserData *ContactsBox::Inner::bot() const { return _bot; } -bool ContactsInner::sharingBotGame() const { +bool ContactsBox::Inner::sharingBotGame() const { return (_bot && _bot->botInfo) ? !_bot->botInfo->shareGameShortName.isEmpty() : false; } -CreatingGroupType ContactsInner::creating() const { +CreatingGroupType ContactsBox::Inner::creating() const { return _creating; } -ContactsInner::~ContactsInner() { - for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) { - delete *i; +ContactsBox::Inner::~Inner() { + for (auto contactData : base::take(_contactsData)) { + delete contactData; } if (_bot) { if (auto &info = _bot->botInfo) { @@ -1204,12 +1671,12 @@ ContactsInner::~ContactsInner() { } } -void ContactsInner::resizeEvent(QResizeEvent *e) { +void ContactsBox::Inner::resizeEvent(QResizeEvent *e) { _addContactLnk.move((width() - _addContactLnk.width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2); _allAdmins.moveToLeft(st::contactsPadding.left(), st::contactsNewItemTop); } -void ContactsInner::selectSkip(int32 dir) { +void ContactsBox::Inner::selectSkip(int32 dir) { _time = unixtime(); _mouseSel = false; if (_filter.isEmpty()) { @@ -1227,7 +1694,7 @@ void ContactsInner::selectSkip(int32 dir) { } cur += dir; if (cur <= 0) { - _newItemSel = (_chat && _membersFilter == MembersFilterAdmins) ? false : (_newItemHeight ? true : false); + _newItemSel = (_chat && _membersFilter == MembersFilter::Admins) ? false : (_newItemHeight ? true : false); _sel = (!_newItemHeight && !_contacts->isEmpty()) ? *_contacts->cbegin() : nullptr; _byUsernameSel = (!_newItemHeight && _contacts->isEmpty() && !_byUsername.isEmpty()) ? 0 : -1; } else if (cur >= _contacts->size() + (_newItemHeight ? 1 : 0)) { @@ -1345,13 +1812,13 @@ void ContactsInner::selectSkip(int32 dir) { update(); } -void ContactsInner::selectSkipPage(int32 h, int32 dir) { +void ContactsBox::Inner::selectSkipPage(int32 h, int32 dir) { int32 points = h / _rowHeight; if (!points) return; selectSkip(points * dir); } -QVector ContactsInner::selected() { +QVector ContactsBox::Inner::selected() { QVector result; if (!usingMultiSelect()) { return result; @@ -1376,7 +1843,7 @@ QVector ContactsInner::selected() { return result; } -QVector ContactsInner::selectedInputs() { +QVector ContactsBox::Inner::selectedInputs() { QVector result; if (!usingMultiSelect()) { return result; @@ -1400,1048 +1867,3 @@ QVector ContactsInner::selectedInputs() { } return result; } - -ContactsBox::ContactsBox() : ItemListBox(st::contactsScroll) -, _inner(CreatingGroupNone) -, _filter(this, st::boxSearchField, lang(lng_participant_filter)) -, _filterCancel(this, st::boxSearchCancel) -, _next(this, lang(lng_create_group_next), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) -, _topShadow(this) { - init(); -} - -ContactsBox::ContactsBox(const QString &name, const QImage &photo) : ItemListBox(st::boxScroll) -, _inner(CreatingGroupGroup) -, _filter(this, st::boxSearchField, lang(lng_participant_filter)) -, _filterCancel(this, st::boxSearchCancel) -, _next(this, lang(lng_create_group_create), st::defaultBoxButton) -, _cancel(this, lang(lng_create_group_back), st::cancelBoxButton) -, _topShadow(this) -, _creationName(name) -, _creationPhoto(photo) { - init(); -} - -ContactsBox::ContactsBox(ChannelData *channel) : ItemListBox(st::boxScroll) -, _inner(channel, MembersFilterRecent, MembersAlreadyIn()) -, _filter(this, st::boxSearchField, lang(lng_participant_filter)) -, _filterCancel(this, st::boxSearchCancel) -, _next(this, lang(lng_participant_invite), st::defaultBoxButton) -, _cancel(this, lang(lng_create_group_skip), st::cancelBoxButton) -, _topShadow(this) { - init(); -} - -ContactsBox::ContactsBox(ChannelData *channel, MembersFilter filter, const MembersAlreadyIn &already) : ItemListBox((filter == MembersFilterAdmins) ? st::contactsScroll : st::boxScroll) -, _inner(channel, filter, already) -, _filter(this, st::boxSearchField, lang(lng_participant_filter)) -, _filterCancel(this, st::boxSearchCancel) -, _next(this, lang(lng_participant_invite), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) -, _topShadow(this) { - init(); -} - -ContactsBox::ContactsBox(ChatData *chat, MembersFilter filter) : ItemListBox(st::boxScroll) -, _inner(chat, filter) -, _filter(this, st::boxSearchField, lang(lng_participant_filter)) -, _filterCancel(this, st::boxSearchCancel) -, _next(this, lang((filter == MembersFilterAdmins) ? lng_settings_save : lng_participant_invite), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) -, _topShadow(this) { - init(); -} - -ContactsBox::ContactsBox(UserData *bot) : ItemListBox(st::contactsScroll) -, _inner(bot) -, _filter(this, st::boxSearchField, lang(lng_participant_filter)) -, _filterCancel(this, st::boxSearchCancel) -, _next(this, lang(lng_create_group_next), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) -, _topShadow(this) { - init(); -} - -void ContactsBox::init() { - bool inviting = (_inner.creating() == CreatingGroupGroup) || (_inner.channel() && _inner.membersFilter() == MembersFilterRecent) || _inner.chat(); - int32 topSkip = st::boxTitleHeight + _filter.height(); - int32 bottomSkip = inviting ? (st::boxButtonPadding.top() + _next.height() + st::boxButtonPadding.bottom()) : st::boxScrollSkip; - ItemListBox::init(&_inner, bottomSkip, topSkip); - - connect(&_inner, SIGNAL(chosenChanged()), this, SLOT(onChosenChanged())); - connect(&_inner, SIGNAL(addRequested()), App::wnd(), SLOT(onShowAddContact())); - if (_inner.channel() && _inner.membersFilter() == MembersFilterAdmins) { - _next.hide(); - _cancel.hide(); - } else if (_inner.chat() && _inner.membersFilter() == MembersFilterAdmins) { - connect(&_next, SIGNAL(clicked()), this, SLOT(onSaveAdmins())); - _bottomShadow = new ScrollableBoxShadow(this); - } else if (_inner.chat() || _inner.channel()) { - connect(&_next, SIGNAL(clicked()), this, SLOT(onInvite())); - _bottomShadow = new ScrollableBoxShadow(this); - } else if (_inner.creating() != CreatingGroupNone) { - connect(&_next, SIGNAL(clicked()), this, SLOT(onCreate())); - _bottomShadow = new ScrollableBoxShadow(this); - } else { - _next.hide(); - _cancel.hide(); - } - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); - connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate())); - connect(&_filter, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_filterCancel, SIGNAL(clicked()), this, SLOT(onFilterCancel())); - connect(&_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int))); - connect(&_inner, SIGNAL(selectAllQuery()), &_filter, SLOT(selectAll())); - connect(&_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername())); - connect(&_inner, SIGNAL(adminAdded()), this, SIGNAL(adminAdded())); - - _filterCancel.setAttribute(Qt::WA_OpaquePaintEvent); - - _searchTimer.setSingleShot(true); - connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchByUsername())); - - prepare(); -} - -bool ContactsBox::onSearchByUsername(bool searchCache) { - QString q = _filter.getLastText().trimmed(); - if (q.isEmpty()) { - if (_peopleRequest) { - _peopleRequest = 0; - } - return true; - } - if (q.size() >= MinUsernameLength) { - if (searchCache) { - PeopleCache::const_iterator i = _peopleCache.constFind(q); - if (i != _peopleCache.cend()) { - _peopleQuery = q; - _peopleRequest = 0; - peopleReceived(i.value(), 0); - return true; - } - } else if (_peopleQuery != q) { - _peopleQuery = q; - _peopleFull = false; - _peopleRequest = MTP::send(MTPcontacts_Search(MTP_string(_peopleQuery), MTP_int(SearchPeopleLimit)), rpcDone(&ContactsBox::peopleReceived), rpcFail(&ContactsBox::peopleFailed)); - _peopleQueries.insert(_peopleRequest, _peopleQuery); - } - } - return false; -} - -void ContactsBox::onNeedSearchByUsername() { - if (!onSearchByUsername(true)) { - _searchTimer.start(AutoSearchTimeout); - } -} - -void ContactsBox::peopleReceived(const MTPcontacts_Found &result, mtpRequestId req) { - QString q = _peopleQuery; - - PeopleQueries::iterator i = _peopleQueries.find(req); - if (i != _peopleQueries.cend()) { - q = i.value(); - _peopleCache[q] = result; - _peopleQueries.erase(i); - } - - if (_peopleRequest == req) { - switch (result.type()) { - case mtpc_contacts_found: { - App::feedUsers(result.c_contacts_found().vusers); - App::feedChats(result.c_contacts_found().vchats); - _inner.peopleReceived(q, result.c_contacts_found().vresults.c_vector().v); - } break; - } - - _peopleRequest = 0; - _inner.updateSel(); - onScroll(); - } -} - -bool ContactsBox::peopleFailed(const RPCError &error, mtpRequestId req) { - if (MTP::isDefaultHandledError(error)) return false; - - if (_peopleRequest == req) { - _peopleRequest = 0; - _peopleFull = true; - } - return true; -} - -void ContactsBox::showAll() { - _filter.show(); - if (_filter.getLastText().isEmpty()) { - _filterCancel.hide(); - } else { - _filterCancel.show(); - } - if (_inner.channel() && _inner.membersFilter() == MembersFilterAdmins) { - _next.hide(); - _cancel.hide(); - } else if (_inner.chat() || _inner.channel()) { - _next.show(); - _cancel.show(); - } else if (_inner.creating() != CreatingGroupNone) { - _next.show(); - _cancel.show(); - } else { - _next.hide(); - _cancel.hide(); - } - _topShadow.show(); - if (_bottomShadow) _bottomShadow->show(); - ItemListBox::showAll(); -} - -void ContactsBox::doSetInnerFocus() { - _filter.setFocus(); -} - -void ContactsBox::onSubmit() { - _inner.chooseParticipant(); -} - -void ContactsBox::keyPressEvent(QKeyEvent *e) { - if (_filter.hasFocus()) { - if (e->key() == Qt::Key_Down) { - _inner.selectSkip(1); - } else if (e->key() == Qt::Key_Up) { - _inner.selectSkip(-1); - } else if (e->key() == Qt::Key_PageDown) { - _inner.selectSkipPage(scrollArea()->height(), 1); - } else if (e->key() == Qt::Key_PageUp) { - _inner.selectSkipPage(scrollArea()->height(), -1); - } else { - ItemListBox::keyPressEvent(e); - } - } else { - ItemListBox::keyPressEvent(e); - } -} - -void ContactsBox::paintEvent(QPaintEvent *e) { - Painter p(this); - if (paint(p)) return; - - bool addingAdmin = _inner.channel() && _inner.membersFilter() == MembersFilterAdmins; - if (_inner.chat() && _inner.membersFilter() == MembersFilterAdmins) { - paintTitle(p, lang(lng_channel_admins)); - } else if (_inner.chat() || _inner.creating() != CreatingGroupNone) { - QString title(lang(addingAdmin ? lng_channel_add_admin : lng_profile_add_participant)); - QString additional((addingAdmin || (_inner.channel() && !_inner.channel()->isMegagroup())) ? QString() : QString("%1 / %2").arg(_inner.selectedCount()).arg(Global::MegagroupSizeMax())); - paintTitle(p, title, additional); - } else if (_inner.sharingBotGame()) { - paintTitle(p, lang(lng_bot_choose_chat)); - } else if (_inner.bot()) { - paintTitle(p, lang(lng_bot_choose_group)); - } else { - paintTitle(p, lang(lng_contacts_header)); - } -} - -void ContactsBox::resizeEvent(QResizeEvent *e) { - ItemListBox::resizeEvent(e); - _filter.resize(width(), _filter.height()); - _filter.moveToLeft(0, st::boxTitleHeight); - _filterCancel.moveToRight(0, st::boxTitleHeight); - _inner.resize(width(), _inner.height()); - _next.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _next.width() + st::boxButtonPadding.left(), _next.y()); - _topShadow.setGeometry(0, st::boxTitleHeight + _filter.height(), width(), st::lineWidth); - if (_bottomShadow) _bottomShadow->setGeometry(0, height() - st::boxButtonPadding.bottom() - _next.height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); -} - -void ContactsBox::closePressed() { - if (_inner.channel() && !_inner.hasAlreadyMembersInChannel()) { - Ui::showPeerHistory(_inner.channel(), ShowAtTheEndMsgId); - } -} - -void ContactsBox::onFilterCancel() { - _filter.setText(QString()); -} - -void ContactsBox::onFilterUpdate() { - scrollArea()->scrollToY(0); - if (_filter.getLastText().isEmpty()) { - _filterCancel.hide(); - } else { - _filterCancel.show(); - } - _inner.updateFilter(_filter.getLastText()); -} - -void ContactsBox::onChosenChanged() { - update(); -} - -void ContactsBox::onInvite() { - QVector users(_inner.selected()); - if (users.isEmpty()) { - _filter.setFocus(); - _filter.showError(); - return; - } - - App::main()->addParticipants(_inner.chat() ? (PeerData*)_inner.chat() : _inner.channel(), users); - if (_inner.chat()) { - Ui::hideLayer(); - Ui::showPeerHistory(_inner.chat(), ShowAtTheEndMsgId); - } else { - onClose(); - } -} - -void ContactsBox::onCreate() { - if (_saveRequestId) return; - - MTPVector users(MTP_vector(_inner.selectedInputs())); - const auto &v(users.c_vector().v); - if (v.isEmpty() || (v.size() == 1 && v.at(0).type() == mtpc_inputUserSelf)) { - _filter.setFocus(); - _filter.showError(); - return; - } - _saveRequestId = MTP::send(MTPmessages_CreateChat(MTP_vector(v), MTP_string(_creationName)), rpcDone(&ContactsBox::creationDone), rpcFail(&ContactsBox::creationFail)); -} - -void ContactsBox::onSaveAdmins() { - if (_saveRequestId) return; - - _inner.saving(true); - _saveRequestId = MTP::send(MTPmessages_ToggleChatAdmins(_inner.chat()->inputChat, MTP_bool(!_inner.allAdmins())), rpcDone(&ContactsBox::saveAdminsDone), rpcFail(&ContactsBox::saveAdminsFail)); -} - -void ContactsBox::saveAdminsDone(const MTPUpdates &result) { - App::main()->sentUpdatesReceived(result); - saveSelectedAdmins(); -} - -void ContactsBox::saveSelectedAdmins() { - if (_inner.allAdmins() && !_inner.chat()->participants.isEmpty()) { - onClose(); - } else { - _saveRequestId = MTP::send(MTPmessages_GetFullChat(_inner.chat()->inputChat), rpcDone(&ContactsBox::getAdminsDone), rpcFail(&ContactsBox::saveAdminsFail)); - } -} - -void ContactsBox::getAdminsDone(const MTPmessages_ChatFull &result) { - App::api()->processFullPeer(_inner.chat(), result); - if (_inner.allAdmins()) { - onClose(); - return; - } - ChatData::Admins curadmins = _inner.chat()->admins; - QVector newadmins = _inner.selected(), appoint; - if (!newadmins.isEmpty()) { - appoint.reserve(newadmins.size()); - for (int32 i = 0, l = newadmins.size(); i < l; ++i) { - ChatData::Admins::iterator c = curadmins.find(newadmins.at(i)); - if (c == curadmins.cend()) { - if (newadmins.at(i)->id != peerFromUser(_inner.chat()->creator)) { - appoint.push_back(newadmins.at(i)); - } - } else { - curadmins.erase(c); - } - } - } - _saveRequestId = 0; - - for_const (UserData *user, curadmins) { - MTP::send(MTPmessages_EditChatAdmin(_inner.chat()->inputChat, user->inputUser, MTP_boolFalse()), rpcDone(&ContactsBox::removeAdminDone, user), rpcFail(&ContactsBox::editAdminFail), 0, 10); - } - for_const (UserData *user, appoint) { - MTP::send(MTPmessages_EditChatAdmin(_inner.chat()->inputChat, user->inputUser, MTP_boolTrue()), rpcDone(&ContactsBox::setAdminDone, user), rpcFail(&ContactsBox::editAdminFail), 0, 10); - } - MTP::sendAnything(); - - _saveRequestId = curadmins.size() + appoint.size(); - if (!_saveRequestId) { - onClose(); - } -} - -void ContactsBox::setAdminDone(UserData *user, const MTPBool &result) { - if (mtpIsTrue(result)) { - if (_inner.chat()->noParticipantInfo()) { - App::api()->requestFullPeer(_inner.chat()); - } else { - _inner.chat()->admins.insert(user); - } - } - --_saveRequestId; - if (!_saveRequestId) { - emit App::main()->peerUpdated(_inner.chat()); - onClose(); - } -} - -void ContactsBox::removeAdminDone(UserData *user, const MTPBool &result) { - if (mtpIsTrue(result)) { - _inner.chat()->admins.remove(user); - } - --_saveRequestId; - if (!_saveRequestId) { - emit App::main()->peerUpdated(_inner.chat()); - onClose(); - } -} - -bool ContactsBox::saveAdminsFail(const RPCError &error) { - if (MTP::isDefaultHandledError(error)) return true; - _saveRequestId = 0; - _inner.saving(false); - if (error.type() == qstr("CHAT_NOT_MODIFIED")) { - saveSelectedAdmins(); - } - return false; -} - -bool ContactsBox::editAdminFail(const RPCError &error) { - if (MTP::isDefaultHandledError(error)) return true; - --_saveRequestId; - _inner.chat()->invalidateParticipants(); - if (!_saveRequestId) { - if (error.type() == qstr("USER_RESTRICTED")) { - Ui::showLayer(new InformBox(lang(lng_cant_do_this))); - return true; - } - onClose(); - } - return false; -} - -void ContactsBox::onScroll() { - _inner.loadProfilePhotos(scrollArea()->scrollTop()); -} - -void ContactsBox::creationDone(const MTPUpdates &updates) { - Ui::hideLayer(); - - App::main()->sentUpdatesReceived(updates); - const QVector *v = 0; - switch (updates.type()) { - case mtpc_updates: v = &updates.c_updates().vchats.c_vector().v; break; - case mtpc_updatesCombined: v = &updates.c_updatesCombined().vchats.c_vector().v; break; - default: LOG(("API Error: unexpected update cons %1 (ContactsBox::creationDone)").arg(updates.type())); break; - } - - PeerData *peer = 0; - if (v && !v->isEmpty() && v->front().type() == mtpc_chat) { - peer = App::chat(v->front().c_chat().vid.v); - if (peer) { - if (!_creationPhoto.isNull()) { - App::app()->uploadProfilePhoto(_creationPhoto, peer->id); - } - Ui::showPeerHistory(peer, ShowAtUnreadMsgId); - } - } else { - LOG(("API Error: chat not found in updates (ContactsBox::creationDone)")); - } -} - -bool ContactsBox::creationFail(const RPCError &error) { - if (MTP::isDefaultHandledError(error)) return false; - - _saveRequestId = 0; - if (error.type() == "NO_CHAT_TITLE") { - onClose(); - return true; - } else if (error.type() == "USERS_TOO_FEW") { - _filter.setFocus(); - _filter.showError(); - return true; - } else if (error.type() == "PEER_FLOOD") { - Ui::showLayer(new InformBox(cantInviteError()), KeepOtherLayers); - return true; - } else if (error.type() == qstr("USER_RESTRICTED")) { - Ui::showLayer(new InformBox(lang(lng_cant_do_this))); - return true; - } - return false; -} - -MembersInner::MembersInner(ChannelData *channel, MembersFilter filter) : TWidget() -, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) -, _newItemHeight((channel->amCreator() && (channel->membersCount() < (channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!channel->isMegagroup() && !channel->isPublic()) || filter == MembersFilterAdmins)) ? st::contactsNewItemHeight : 0) -, _newItemSel(false) -, _channel(channel) -, _filter(filter) -, _kickText(lang(lng_profile_kick)) -, _time(0) -, _kickWidth(st::normalFont->width(_kickText)) -, _sel(-1) -, _kickSel(-1) -, _kickDown(-1) -, _mouseSel(false) -, _kickConfirm(0) -, _kickRequestId(0) -, _kickBox(0) -, _loading(true) -, _loadingRequestId(0) -, _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right()) -, _about(_aboutWidth) -, _aboutHeight(0) { - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); - - connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&))); - connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*))); - - refresh(); - - load(); -} - -void MembersInner::load() { - if (!_loadingRequestId) { - _loadingRequestId = MTP::send(MTPchannels_GetParticipants(_channel->inputChannel, (_filter == MembersFilterRecent) ? MTP_channelParticipantsRecent() : MTP_channelParticipantsAdmins(), MTP_int(0), MTP_int(Global::ChatSizeMax())), rpcDone(&MembersInner::membersReceived), rpcFail(&MembersInner::membersFailed)); - } -} - -void MembersInner::paintEvent(QPaintEvent *e) { - QRect r(e->rect()); - Painter p(this); - - _time = unixtime(); - p.fillRect(r, st::white->b); - - int32 yFrom = r.y() - st::membersPadding.top(), yTo = r.y() + r.height() - st::membersPadding.top(); - p.translate(0, st::membersPadding.top()); - if (_rows.isEmpty()) { - p.setFont(st::noContactsFont->f); - p.setPen(st::noContactsColor->p); - p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); - } else { - if (_newItemHeight) { - p.fillRect(0, 0, width(), _newItemHeight, (_newItemSel ? st::contactsBgOver : st::white)->b); - p.drawSpriteLeft(st::contactsNewItemIconPosition.x(), st::contactsNewItemIconPosition.y(), width(), st::contactsNewItemIcon); - p.setFont(st::contactsNameFont); - p.setPen(st::contactsNewItemFg); - p.drawTextLeft(st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(), st::contactsNewItemTop, width(), lang(_filter == MembersFilterAdmins ? lng_channel_add_admins : lng_channel_add_members)); - - yFrom -= _newItemHeight; - yTo -= _newItemHeight; - p.translate(0, _newItemHeight); - } - int32 from = floorclamp(yFrom, _rowHeight, 0, _rows.size()); - int32 to = ceilclamp(yTo, _rowHeight, 0, _rows.size()); - p.translate(0, from * _rowHeight); - for (; from < to; ++from) { - bool sel = (from == _sel); - bool kickSel = (from == _kickSel && (_kickDown < 0 || from == _kickDown)); - bool kickDown = kickSel && (from == _kickDown); - paintDialog(p, _rows[from], data(from), sel, kickSel, kickDown); - p.translate(0, _rowHeight); - } - if (to == _rows.size() && _filter == MembersFilterRecent && (_rows.size() < _channel->membersCount() || _rows.size() >= Global::ChatSizeMax())) { - p.setPen(st::stickersReorderFg); - _about.draw(p, st::contactsPadding.left(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center); - } - } -} - -void MembersInner::enterEvent(QEvent *e) { - setMouseTracking(true); -} - -void MembersInner::leaveEvent(QEvent *e) { - _mouseSel = false; - setMouseTracking(false); - if (_sel >= 0) { - clearSel(); - } -} - -void MembersInner::mouseMoveEvent(QMouseEvent *e) { - _mouseSel = true; - _lastMousePos = e->globalPos(); - updateSel(); -} - -void MembersInner::mousePressEvent(QMouseEvent *e) { - _mouseSel = true; - _lastMousePos = e->globalPos(); - updateSel(); - if (e->button() == Qt::LeftButton && _kickSel < 0) { - chooseParticipant(); - } - _kickDown = _kickSel; - update(); -} - -void MembersInner::mouseReleaseEvent(QMouseEvent *e) { - _mouseSel = true; - _lastMousePos = e->globalPos(); - updateSel(); - if (_kickDown >= 0 && _kickDown == _kickSel && !_kickRequestId) { - _kickConfirm = _rows.at(_kickSel); - if (_kickBox) _kickBox->deleteLater(); - _kickBox = new ConfirmBox((_filter == MembersFilterRecent ? (_channel->isMegagroup() ? lng_profile_sure_kick : lng_profile_sure_kick_channel) : lng_profile_sure_kick_admin)(lt_user, _kickConfirm->firstName)); - connect(_kickBox, SIGNAL(confirmed()), this, SLOT(onKickConfirm())); - connect(_kickBox, SIGNAL(destroyed(QObject*)), this, SLOT(onKickBoxDestroyed(QObject*))); - Ui::showLayer(_kickBox, KeepOtherLayers); - } - _kickDown = -1; -} - -void MembersInner::onKickBoxDestroyed(QObject *obj) { - if (_kickBox == obj) { - _kickBox = 0; - } -} - -void MembersInner::onKickConfirm() { - if (_filter == MembersFilterRecent) { - _kickRequestId = MTP::send(MTPchannels_KickFromChannel(_channel->inputChannel, _kickConfirm->inputUser, MTP_bool(true)), rpcDone(&MembersInner::kickDone), rpcFail(&MembersInner::kickFail)); - } else { - _kickRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _kickConfirm->inputUser, MTP_channelRoleEmpty()), rpcDone(&MembersInner::kickAdminDone), rpcFail(&MembersInner::kickFail)); - } -} - -void MembersInner::paintDialog(Painter &p, PeerData *peer, MemberData *data, bool sel, bool kickSel, bool kickDown) { - UserData *user = peer->asUser(); - - p.fillRect(0, 0, width(), _rowHeight, (sel ? st::contactsBgOver : st::white)->b); - peer->paintUserpicLeft(p, st::contactsPhotoSize, st::contactsPadding.left(), st::contactsPadding.top(), width()); - - p.setPen(st::black); - - int32 namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); - int32 namew = width() - namex - st::contactsPadding.right() - (data->canKick ? (_kickWidth + st::contactsCheckPosition.x() * 2) : 0); - if (peer->isVerified()) { - auto icon = &st::dialogsVerifiedIcon; - namew -= icon->width(); - icon->paint(p, namex + qMin(data->name.maxWidth(), namew), st::contactsPadding.top() + st::contactsNameTop, width()); - } - data->name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width()); - - if (data->canKick) { - p.setFont((kickSel ? st::linkOverFont : st::linkFont)->f); - if (kickDown) { - p.setPen(st::btnDefLink.downColor->p); - } else { - p.setPen(st::btnDefLink.color->p); - } - p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), _kickText, _kickWidth); - } - - p.setFont(st::contactsStatusFont->f); - p.setPen(data->onlineColor ? st::contactsStatusFgOnline : (sel ? st::contactsStatusFgOver : st::contactsStatusFg)); - p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsStatusTop, width(), data->online); -} - -void MembersInner::selectSkip(int32 dir) { - _time = unixtime(); - _mouseSel = false; - - int cur = -1; - if (_newItemHeight && _newItemSel) { - cur = 0; - } else if (_sel >= 0) { - cur = _sel + (_newItemHeight ? 1 : 0); - } - cur += dir; - if (cur <= 0) { - _newItemSel = _newItemHeight ? true : false; - _sel = (_newItemSel || _rows.isEmpty()) ? -1 : 0; - } else if (cur >= _rows.size() + (_newItemHeight ? 1 : 0)) { - _sel = -1; - } else { - _sel = cur - (_newItemHeight ? 1 : 0); - } - if (dir > 0) { - if (_sel < 0 || _sel >= _rows.size()) { - _sel = -1; - } - } else { - if (!_rows.isEmpty()) { - if (_sel < 0 && !_newItemSel) _sel = _rows.size() - 1; - } - } - if (_newItemSel) { - emit mustScrollTo(0, _newItemHeight); - } else if (_sel >= 0) { - emit mustScrollTo(_newItemHeight + _sel * _rowHeight, _newItemHeight + (_sel + 1) * _rowHeight); - } - - update(); -} - -void MembersInner::selectSkipPage(int32 h, int32 dir) { - int32 points = h / _rowHeight; - if (!points) return; - selectSkip(points * dir); -} - -void MembersInner::loadProfilePhotos(int32 yFrom) { - int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5; - MTP::clearLoaderPriorities(); - - if (yTo < 0) return; - if (yFrom < 0) yFrom = 0; - - if (!_rows.isEmpty()) { - int32 from = (yFrom - _newItemHeight) / _rowHeight; - if (from < 0) from = 0; - if (from < _rows.size()) { - int32 to = ((yTo - _newItemHeight) / _rowHeight) + 1; - if (to > _rows.size()) to = _rows.size(); - - for (; from < to; ++from) { - _rows[from]->loadUserpic(); - } - } - } -} - -void MembersInner::chooseParticipant() { - if (_newItemSel) { - emit addRequested(); - return; - } - if (_sel < 0 || _sel >= _rows.size()) return; - if (PeerData *peer = _rows[_sel]) { - Ui::hideLayer(); - Ui::showPeerProfile(peer); - } -} - -void MembersInner::refresh() { - if (_rows.isEmpty()) { - resize(width(), st::membersPadding.top() + st::noContactsHeight + st::membersPadding.bottom()); - _aboutHeight = 0; - } else { - _about.setText(st::boxTextFont, lng_channel_only_last_shown(lt_count, _rows.size())); - _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); - if (_filter != MembersFilterRecent || (_rows.size() >= _channel->membersCount() && _rows.size() < Global::ChatSizeMax())) { - _aboutHeight = 0; - } - resize(width(), st::membersPadding.top() + _newItemHeight + _rows.size() * _rowHeight + st::membersPadding.bottom() + _aboutHeight); - } - update(); -} - -ChannelData *MembersInner::channel() const { - return _channel; -} - -MembersFilter MembersInner::filter() const { - return _filter; -} - -MembersAlreadyIn MembersInner::already() const { - MembersAlreadyIn result; - for_const (auto peer, _rows) { - if (peer->isUser()) { - result.insert(peer->asUser()); - } - } - return result; -} - -void MembersInner::clearSel() { - updateSelectedRow(); - _newItemSel = false; - _sel = _kickSel = _kickDown = -1; - _lastMousePos = QCursor::pos(); - updateSel(); -} - -MembersInner::MemberData *MembersInner::data(int32 index) { - if (MemberData *result = _datas.at(index)) { - return result; - } - MemberData *result = _datas[index] = new MemberData(); - result->name.setText(st::contactsNameFont, _rows[index]->name, _textNameOptions); - int32 t = unixtime(); - result->online = App::onlineText(_rows[index], t);// lng_mediaview_date_time(lt_date, _dates[index].date().toString(qsl("dd.MM.yy")), lt_time, _dates[index].time().toString(cTimeFormat())); - result->onlineColor = App::onlineColorUse(_rows[index], t); - if (_filter == MembersFilterRecent) { - result->canKick = (_channel->amCreator() || _channel->amEditor() || _channel->amModerator()) ? (_roles[index] == MemberRoleNone) : false; - } else if (_filter == MembersFilterAdmins) { - result->canKick = _channel->amCreator() ? (_roles[index] == MemberRoleEditor || _roles[index] == MemberRoleModerator) : false; - } else { - result->canKick = false; - } - return result; -} - -void MembersInner::clear() { - for (int32 i = 0, l = _datas.size(); i < l; ++i) { - delete _datas.at(i); - } - _datas.clear(); - _rows.clear(); - _dates.clear(); - _roles.clear(); - if (_kickBox) _kickBox->deleteLater(); - clearSel(); -} - -MembersInner::~MembersInner() { - clear(); -} - -void MembersInner::updateSel() { - if (!_mouseSel) return; - - QPoint p(mapFromGlobal(_lastMousePos)); - p.setY(p.y() - st::membersPadding.top()); - bool in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); - bool newItemSel = (in && p.y() >= 0 && p.y() < _newItemHeight); - int32 newSel = (in && !newItemSel && p.y() >= _newItemHeight && p.y() < _newItemHeight + _rows.size() * _rowHeight) ? ((p.y() - _newItemHeight) / _rowHeight) : -1; - int32 newKickSel = newSel; - if (newSel >= 0 && (!data(newSel)->canKick || !QRect(width() - _kickWidth - st::contactsPadding.right() - st::contactsCheckPosition.x(), _newItemHeight + newSel * _rowHeight + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _kickWidth, st::normalFont->height).contains(p))) { - newKickSel = -1; - } - if (newSel != _sel || newKickSel != _kickSel || newItemSel != _newItemSel) { - updateSelectedRow(); - _newItemSel = newItemSel; - _sel = newSel; - _kickSel = newKickSel; - updateSelectedRow(); - setCursor(_kickSel >= 0 ? style::cur_pointer : style::cur_default); - } -} - -void MembersInner::peerUpdated(PeerData *peer) { - update(); -} - -void MembersInner::updateSelectedRow() { - if (_newItemSel) { - update(0, st::membersPadding.top(), width(), _newItemHeight); - } - if (_sel >= 0) { - update(0, st::membersPadding.top() + _newItemHeight + _sel * _rowHeight, width(), _rowHeight); - } -} - -void MembersInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { - for (int32 i = 0, l = _rows.size(); i < l; ++i) { - if (_rows.at(i) == peer) { - if (_datas.at(i)) { - _datas.at(i)->name.setText(st::contactsNameFont, peer->name, _textNameOptions); - update(0, st::membersPadding.top() + i * _rowHeight, width(), _rowHeight); - } else { - break; - } - } - } -} - -void MembersInner::membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req) { - clear(); - _loadingRequestId = 0; - - if (result.type() == mtpc_channels_channelParticipants) { - const auto &d(result.c_channels_channelParticipants()); - const auto &v(d.vparticipants.c_vector().v); - _rows.reserve(v.size()); - _datas.reserve(v.size()); - _dates.reserve(v.size()); - _roles.reserve(v.size()); - - if (_filter == MembersFilterRecent && _channel->membersCount() < d.vcount.v) { - _channel->setMembersCount(d.vcount.v); - if (App::main()) emit App::main()->peerUpdated(_channel); - } else if (_filter == MembersFilterAdmins && _channel->adminsCount() < d.vcount.v) { - _channel->setAdminsCount(d.vcount.v); - if (App::main()) emit App::main()->peerUpdated(_channel); - } - App::feedUsers(d.vusers); - - for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) { - int32 userId = 0, addedTime = 0; - MemberRole role = MemberRoleNone; - switch (i->type()) { - case mtpc_channelParticipant: - userId = i->c_channelParticipant().vuser_id.v; - addedTime = i->c_channelParticipant().vdate.v; - break; - case mtpc_channelParticipantSelf: - role = MemberRoleSelf; - userId = i->c_channelParticipantSelf().vuser_id.v; - addedTime = i->c_channelParticipantSelf().vdate.v; - break; - case mtpc_channelParticipantModerator: - role = MemberRoleModerator; - userId = i->c_channelParticipantModerator().vuser_id.v; - addedTime = i->c_channelParticipantModerator().vdate.v; - break; - case mtpc_channelParticipantEditor: - role = MemberRoleEditor; - userId = i->c_channelParticipantEditor().vuser_id.v; - addedTime = i->c_channelParticipantEditor().vdate.v; - break; - case mtpc_channelParticipantKicked: - userId = i->c_channelParticipantKicked().vuser_id.v; - addedTime = i->c_channelParticipantKicked().vdate.v; - role = MemberRoleKicked; - break; - case mtpc_channelParticipantCreator: - userId = i->c_channelParticipantCreator().vuser_id.v; - addedTime = _channel->date; - role = MemberRoleCreator; - break; - } - if (UserData *user = App::userLoaded(userId)) { - _rows.push_back(user); - _dates.push_back(date(addedTime)); - _roles.push_back(role); - _datas.push_back(0); - } - } - - // update admins if we got all of them - if (_filter == MembersFilterAdmins && _channel->isMegagroup() && _rows.size() < Global::ChatSizeMax()) { - _channel->mgInfo->lastAdmins.clear(); - for (int32 i = 0, l = _rows.size(); i != l; ++i) { - if (_roles.at(i) == MemberRoleCreator || _roles.at(i) == MemberRoleEditor) { - _channel->mgInfo->lastAdmins.insert(_rows.at(i)); - } - } - - Notify::peerUpdatedDelayed(_channel, Notify::PeerUpdate::Flag::AdminsChanged); - } - } - if (_rows.isEmpty()) { - _rows.push_back(App::self()); - _dates.push_back(date(MTP_int(_channel->date))); - _roles.push_back(MemberRoleSelf); - _datas.push_back(0); - } - - clearSel(); - _loading = false; - refresh(); - - emit loaded(); -} - -bool MembersInner::membersFailed(const RPCError &error, mtpRequestId req) { - if (MTP::isDefaultHandledError(error)) return false; - - Ui::hideLayer(); - return true; -} - -void MembersInner::kickDone(const MTPUpdates &result, mtpRequestId req) { - App::main()->sentUpdatesReceived(result); - - if (_kickRequestId != req) return; - removeKicked(); - if (_kickBox) _kickBox->onClose(); -} - -void MembersInner::kickAdminDone(const MTPUpdates &result, mtpRequestId req) { - if (_kickRequestId != req) return; - if (App::main()) App::main()->sentUpdatesReceived(result); - removeKicked(); - if (_kickBox) _kickBox->onClose(); -} - -bool MembersInner::kickFail(const RPCError &error, mtpRequestId req) { - if (MTP::isDefaultHandledError(error)) return false; - - if (_kickBox) _kickBox->onClose(); - load(); - return true; -} - -void MembersInner::removeKicked() { - _kickRequestId = 0; - int32 index = _rows.indexOf(_kickConfirm); - if (index >= 0) { - _rows.removeAt(index); - delete _datas.at(index); - _datas.removeAt(index); - _dates.removeAt(index); - _roles.removeAt(index); - clearSel(); - if (_filter == MembersFilterRecent && _channel->membersCount() > 1) { - _channel->setMembersCount(_channel->membersCount() - 1); - if (App::main()) emit App::main()->peerUpdated(_channel); - } else if (_filter == MembersFilterAdmins && _channel->adminsCount() > 1) { - _channel->setAdminsCount(_channel->adminsCount() - 1); - if (App::main()) emit App::main()->peerUpdated(_channel); - } - refresh(); - } - _kickConfirm = 0; -} - -MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox(st::boxScroll) -, _inner(channel, filter) -, _addBox(0) { - ItemListBox::init(&_inner); - - connect(&_inner, SIGNAL(addRequested()), this, SLOT(onAdd())); - - connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); - connect(&_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int))); - connect(&_inner, SIGNAL(loaded()), this, SLOT(onLoaded())); - - connect(&_loadTimer, SIGNAL(timeout()), &_inner, SLOT(load())); - - prepare(); -} - -void MembersBox::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Down) { - _inner.selectSkip(1); - } else if (e->key() == Qt::Key_Up) { - _inner.selectSkip(-1); - } else if (e->key() == Qt::Key_PageDown) { - _inner.selectSkipPage(scrollArea()->height(), 1); - } else if (e->key() == Qt::Key_PageUp) { - _inner.selectSkipPage(scrollArea()->height(), -1); - } else { - ItemListBox::keyPressEvent(e); - } -} - -void MembersBox::paintEvent(QPaintEvent *e) { - Painter p(this); - if (paint(p)) return; - - QString title(lang(_inner.filter() == MembersFilterRecent ? lng_channel_members : lng_channel_admins)); - paintTitle(p, title); -} - -void MembersBox::resizeEvent(QResizeEvent *e) { - ItemListBox::resizeEvent(e); - _inner.resize(width(), _inner.height()); -} - -void MembersBox::onScroll() { - _inner.loadProfilePhotos(scrollArea()->scrollTop()); -} - -void MembersBox::onAdd() { - if (_inner.filter() == MembersFilterRecent && _inner.channel()->membersCount() >= (_inner.channel()->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax())) { - Ui::showLayer(new MaxInviteBox(_inner.channel()->inviteLink()), KeepOtherLayers); - return; - } - ContactsBox *box = new ContactsBox(_inner.channel(), _inner.filter(), _inner.already()); - if (_inner.filter() == MembersFilterRecent) { - Ui::showLayer(box); - } else { - _addBox = box; - connect(_addBox, SIGNAL(adminAdded()), this, SLOT(onAdminAdded())); - Ui::showLayer(_addBox, KeepOtherLayers); - } -} - -void MembersBox::onAdminAdded() { - if (!_addBox) return; - _addBox->onClose(); - _addBox = 0; - _loadTimer.start(ReloadChannelMembersTimeout); -} diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index 6f81ccf4e..4315a002d 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -23,186 +23,24 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" #include "core/single_timer.h" #include "ui/effects/round_image_checkbox.h" +#include "boxes/members_box.h" namespace Dialogs { class Row; class IndexedList; } // namespace Dialogs -enum MembersFilter { - MembersFilterRecent, - MembersFilterAdmins, -}; -using MembersAlreadyIn = OrderedSet; +namespace Ui { +class IconButton; +} // namespace Ui QString cantInviteError(); -class ConfirmBox; -class ContactsInner : public TWidget, public RPCSender, private base::Subscriber { - Q_OBJECT - -private: - struct ContactData; - -public: - ContactsInner(CreatingGroupType creating = CreatingGroupNone); - ContactsInner(ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already); - ContactsInner(ChatData *chat, MembersFilter membersFilter); - ContactsInner(UserData *bot); - void init(); - void initList(); - - void updateFilter(QString filter = QString()); - - void selectSkip(int32 dir); - void selectSkipPage(int32 h, int32 dir); - - QVector selected(); - QVector selectedInputs(); - bool allAdmins() const { - return _allAdmins.checked(); - } - - void loadProfilePhotos(int32 yFrom); - void chooseParticipant(); - void changeCheckState(Dialogs::Row *row); - void changeCheckState(ContactData *data, PeerData *peer); - - void peopleReceived(const QString &query, const QVector &people); - - void refresh(); - - ChatData *chat() const; - ChannelData *channel() const; - MembersFilter membersFilter() const; - UserData *bot() const; - CreatingGroupType creating() const; - - bool sharingBotGame() const; - - int32 selectedCount() const; - bool hasAlreadyMembersInChannel() const { - return !_already.isEmpty(); - } - - void saving(bool flag); - - ~ContactsInner(); - -signals: - void mustScrollTo(int ymin, int ymax); - void selectAllQuery(); - void searchByUsername(); - void chosenChanged(); - void adminAdded(); - void addRequested(); - -public slots: - void onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow); - - void updateSel(); - void peerUpdated(PeerData *peer); - void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); - - void onAddBot(); - void onAddAdmin(); - void onNoAddAdminBox(QObject *obj); - - void onAllAdminsChanged(); - -protected: - void paintEvent(QPaintEvent *e) override; - void enterEvent(QEvent *e) override; - void leaveEvent(QEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - -private: - void updateRowWithTop(int rowTop); - int getSelectedRowTop() const; - void updateSelectedRow(); - int getRowTopWithPeer(PeerData *peer) const; - void updateRowWithPeer(PeerData *peer); - void addAdminDone(const MTPUpdates &result, mtpRequestId req); - bool addAdminFail(const RPCError &error, mtpRequestId req); - - void paintDialog(Painter &p, PeerData *peer, ContactData *data, bool sel); - void paintDisabledCheckUserpic(Painter &p, PeerData *peer, int x, int y, int outerWidth) const; - - template - void addDialogsToList(FilterCallback callback); - - bool usingMultiSelect() const { - return (_chat != nullptr) || (_creating != CreatingGroupNone && (!_channel || _membersFilter != MembersFilterAdmins)); - } - - int32 _rowHeight; - int _newItemHeight = 0; - bool _newItemSel = false; - - ChatData *_chat = nullptr; - ChannelData *_channel = nullptr; - MembersFilter _membersFilter = MembersFilterRecent; - UserData *_bot = nullptr; - CreatingGroupType _creating = CreatingGroupNone; - MembersAlreadyIn _already; - - Checkbox _allAdmins; - int32 _aboutWidth; - Text _aboutAllAdmins, _aboutAdmins; - - PeerData *_addToPeer = nullptr; - UserData *_addAdmin = nullptr; - mtpRequestId _addAdminRequestId = 0; - ConfirmBox *_addAdminBox = nullptr; - - int32 _time; - - std_::unique_ptr _customList; - Dialogs::IndexedList *_contacts = nullptr; - Dialogs::Row *_sel = nullptr; - QString _filter; - typedef QVector FilteredDialogs; - FilteredDialogs _filtered; - int _filteredSel = -1; - bool _mouseSel = false; - - int _selCount = 0; - - struct ContactData { - ContactData(); - ContactData(PeerData *peer, Ui::RoundImageCheckbox::UpdateCallback &&updateCallback); - - std_::unique_ptr checkbox; - Text name; - QString statusText; - bool statusHasOnlineColor = false; - bool disabledChecked = false; +inline Ui::RoundImageCheckbox::PaintRoundImage PaintUserpicCallback(PeerData *peer) { + return [peer](Painter &p, int x, int y, int outerWidth, int size) { + peer->paintUserpicLeft(p, size, x, y, outerWidth); }; - typedef QMap ContactsData; - ContactsData _contactsData; - typedef QMap CheckedContacts; - CheckedContacts _checkedContacts; - - ContactData *contactData(Dialogs::Row *row); - - bool _searching = false; - QString _lastQuery; - typedef QVector ByUsernameRows; - typedef QVector ByUsernameDatas; - ByUsernameRows _byUsername, _byUsernameFiltered; - ByUsernameDatas d_byUsername, d_byUsernameFiltered; // filtered is partly subset of d_byUsername, partly subset of _byUsernameDatas - ByUsernameDatas _byUsernameDatas; - int _byUsernameSel = -1; - - QPoint _lastMousePos; - LinkButton _addContactLnk; - - bool _saving = false; - bool _allAdminsChecked = false; - -}; +} class ContactsBox : public ItemListBox, public RPCSender { Q_OBJECT @@ -245,9 +83,10 @@ protected: private: void init(); - ContactsInner _inner; - InputField _filter; - IconedButton _filterCancel; + class Inner; + ChildWidget _inner; + ChildWidget _filter; + ChildWidget _filterCancel; BoxButton _next, _cancel; MembersFilter _membersFilter; @@ -289,50 +128,74 @@ private: }; -class MembersInner : public TWidget, public RPCSender, private base::Subscriber { +// This class is hold in header because it requires Qt preprocessing. +class ContactsBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { Q_OBJECT -private: - struct MemberData; - public: - MembersInner(ChannelData *channel, MembersFilter filter); + Inner(QWidget *parent, CreatingGroupType creating = CreatingGroupNone); + Inner(QWidget *parent, ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already); + Inner(QWidget *parent, ChatData *chat, MembersFilter membersFilter); + Inner(QWidget *parent, UserData *bot); - void paintDialog(Painter &p, PeerData *peer, MemberData *data, bool sel, bool kickSel, bool kickDown); + void init(); + void initList(); + + void updateFilter(QString filter = QString()); void selectSkip(int32 dir); void selectSkipPage(int32 h, int32 dir); + QVector selected(); + QVector selectedInputs(); + bool allAdmins() const { + return _allAdmins.checked(); + } + void loadProfilePhotos(int32 yFrom); void chooseParticipant(); + void peopleReceived(const QString &query, const QVector &people); + void refresh(); + ChatData *chat() const; ChannelData *channel() const; - MembersFilter filter() const; + MembersFilter membersFilter() const; + UserData *bot() const; + CreatingGroupType creating() const; - bool isLoaded() const { - return !_loading; + bool sharingBotGame() const; + + int32 selectedCount() const; + bool hasAlreadyMembersInChannel() const { + return !_already.isEmpty(); } - void clearSel(); - MembersAlreadyIn already() const; + void saving(bool flag); - ~MembersInner(); + ~Inner(); signals: void mustScrollTo(int ymin, int ymax); + void selectAllQuery(); + void searchByUsername(); + void chosenChanged(); + void adminAdded(); void addRequested(); - void loaded(); public slots: - void load(); + void onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow); void updateSel(); void peerUpdated(PeerData *peer); void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); - void onKickConfirm(); - void onKickBoxDestroyed(QObject *obj); + + void onAddBot(); + void onAddAdmin(); + void onNoAddAdminBox(QObject *obj); + + void onAllAdminsChanged(); protected: void paintEvent(QPaintEvent *e) override; @@ -340,102 +203,94 @@ protected: void leaveEvent(QEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - -private: - void updateSelectedRow(); - MemberData *data(int32 index); - - void membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req); - bool membersFailed(const RPCError &error, mtpRequestId req); - - void kickDone(const MTPUpdates &result, mtpRequestId req); - void kickAdminDone(const MTPUpdates &result, mtpRequestId req); - bool kickFail(const RPCError &error, mtpRequestId req); - void removeKicked(); - - void clear(); - - int32 _rowHeight, _newItemHeight; - bool _newItemSel; - - ChannelData *_channel; - MembersFilter _filter; - - QString _kickText; - int32 _time, _kickWidth; - - int32 _sel, _kickSel, _kickDown; - bool _mouseSel; - - UserData *_kickConfirm; - mtpRequestId _kickRequestId; - - ConfirmBox *_kickBox; - - enum MemberRole { - MemberRoleNone, - MemberRoleSelf, - MemberRoleCreator, - MemberRoleEditor, - MemberRoleModerator, - MemberRoleKicked - }; - - struct MemberData { - Text name; - QString online; - bool onlineColor; - bool canKick; - }; - - bool _loading; - mtpRequestId _loadingRequestId; - typedef QVector MemberRows; - typedef QVector MemberDates; - typedef QVector MemberRoles; - typedef QVector MemberDatas; - MemberRows _rows; - MemberDates _dates; - MemberRoles _roles; - MemberDatas _datas; - - int32 _aboutWidth; - Text _about; - int32 _aboutHeight; - - QPoint _lastMousePos; - -}; - -class MembersBox : public ItemListBox { - Q_OBJECT - -public: - MembersBox(ChannelData *channel, MembersFilter filter); - -public slots: - void onScroll(); - - void onAdd(); - void onAdminAdded(); - -protected: - void keyPressEvent(QKeyEvent *e) override; - void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; private: - MembersInner _inner; + struct ContactData { + ContactData(); + ContactData(PeerData *peer, Ui::RoundImageCheckbox::UpdateCallback &&updateCallback); - ContactsBox *_addBox; + std_::unique_ptr checkbox; + Text name; + QString statusText; + bool statusHasOnlineColor = false; + bool disabledChecked = false; + }; - SingleTimer _loadTimer; + void updateRowWithTop(int rowTop); + int getSelectedRowTop() const; + void updateSelectedRow(); + int getRowTopWithPeer(PeerData *peer) const; + void updateRowWithPeer(PeerData *peer); + void addAdminDone(const MTPUpdates &result, mtpRequestId req); + bool addAdminFail(const RPCError &error, mtpRequestId req); + + void paintDialog(Painter &p, PeerData *peer, ContactData *data, bool sel); + void paintDisabledCheckUserpic(Painter &p, PeerData *peer, int x, int y, int outerWidth) const; + + void changeCheckState(Dialogs::Row *row); + void changeCheckState(ContactData *data, PeerData *peer); + + template + void addDialogsToList(FilterCallback callback); + + bool usingMultiSelect() const { + return (_chat != nullptr) || (_creating != CreatingGroupNone && (!_channel || _membersFilter != MembersFilter::Admins)); + } + + int32 _rowHeight; + int _newItemHeight = 0; + bool _newItemSel = false; + + ChatData *_chat = nullptr; + ChannelData *_channel = nullptr; + MembersFilter _membersFilter = MembersFilter::Recent; + UserData *_bot = nullptr; + CreatingGroupType _creating = CreatingGroupNone; + MembersAlreadyIn _already; + + Checkbox _allAdmins; + int32 _aboutWidth; + Text _aboutAllAdmins, _aboutAdmins; + + PeerData *_addToPeer = nullptr; + UserData *_addAdmin = nullptr; + mtpRequestId _addAdminRequestId = 0; + ConfirmBox *_addAdminBox = nullptr; + + int32 _time; + + std_::unique_ptr _customList; + Dialogs::IndexedList *_contacts = nullptr; + Dialogs::Row *_sel = nullptr; + QString _filter; + typedef QVector FilteredDialogs; + FilteredDialogs _filtered; + int _filteredSel = -1; + bool _mouseSel = false; + + int _selCount = 0; + + typedef QMap ContactsData; + ContactsData _contactsData; + typedef QMap CheckedContacts; + CheckedContacts _checkedContacts; + + ContactData *contactData(Dialogs::Row *row); + + bool _searching = false; + QString _lastQuery; + typedef QVector ByUsernameRows; + typedef QVector ByUsernameDatas; + ByUsernameRows _byUsername, _byUsernameFiltered; + ByUsernameDatas d_byUsername, d_byUsernameFiltered; // filtered is partly subset of d_byUsername, partly subset of _byUsernameDatas + ByUsernameDatas _byUsernameDatas; + int _byUsernameSel = -1; + + QPoint _lastMousePos; + LinkButton _addContactLnk; + + bool _saving = false; + bool _allAdminsChecked = false; }; - -inline Ui::RoundImageCheckbox::PaintRoundImage PaintUserpicCallback(PeerData *peer) { - return [peer](Painter &p, int x, int y, int outerWidth, int size) { - peer->paintUserpicLeft(p, size, x, y, outerWidth); - }; -} diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp new file mode 100644 index 000000000..a2c3c972c --- /dev/null +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -0,0 +1,607 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "boxes/members_box.h" + +#include "styles/style_dialogs.h" +#include "lang.h" +#include "mainwidget.h" +#include "mainwindow.h" +#include "boxes/contactsbox.h" +#include "boxes/confirmbox.h" +#include "observer_peer.h" + +MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox(st::boxScroll) +, _inner(this, channel, filter) { + ItemListBox::init(_inner); + + connect(_inner, SIGNAL(addRequested()), this, SLOT(onAdd())); + + connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); + connect(_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int))); + + connect(&_loadTimer, SIGNAL(timeout()), _inner, SLOT(load())); + + prepare(); +} + +void MembersBox::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Down) { + _inner->selectSkip(1); + } else if (e->key() == Qt::Key_Up) { + _inner->selectSkip(-1); + } else if (e->key() == Qt::Key_PageDown) { + _inner->selectSkipPage(scrollArea()->height(), 1); + } else if (e->key() == Qt::Key_PageUp) { + _inner->selectSkipPage(scrollArea()->height(), -1); + } else { + ItemListBox::keyPressEvent(e); + } +} + +void MembersBox::paintEvent(QPaintEvent *e) { + Painter p(this); + if (paint(p)) return; + + QString title(lang(_inner->filter() == MembersFilter::Recent ? lng_channel_members : lng_channel_admins)); + paintTitle(p, title); +} + +void MembersBox::resizeEvent(QResizeEvent *e) { + ItemListBox::resizeEvent(e); + _inner->resize(width(), _inner->height()); +} + +void MembersBox::onScroll() { + _inner->loadProfilePhotos(scrollArea()->scrollTop()); +} + +void MembersBox::onAdd() { + if (_inner->filter() == MembersFilter::Recent && _inner->channel()->membersCount() >= (_inner->channel()->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax())) { + Ui::showLayer(new MaxInviteBox(_inner->channel()->inviteLink()), KeepOtherLayers); + return; + } + ContactsBox *box = new ContactsBox(_inner->channel(), _inner->filter(), _inner->already()); + if (_inner->filter() == MembersFilter::Recent) { + Ui::showLayer(box); + } else { + _addBox = box; + connect(_addBox, SIGNAL(adminAdded()), this, SLOT(onAdminAdded())); + Ui::showLayer(_addBox, KeepOtherLayers); + } +} + +void MembersBox::onAdminAdded() { + if (!_addBox) return; + _addBox->onClose(); + _addBox = 0; + _loadTimer.start(ReloadChannelMembersTimeout); +} + +MembersBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter filter) : ScrolledWidget(parent) +, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) +, _newItemHeight((channel->amCreator() && (channel->membersCount() < (channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!channel->isMegagroup() && !channel->isPublic()) || filter == MembersFilter::Admins)) ? st::contactsNewItemHeight : 0) +, _newItemSel(false) +, _channel(channel) +, _filter(filter) +, _kickText(lang(lng_profile_kick)) +, _time(0) +, _kickWidth(st::normalFont->width(_kickText)) +, _sel(-1) +, _kickSel(-1) +, _kickDown(-1) +, _mouseSel(false) +, _kickConfirm(0) +, _kickRequestId(0) +, _kickBox(0) +, _loading(true) +, _loadingRequestId(0) +, _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right()) +, _about(_aboutWidth) +, _aboutHeight(0) { + subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + + connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&))); + connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*))); + + refresh(); + + load(); +} + +void MembersBox::Inner::load() { + if (!_loadingRequestId) { + _loadingRequestId = MTP::send(MTPchannels_GetParticipants(_channel->inputChannel, (_filter == MembersFilter::Recent) ? MTP_channelParticipantsRecent() : MTP_channelParticipantsAdmins(), MTP_int(0), MTP_int(Global::ChatSizeMax())), rpcDone(&Inner::membersReceived), rpcFail(&Inner::membersFailed)); + } +} + +void MembersBox::Inner::paintEvent(QPaintEvent *e) { + QRect r(e->rect()); + Painter p(this); + + _time = unixtime(); + p.fillRect(r, st::white->b); + + int32 yFrom = r.y() - st::membersPadding.top(), yTo = r.y() + r.height() - st::membersPadding.top(); + p.translate(0, st::membersPadding.top()); + if (_rows.isEmpty()) { + p.setFont(st::noContactsFont->f); + p.setPen(st::noContactsColor->p); + p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); + } else { + if (_newItemHeight) { + p.fillRect(0, 0, width(), _newItemHeight, (_newItemSel ? st::contactsBgOver : st::white)->b); + p.drawSpriteLeft(st::contactsNewItemIconPosition.x(), st::contactsNewItemIconPosition.y(), width(), st::contactsNewItemIcon); + p.setFont(st::contactsNameFont); + p.setPen(st::contactsNewItemFg); + p.drawTextLeft(st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(), st::contactsNewItemTop, width(), lang(_filter == MembersFilter::Admins ? lng_channel_add_admins : lng_channel_add_members)); + + yFrom -= _newItemHeight; + yTo -= _newItemHeight; + p.translate(0, _newItemHeight); + } + int32 from = floorclamp(yFrom, _rowHeight, 0, _rows.size()); + int32 to = ceilclamp(yTo, _rowHeight, 0, _rows.size()); + p.translate(0, from * _rowHeight); + for (; from < to; ++from) { + bool sel = (from == _sel); + bool kickSel = (from == _kickSel && (_kickDown < 0 || from == _kickDown)); + bool kickDown = kickSel && (from == _kickDown); + paintDialog(p, _rows[from], data(from), sel, kickSel, kickDown); + p.translate(0, _rowHeight); + } + if (to == _rows.size() && _filter == MembersFilter::Recent && (_rows.size() < _channel->membersCount() || _rows.size() >= Global::ChatSizeMax())) { + p.setPen(st::stickersReorderFg); + _about.draw(p, st::contactsPadding.left(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center); + } + } +} + +void MembersBox::Inner::enterEvent(QEvent *e) { + setMouseTracking(true); +} + +void MembersBox::Inner::leaveEvent(QEvent *e) { + _mouseSel = false; + setMouseTracking(false); + if (_sel >= 0) { + clearSel(); + } +} + +void MembersBox::Inner::mouseMoveEvent(QMouseEvent *e) { + _mouseSel = true; + _lastMousePos = e->globalPos(); + updateSel(); +} + +void MembersBox::Inner::mousePressEvent(QMouseEvent *e) { + _mouseSel = true; + _lastMousePos = e->globalPos(); + updateSel(); + if (e->button() == Qt::LeftButton && _kickSel < 0) { + chooseParticipant(); + } + _kickDown = _kickSel; + update(); +} + +void MembersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { + _mouseSel = true; + _lastMousePos = e->globalPos(); + updateSel(); + if (_kickDown >= 0 && _kickDown == _kickSel && !_kickRequestId) { + _kickConfirm = _rows.at(_kickSel); + if (_kickBox) _kickBox->deleteLater(); + _kickBox = new ConfirmBox((_filter == MembersFilter::Recent ? (_channel->isMegagroup() ? lng_profile_sure_kick : lng_profile_sure_kick_channel) : lng_profile_sure_kick_admin)(lt_user, _kickConfirm->firstName)); + connect(_kickBox, SIGNAL(confirmed()), this, SLOT(onKickConfirm())); + connect(_kickBox, SIGNAL(destroyed(QObject*)), this, SLOT(onKickBoxDestroyed(QObject*))); + Ui::showLayer(_kickBox, KeepOtherLayers); + } + _kickDown = -1; +} + +void MembersBox::Inner::onKickBoxDestroyed(QObject *obj) { + if (_kickBox == obj) { + _kickBox = 0; + } +} + +void MembersBox::Inner::onKickConfirm() { + if (_filter == MembersFilter::Recent) { + _kickRequestId = MTP::send(MTPchannels_KickFromChannel(_channel->inputChannel, _kickConfirm->inputUser, MTP_bool(true)), rpcDone(&Inner::kickDone), rpcFail(&Inner::kickFail)); + } else { + _kickRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _kickConfirm->inputUser, MTP_channelRoleEmpty()), rpcDone(&Inner::kickAdminDone), rpcFail(&Inner::kickFail)); + } +} + +void MembersBox::Inner::paintDialog(Painter &p, PeerData *peer, MemberData *data, bool sel, bool kickSel, bool kickDown) { + UserData *user = peer->asUser(); + + p.fillRect(0, 0, width(), _rowHeight, (sel ? st::contactsBgOver : st::white)->b); + peer->paintUserpicLeft(p, st::contactsPhotoSize, st::contactsPadding.left(), st::contactsPadding.top(), width()); + + p.setPen(st::black); + + int32 namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); + int32 namew = width() - namex - st::contactsPadding.right() - (data->canKick ? (_kickWidth + st::contactsCheckPosition.x() * 2) : 0); + if (peer->isVerified()) { + auto icon = &st::dialogsVerifiedIcon; + namew -= icon->width(); + icon->paint(p, namex + qMin(data->name.maxWidth(), namew), st::contactsPadding.top() + st::contactsNameTop, width()); + } + data->name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width()); + + if (data->canKick) { + p.setFont((kickSel ? st::linkOverFont : st::linkFont)->f); + if (kickDown) { + p.setPen(st::btnDefLink.downColor->p); + } else { + p.setPen(st::btnDefLink.color->p); + } + p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), _kickText, _kickWidth); + } + + p.setFont(st::contactsStatusFont->f); + p.setPen(data->onlineColor ? st::contactsStatusFgOnline : (sel ? st::contactsStatusFgOver : st::contactsStatusFg)); + p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsStatusTop, width(), data->online); +} + +void MembersBox::Inner::selectSkip(int32 dir) { + _time = unixtime(); + _mouseSel = false; + + int cur = -1; + if (_newItemHeight && _newItemSel) { + cur = 0; + } else if (_sel >= 0) { + cur = _sel + (_newItemHeight ? 1 : 0); + } + cur += dir; + if (cur <= 0) { + _newItemSel = _newItemHeight ? true : false; + _sel = (_newItemSel || _rows.isEmpty()) ? -1 : 0; + } else if (cur >= _rows.size() + (_newItemHeight ? 1 : 0)) { + _sel = -1; + } else { + _sel = cur - (_newItemHeight ? 1 : 0); + } + if (dir > 0) { + if (_sel < 0 || _sel >= _rows.size()) { + _sel = -1; + } + } else { + if (!_rows.isEmpty()) { + if (_sel < 0 && !_newItemSel) _sel = _rows.size() - 1; + } + } + if (_newItemSel) { + emit mustScrollTo(0, _newItemHeight); + } else if (_sel >= 0) { + emit mustScrollTo(_newItemHeight + _sel * _rowHeight, _newItemHeight + (_sel + 1) * _rowHeight); + } + + update(); +} + +void MembersBox::Inner::selectSkipPage(int32 h, int32 dir) { + int32 points = h / _rowHeight; + if (!points) return; + selectSkip(points * dir); +} + +void MembersBox::Inner::loadProfilePhotos(int32 yFrom) { + int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5; + MTP::clearLoaderPriorities(); + + if (yTo < 0) return; + if (yFrom < 0) yFrom = 0; + + if (!_rows.isEmpty()) { + int32 from = (yFrom - _newItemHeight) / _rowHeight; + if (from < 0) from = 0; + if (from < _rows.size()) { + int32 to = ((yTo - _newItemHeight) / _rowHeight) + 1; + if (to > _rows.size()) to = _rows.size(); + + for (; from < to; ++from) { + _rows[from]->loadUserpic(); + } + } + } +} + +void MembersBox::Inner::chooseParticipant() { + if (_newItemSel) { + emit addRequested(); + return; + } + if (_sel < 0 || _sel >= _rows.size()) return; + if (PeerData *peer = _rows[_sel]) { + Ui::hideLayer(); + Ui::showPeerProfile(peer); + } +} + +void MembersBox::Inner::refresh() { + if (_rows.isEmpty()) { + resize(width(), st::membersPadding.top() + st::noContactsHeight + st::membersPadding.bottom()); + _aboutHeight = 0; + } else { + _about.setText(st::boxTextFont, lng_channel_only_last_shown(lt_count, _rows.size())); + _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); + if (_filter != MembersFilter::Recent || (_rows.size() >= _channel->membersCount() && _rows.size() < Global::ChatSizeMax())) { + _aboutHeight = 0; + } + resize(width(), st::membersPadding.top() + _newItemHeight + _rows.size() * _rowHeight + st::membersPadding.bottom() + _aboutHeight); + } + update(); +} + +ChannelData *MembersBox::Inner::channel() const { + return _channel; +} + +MembersFilter MembersBox::Inner::filter() const { + return _filter; +} + +MembersAlreadyIn MembersBox::Inner::already() const { + MembersAlreadyIn result; + for_const (auto peer, _rows) { + if (peer->isUser()) { + result.insert(peer->asUser()); + } + } + return result; +} + +void MembersBox::Inner::clearSel() { + updateSelectedRow(); + _newItemSel = false; + _sel = _kickSel = _kickDown = -1; + _lastMousePos = QCursor::pos(); + updateSel(); +} + +MembersBox::Inner::MemberData *MembersBox::Inner::data(int32 index) { + if (MemberData *result = _datas.at(index)) { + return result; + } + MemberData *result = _datas[index] = new MemberData(); + result->name.setText(st::contactsNameFont, _rows[index]->name, _textNameOptions); + int32 t = unixtime(); + result->online = App::onlineText(_rows[index], t);// lng_mediaview_date_time(lt_date, _dates[index].date().toString(qsl("dd.MM.yy")), lt_time, _dates[index].time().toString(cTimeFormat())); + result->onlineColor = App::onlineColorUse(_rows[index], t); + if (_filter == MembersFilter::Recent) { + result->canKick = (_channel->amCreator() || _channel->amEditor() || _channel->amModerator()) ? (_roles[index] == MemberRole::None) : false; + } else if (_filter == MembersFilter::Admins) { + result->canKick = _channel->amCreator() ? (_roles[index] == MemberRole::Editor || _roles[index] == MemberRole::Moderator) : false; + } else { + result->canKick = false; + } + return result; +} + +void MembersBox::Inner::clear() { + for (int32 i = 0, l = _datas.size(); i < l; ++i) { + delete _datas.at(i); + } + _datas.clear(); + _rows.clear(); + _dates.clear(); + _roles.clear(); + if (_kickBox) _kickBox->deleteLater(); + clearSel(); +} + +MembersBox::Inner::~Inner() { + clear(); +} + +void MembersBox::Inner::updateSel() { + if (!_mouseSel) return; + + QPoint p(mapFromGlobal(_lastMousePos)); + p.setY(p.y() - st::membersPadding.top()); + bool in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); + bool newItemSel = (in && p.y() >= 0 && p.y() < _newItemHeight); + int32 newSel = (in && !newItemSel && p.y() >= _newItemHeight && p.y() < _newItemHeight + _rows.size() * _rowHeight) ? ((p.y() - _newItemHeight) / _rowHeight) : -1; + int32 newKickSel = newSel; + if (newSel >= 0 && (!data(newSel)->canKick || !QRect(width() - _kickWidth - st::contactsPadding.right() - st::contactsCheckPosition.x(), _newItemHeight + newSel * _rowHeight + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _kickWidth, st::normalFont->height).contains(p))) { + newKickSel = -1; + } + if (newSel != _sel || newKickSel != _kickSel || newItemSel != _newItemSel) { + updateSelectedRow(); + _newItemSel = newItemSel; + _sel = newSel; + _kickSel = newKickSel; + updateSelectedRow(); + setCursor(_kickSel >= 0 ? style::cur_pointer : style::cur_default); + } +} + +void MembersBox::Inner::peerUpdated(PeerData *peer) { + update(); +} + +void MembersBox::Inner::updateSelectedRow() { + if (_newItemSel) { + update(0, st::membersPadding.top(), width(), _newItemHeight); + } + if (_sel >= 0) { + update(0, st::membersPadding.top() + _newItemHeight + _sel * _rowHeight, width(), _rowHeight); + } +} + +void MembersBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { + for (int32 i = 0, l = _rows.size(); i < l; ++i) { + if (_rows.at(i) == peer) { + if (_datas.at(i)) { + _datas.at(i)->name.setText(st::contactsNameFont, peer->name, _textNameOptions); + update(0, st::membersPadding.top() + i * _rowHeight, width(), _rowHeight); + } else { + break; + } + } + } +} + +void MembersBox::Inner::membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req) { + clear(); + _loadingRequestId = 0; + + if (result.type() == mtpc_channels_channelParticipants) { + const auto &d(result.c_channels_channelParticipants()); + const auto &v(d.vparticipants.c_vector().v); + _rows.reserve(v.size()); + _datas.reserve(v.size()); + _dates.reserve(v.size()); + _roles.reserve(v.size()); + + if (_filter == MembersFilter::Recent && _channel->membersCount() < d.vcount.v) { + _channel->setMembersCount(d.vcount.v); + if (App::main()) emit App::main()->peerUpdated(_channel); + } else if (_filter == MembersFilter::Admins && _channel->adminsCount() < d.vcount.v) { + _channel->setAdminsCount(d.vcount.v); + if (App::main()) emit App::main()->peerUpdated(_channel); + } + App::feedUsers(d.vusers); + + for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) { + int32 userId = 0, addedTime = 0; + MemberRole role = MemberRole::None; + switch (i->type()) { + case mtpc_channelParticipant: + userId = i->c_channelParticipant().vuser_id.v; + addedTime = i->c_channelParticipant().vdate.v; + break; + case mtpc_channelParticipantSelf: + role = MemberRole::Self; + userId = i->c_channelParticipantSelf().vuser_id.v; + addedTime = i->c_channelParticipantSelf().vdate.v; + break; + case mtpc_channelParticipantModerator: + role = MemberRole::Moderator; + userId = i->c_channelParticipantModerator().vuser_id.v; + addedTime = i->c_channelParticipantModerator().vdate.v; + break; + case mtpc_channelParticipantEditor: + role = MemberRole::Editor; + userId = i->c_channelParticipantEditor().vuser_id.v; + addedTime = i->c_channelParticipantEditor().vdate.v; + break; + case mtpc_channelParticipantKicked: + userId = i->c_channelParticipantKicked().vuser_id.v; + addedTime = i->c_channelParticipantKicked().vdate.v; + role = MemberRole::Kicked; + break; + case mtpc_channelParticipantCreator: + userId = i->c_channelParticipantCreator().vuser_id.v; + addedTime = _channel->date; + role = MemberRole::Creator; + break; + } + if (UserData *user = App::userLoaded(userId)) { + _rows.push_back(user); + _dates.push_back(date(addedTime)); + _roles.push_back(role); + _datas.push_back(0); + } + } + + // update admins if we got all of them + if (_filter == MembersFilter::Admins && _channel->isMegagroup() && _rows.size() < Global::ChatSizeMax()) { + _channel->mgInfo->lastAdmins.clear(); + for (int32 i = 0, l = _rows.size(); i != l; ++i) { + if (_roles.at(i) == MemberRole::Creator || _roles.at(i) == MemberRole::Editor) { + _channel->mgInfo->lastAdmins.insert(_rows.at(i)); + } + } + + Notify::peerUpdatedDelayed(_channel, Notify::PeerUpdate::Flag::AdminsChanged); + } + } + if (_rows.isEmpty()) { + _rows.push_back(App::self()); + _dates.push_back(date(MTP_int(_channel->date))); + _roles.push_back(MemberRole::Self); + _datas.push_back(0); + } + + clearSel(); + _loading = false; + refresh(); + + emit loaded(); +} + +bool MembersBox::Inner::membersFailed(const RPCError &error, mtpRequestId req) { + if (MTP::isDefaultHandledError(error)) return false; + + Ui::hideLayer(); + return true; +} + +void MembersBox::Inner::kickDone(const MTPUpdates &result, mtpRequestId req) { + App::main()->sentUpdatesReceived(result); + + if (_kickRequestId != req) return; + removeKicked(); + if (_kickBox) _kickBox->onClose(); +} + +void MembersBox::Inner::kickAdminDone(const MTPUpdates &result, mtpRequestId req) { + if (_kickRequestId != req) return; + if (App::main()) App::main()->sentUpdatesReceived(result); + removeKicked(); + if (_kickBox) _kickBox->onClose(); +} + +bool MembersBox::Inner::kickFail(const RPCError &error, mtpRequestId req) { + if (MTP::isDefaultHandledError(error)) return false; + + if (_kickBox) _kickBox->onClose(); + load(); + return true; +} + +void MembersBox::Inner::removeKicked() { + _kickRequestId = 0; + int32 index = _rows.indexOf(_kickConfirm); + if (index >= 0) { + _rows.removeAt(index); + delete _datas.at(index); + _datas.removeAt(index); + _dates.removeAt(index); + _roles.removeAt(index); + clearSel(); + if (_filter == MembersFilter::Recent && _channel->membersCount() > 1) { + _channel->setMembersCount(_channel->membersCount() - 1); + if (App::main()) emit App::main()->peerUpdated(_channel); + } else if (_filter == MembersFilter::Admins && _channel->adminsCount() > 1) { + _channel->setAdminsCount(_channel->adminsCount() - 1); + if (App::main()) emit App::main()->peerUpdated(_channel); + } + refresh(); + } + _kickConfirm = 0; +} diff --git a/Telegram/SourceFiles/boxes/members_box.h b/Telegram/SourceFiles/boxes/members_box.h new file mode 100644 index 000000000..f93f7a7b2 --- /dev/null +++ b/Telegram/SourceFiles/boxes/members_box.h @@ -0,0 +1,178 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "abstractbox.h" +#include "core/single_timer.h" +#include "ui/effects/round_image_checkbox.h" + +class ContactsBox; +class ConfirmBox; + +enum class MembersFilter { + Recent, + Admins, +}; +using MembersAlreadyIn = OrderedSet; + +class MembersBox : public ItemListBox { + Q_OBJECT + +public: + MembersBox(ChannelData *channel, MembersFilter filter); + +public slots: + void onScroll(); + + void onAdd(); + void onAdminAdded(); + +protected: + void keyPressEvent(QKeyEvent *e) override; + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +private: + class Inner; + ChildWidget _inner; + + ContactsBox *_addBox = nullptr; + + SingleTimer _loadTimer; + +}; + +// This class is hold in header because it requires Qt preprocessing. +class MembersBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { + Q_OBJECT + +public: + Inner(QWidget *parent, ChannelData *channel, MembersFilter filter); + + void selectSkip(int32 dir); + void selectSkipPage(int32 h, int32 dir); + + void loadProfilePhotos(int32 yFrom); + void chooseParticipant(); + + void refresh(); + + ChannelData *channel() const; + MembersFilter filter() const; + + bool isLoaded() const { + return !_loading; + } + void clearSel(); + + MembersAlreadyIn already() const; + + ~Inner(); + +signals: + void mustScrollTo(int ymin, int ymax); + void addRequested(); + void loaded(); + +public slots: + void load(); + + void updateSel(); + void peerUpdated(PeerData *peer); + void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); + void onKickConfirm(); + void onKickBoxDestroyed(QObject *obj); + +protected: + void paintEvent(QPaintEvent *e) override; + void enterEvent(QEvent *e) override; + void leaveEvent(QEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + +private: + struct MemberData { + Text name; + QString online; + bool onlineColor; + bool canKick; + }; + + void updateSelectedRow(); + MemberData *data(int32 index); + + void paintDialog(Painter &p, PeerData *peer, MemberData *data, bool sel, bool kickSel, bool kickDown); + + void membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req); + bool membersFailed(const RPCError &error, mtpRequestId req); + + void kickDone(const MTPUpdates &result, mtpRequestId req); + void kickAdminDone(const MTPUpdates &result, mtpRequestId req); + bool kickFail(const RPCError &error, mtpRequestId req); + void removeKicked(); + + void clear(); + + int32 _rowHeight, _newItemHeight; + bool _newItemSel; + + ChannelData *_channel; + MembersFilter _filter; + + QString _kickText; + int32 _time, _kickWidth; + + int32 _sel, _kickSel, _kickDown; + bool _mouseSel; + + UserData *_kickConfirm; + mtpRequestId _kickRequestId; + + ConfirmBox *_kickBox; + + enum class MemberRole { + None, + Self, + Creator, + Editor, + Moderator, + Kicked + }; + + bool _loading; + mtpRequestId _loadingRequestId; + typedef QVector MemberRows; + typedef QVector MemberDates; + typedef QVector MemberRoles; + typedef QVector MemberDatas; + MemberRows _rows; + MemberDates _dates; + MemberRoles _roles; + MemberDatas _datas; + + int32 _aboutWidth; + Text _about; + int32 _aboutHeight; + + QPoint _lastMousePos; + +}; diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index b2fc879fa..52003f6ff 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.cpp +++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp @@ -30,213 +30,23 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "countries.h" #include "confirmbox.h" -SessionsInner::SessionsInner(SessionsList *list, SessionData *current) : TWidget() -, _list(list) -, _current(current) -, _terminating(0) -, _terminateAll(this, lang(lng_sessions_terminate_all), st::redBoxLinkButton) -, _terminateBox(0) { - connect(&_terminateAll, SIGNAL(clicked()), this, SLOT(onTerminateAll())); - _terminateAll.hide(); - setAttribute(Qt::WA_OpaquePaintEvent); -} - -void SessionsInner::paintEvent(QPaintEvent *e) { - QRect r(e->rect()); - Painter p(this); - - p.fillRect(r, st::white->b); - int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPos.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip; - int32 w = width(); - - if (_current->active.isEmpty() && _list->isEmpty()) { - p.setFont(st::noContactsFont->f); - p.setPen(st::noContactsColor->p); - p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); - return; - } - - if (r.y() <= st::sessionCurrentHeight) { - p.translate(0, st::sessionCurrentPadding.top()); - p.setFont(st::sessionNameFont->f); - p.setPen(st::black->p); - p.drawTextLeft(x, st::sessionPadding.top(), w, _current->name, _current->nameWidth); - - p.setFont(st::sessionActiveFont->f); - p.setPen(st::sessionActiveColor->p); - p.drawTextRight(x, st::sessionPadding.top(), w, _current->active, _current->activeWidth); - - p.setFont(st::sessionInfoFont->f); - p.setPen(st::black->p); - p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, _current->info, _current->infoWidth); - p.setPen(st::sessionInfoColor->p); - p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, _current->ip, _current->ipWidth); - } - p.translate(0, st::sessionCurrentHeight - st::sessionCurrentPadding.top()); - if (_list->isEmpty()) { - p.setFont(st::sessionInfoFont->f); - p.setPen(st::sessionInfoColor->p); - p.drawText(QRect(st::sessionPadding.left(), 0, width() - st::sessionPadding.left() - st::sessionPadding.right(), st::noContactsHeight), lang(lng_sessions_other_desc), style::al_topleft); - return; - } - - p.setFont(st::linkFont->f); - int32 count = _list->size(); - int32 from = floorclamp(r.y() - st::sessionCurrentHeight, st::sessionHeight, 0, count); - int32 to = ceilclamp(r.y() + r.height() - st::sessionCurrentHeight, st::sessionHeight, 0, count); - p.translate(0, from * st::sessionHeight); - for (int32 i = from; i < to; ++i) { - const SessionData &auth(_list->at(i)); - - p.setFont(st::sessionNameFont->f); - p.setPen(st::black->p); - p.drawTextLeft(x, st::sessionPadding.top(), w, auth.name, auth.nameWidth); - - p.setFont(st::sessionActiveFont->f); - p.setPen(st::sessionActiveColor->p); - p.drawTextRight(xact, st::sessionPadding.top(), w, auth.active, auth.activeWidth); - - p.setFont(st::sessionInfoFont->f); - p.setPen(st::black->p); - p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, auth.info, auth.infoWidth); - p.setPen(st::sessionInfoColor->p); - p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, auth.ip, auth.ipWidth); - - p.translate(0, st::sessionHeight); - } -} - -void SessionsInner::onTerminate() { - for (TerminateButtons::iterator i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) { - if (i.value()->getState() & Button::StateOver) { - _terminating = i.key(); - - if (_terminateBox) _terminateBox->deleteLater(); - _terminateBox = new ConfirmBox(lang(lng_settings_reset_one_sure), lang(lng_settings_reset_button), st::attentionBoxButton); - connect(_terminateBox, SIGNAL(confirmed()), this, SLOT(onTerminateSure())); - connect(_terminateBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoTerminateBox(QObject*))); - Ui::showLayer(_terminateBox, KeepOtherLayers); - } - } -} - -void SessionsInner::onTerminateSure() { - if (_terminateBox) { - _terminateBox->onClose(); - _terminateBox = 0; - } - MTP::send(MTPaccount_ResetAuthorization(MTP_long(_terminating)), rpcDone(&SessionsInner::terminateDone, _terminating), rpcFail(&SessionsInner::terminateFail, _terminating)); - TerminateButtons::iterator i = _terminateButtons.find(_terminating); - if (i != _terminateButtons.cend()) { - i.value()->clearState(); - i.value()->hide(); - } -} - -void SessionsInner::onTerminateAll() { - if (_terminateBox) _terminateBox->deleteLater(); - _terminateBox = new ConfirmBox(lang(lng_settings_reset_sure), lang(lng_settings_reset_button), st::attentionBoxButton); - connect(_terminateBox, SIGNAL(confirmed()), this, SLOT(onTerminateAllSure())); - connect(_terminateBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoTerminateBox(QObject*))); - Ui::showLayer(_terminateBox, KeepOtherLayers); -} - -void SessionsInner::onTerminateAllSure() { - if (_terminateBox) { - _terminateBox->onClose(); - _terminateBox = 0; - } - MTP::send(MTPauth_ResetAuthorizations(), rpcDone(&SessionsInner::terminateAllDone), rpcFail(&SessionsInner::terminateAllFail)); - emit terminateAll(); -} - -void SessionsInner::onNoTerminateBox(QObject *obj) { - if (obj == _terminateBox) _terminateBox = 0; -} - -void SessionsInner::terminateDone(uint64 hash, const MTPBool &result) { - for (int32 i = 0, l = _list->size(); i < l; ++i) { - if (_list->at(i).hash == hash) { - _list->removeAt(i); - break; - } - } - listUpdated(); - emit oneTerminated(); -} - -bool SessionsInner::terminateFail(uint64 hash, const RPCError &error) { - if (MTP::isDefaultHandledError(error)) return false; - - TerminateButtons::iterator i = _terminateButtons.find(hash); - if (i != _terminateButtons.end()) { - i.value()->show(); - return true; - } - return false; -} - -void SessionsInner::terminateAllDone(const MTPBool &result) { - emit allTerminated(); -} - -bool SessionsInner::terminateAllFail(const RPCError &error) { - if (MTP::isDefaultHandledError(error)) return false; - emit allTerminated(); - return true; -} - -void SessionsInner::resizeEvent(QResizeEvent *e) { - _terminateAll.moveToLeft(st::sessionPadding.left(), st::sessionCurrentPadding.top() + st::sessionHeight + st::sessionCurrentPadding.bottom()); -} - -void SessionsInner::listUpdated() { - if (_list->isEmpty()) { - _terminateAll.hide(); - } else { - _terminateAll.show(); - } - for (TerminateButtons::iterator i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) { - i.value()->move(0, -1); - } - for (int32 i = 0, l = _list->size(); i < l; ++i) { - TerminateButtons::iterator j = _terminateButtons.find(_list->at(i).hash); - if (j == _terminateButtons.cend()) { - j = _terminateButtons.insert(_list->at(i).hash, new IconedButton(this, st::sessionTerminate)); - connect(j.value(), SIGNAL(clicked()), this, SLOT(onTerminate())); - } - j.value()->moveToRight(st::sessionTerminateSkip, st::sessionCurrentHeight + i * st::sessionHeight + st::sessionTerminateTop, width()); - j.value()->show(); - } - for (TerminateButtons::iterator i = _terminateButtons.begin(); i != _terminateButtons.cend();) { - if (i.value()->y() >= 0) { - ++i; - } else { - delete i.value(); - i = _terminateButtons.erase(i); - } - } - resize(width(), _list->isEmpty() ? (st::sessionCurrentHeight + st::noContactsHeight) : (st::sessionCurrentHeight + _list->size() * st::sessionHeight)); - update(); -} - SessionsBox::SessionsBox() : ScrollableBox(st::sessionsScroll) , _loading(true) -, _inner(&_list, &_current) +, _inner(this, &_list, &_current) , _shadow(this) , _done(this, lang(lng_about_done), st::defaultBoxButton) , _shortPollRequest(0) { setMaxHeight(st::sessionsHeight); connect(&_done, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_inner, SIGNAL(oneTerminated()), this, SLOT(onOneTerminated())); - connect(&_inner, SIGNAL(allTerminated()), this, SLOT(onAllTerminated())); - connect(&_inner, SIGNAL(terminateAll()), this, SLOT(onTerminateAll())); + connect(_inner, SIGNAL(oneTerminated()), this, SLOT(onOneTerminated())); + connect(_inner, SIGNAL(allTerminated()), this, SLOT(onAllTerminated())); + connect(_inner, SIGNAL(terminateAll()), this, SLOT(onTerminateAll())); connect(App::wnd(), SIGNAL(newAuthorization()), this, SLOT(onNewAuthorization())); connect(&_shortPollTimer, SIGNAL(timeout()), this, SLOT(onShortPollAuthorizations())); - init(&_inner, st::boxButtonPadding.bottom() + _done.height() + st::boxButtonPadding.top(), st::boxTitleHeight); - _inner.resize(width(), st::noContactsHeight); + init(_inner, st::boxButtonPadding.bottom() + _done.height() + st::boxButtonPadding.top(), st::boxTitleHeight); + _inner->resize(width(), st::noContactsHeight); prepare(); @@ -291,7 +101,7 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) { for (int32 i = 0; i < l; ++i) { const auto &d(v.at(i).c_authorization()); - SessionData data; + Data data; data.hash = d.vhash.v; QString appName, appVer = qs(d.vapp_version), systemVer = qs(d.vsystem_version), deviceModel = qs(d.vdevice_model); @@ -383,7 +193,7 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) { } } } - _inner.listUpdated(); + _inner->listUpdated(); if (!_done.isHidden()) { showAll(); update(); @@ -430,4 +240,194 @@ void SessionsBox::onTerminateAll() { showAll(); update(); } -} \ No newline at end of file +} + +SessionsBox::Inner::Inner(QWidget *parent, SessionsBox::List *list, SessionsBox::Data *current) : ScrolledWidget(parent) +, _list(list) +, _current(current) +, _terminating(0) +, _terminateAll(this, lang(lng_sessions_terminate_all), st::redBoxLinkButton) +, _terminateBox(0) { + connect(&_terminateAll, SIGNAL(clicked()), this, SLOT(onTerminateAll())); + _terminateAll.hide(); + setAttribute(Qt::WA_OpaquePaintEvent); +} + +void SessionsBox::Inner::paintEvent(QPaintEvent *e) { + QRect r(e->rect()); + Painter p(this); + + p.fillRect(r, st::white->b); + int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPos.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip; + int32 w = width(); + + if (_current->active.isEmpty() && _list->isEmpty()) { + p.setFont(st::noContactsFont->f); + p.setPen(st::noContactsColor->p); + p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); + return; + } + + if (r.y() <= st::sessionCurrentHeight) { + p.translate(0, st::sessionCurrentPadding.top()); + p.setFont(st::sessionNameFont->f); + p.setPen(st::black->p); + p.drawTextLeft(x, st::sessionPadding.top(), w, _current->name, _current->nameWidth); + + p.setFont(st::sessionActiveFont->f); + p.setPen(st::sessionActiveColor->p); + p.drawTextRight(x, st::sessionPadding.top(), w, _current->active, _current->activeWidth); + + p.setFont(st::sessionInfoFont->f); + p.setPen(st::black->p); + p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, _current->info, _current->infoWidth); + p.setPen(st::sessionInfoColor->p); + p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, _current->ip, _current->ipWidth); + } + p.translate(0, st::sessionCurrentHeight - st::sessionCurrentPadding.top()); + if (_list->isEmpty()) { + p.setFont(st::sessionInfoFont->f); + p.setPen(st::sessionInfoColor->p); + p.drawText(QRect(st::sessionPadding.left(), 0, width() - st::sessionPadding.left() - st::sessionPadding.right(), st::noContactsHeight), lang(lng_sessions_other_desc), style::al_topleft); + return; + } + + p.setFont(st::linkFont->f); + int32 count = _list->size(); + int32 from = floorclamp(r.y() - st::sessionCurrentHeight, st::sessionHeight, 0, count); + int32 to = ceilclamp(r.y() + r.height() - st::sessionCurrentHeight, st::sessionHeight, 0, count); + p.translate(0, from * st::sessionHeight); + for (int32 i = from; i < to; ++i) { + const SessionsBox::Data &auth(_list->at(i)); + + p.setFont(st::sessionNameFont->f); + p.setPen(st::black->p); + p.drawTextLeft(x, st::sessionPadding.top(), w, auth.name, auth.nameWidth); + + p.setFont(st::sessionActiveFont->f); + p.setPen(st::sessionActiveColor->p); + p.drawTextRight(xact, st::sessionPadding.top(), w, auth.active, auth.activeWidth); + + p.setFont(st::sessionInfoFont->f); + p.setPen(st::black->p); + p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, auth.info, auth.infoWidth); + p.setPen(st::sessionInfoColor->p); + p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, auth.ip, auth.ipWidth); + + p.translate(0, st::sessionHeight); + } +} + +void SessionsBox::Inner::onTerminate() { + for (TerminateButtons::iterator i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) { + if (i.value()->getState() & Button::StateOver) { + _terminating = i.key(); + + if (_terminateBox) _terminateBox->deleteLater(); + _terminateBox = new ConfirmBox(lang(lng_settings_reset_one_sure), lang(lng_settings_reset_button), st::attentionBoxButton); + connect(_terminateBox, SIGNAL(confirmed()), this, SLOT(onTerminateSure())); + connect(_terminateBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoTerminateBox(QObject*))); + Ui::showLayer(_terminateBox, KeepOtherLayers); + } + } +} + +void SessionsBox::Inner::onTerminateSure() { + if (_terminateBox) { + _terminateBox->onClose(); + _terminateBox = 0; + } + MTP::send(MTPaccount_ResetAuthorization(MTP_long(_terminating)), rpcDone(&Inner::terminateDone, _terminating), rpcFail(&Inner::terminateFail, _terminating)); + TerminateButtons::iterator i = _terminateButtons.find(_terminating); + if (i != _terminateButtons.cend()) { + i.value()->clearState(); + i.value()->hide(); + } +} + +void SessionsBox::Inner::onTerminateAll() { + if (_terminateBox) _terminateBox->deleteLater(); + _terminateBox = new ConfirmBox(lang(lng_settings_reset_sure), lang(lng_settings_reset_button), st::attentionBoxButton); + connect(_terminateBox, SIGNAL(confirmed()), this, SLOT(onTerminateAllSure())); + connect(_terminateBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoTerminateBox(QObject*))); + Ui::showLayer(_terminateBox, KeepOtherLayers); +} + +void SessionsBox::Inner::onTerminateAllSure() { + if (_terminateBox) { + _terminateBox->onClose(); + _terminateBox = 0; + } + MTP::send(MTPauth_ResetAuthorizations(), rpcDone(&Inner::terminateAllDone), rpcFail(&Inner::terminateAllFail)); + emit terminateAll(); +} + +void SessionsBox::Inner::onNoTerminateBox(QObject *obj) { + if (obj == _terminateBox) _terminateBox = 0; +} + +void SessionsBox::Inner::terminateDone(uint64 hash, const MTPBool &result) { + for (int32 i = 0, l = _list->size(); i < l; ++i) { + if (_list->at(i).hash == hash) { + _list->removeAt(i); + break; + } + } + listUpdated(); + emit oneTerminated(); +} + +bool SessionsBox::Inner::terminateFail(uint64 hash, const RPCError &error) { + if (MTP::isDefaultHandledError(error)) return false; + + TerminateButtons::iterator i = _terminateButtons.find(hash); + if (i != _terminateButtons.end()) { + i.value()->show(); + return true; + } + return false; +} + +void SessionsBox::Inner::terminateAllDone(const MTPBool &result) { + emit allTerminated(); +} + +bool SessionsBox::Inner::terminateAllFail(const RPCError &error) { + if (MTP::isDefaultHandledError(error)) return false; + emit allTerminated(); + return true; +} + +void SessionsBox::Inner::resizeEvent(QResizeEvent *e) { + _terminateAll.moveToLeft(st::sessionPadding.left(), st::sessionCurrentPadding.top() + st::sessionHeight + st::sessionCurrentPadding.bottom()); +} + +void SessionsBox::Inner::listUpdated() { + if (_list->isEmpty()) { + _terminateAll.hide(); + } else { + _terminateAll.show(); + } + for (TerminateButtons::iterator i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) { + i.value()->move(0, -1); + } + for (int32 i = 0, l = _list->size(); i < l; ++i) { + TerminateButtons::iterator j = _terminateButtons.find(_list->at(i).hash); + if (j == _terminateButtons.cend()) { + j = _terminateButtons.insert(_list->at(i).hash, new IconedButton(this, st::sessionTerminate)); + connect(j.value(), SIGNAL(clicked()), this, SLOT(onTerminate())); + } + j.value()->moveToRight(st::sessionTerminateSkip, st::sessionCurrentHeight + i * st::sessionHeight + st::sessionTerminateTop, width()); + j.value()->show(); + } + for (TerminateButtons::iterator i = _terminateButtons.begin(); i != _terminateButtons.cend();) { + if (i.value()->y() >= 0) { + ++i; + } else { + delete i.value(); + i = _terminateButtons.erase(i); + } + } + resize(width(), _list->isEmpty() ? (st::sessionCurrentHeight + st::noContactsHeight) : (st::sessionCurrentHeight + _list->size() * st::sessionHeight)); + update(); +} diff --git a/Telegram/SourceFiles/boxes/sessionsbox.h b/Telegram/SourceFiles/boxes/sessionsbox.h index 851bfa8fb..f591464f4 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.h +++ b/Telegram/SourceFiles/boxes/sessionsbox.h @@ -25,20 +25,58 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org class ConfirmBox; -struct SessionData { - uint64 hash; - - int32 activeTime; - int32 nameWidth, activeWidth, infoWidth, ipWidth; - QString name, active, info, ip; -}; -typedef QList SessionsList; - -class SessionsInner : public TWidget, public RPCSender { +class SessionsBox : public ScrollableBox, public RPCSender { Q_OBJECT public: - SessionsInner(SessionsList *list, SessionData *current); + SessionsBox(); + +public slots: + void onOneTerminated(); + void onAllTerminated(); + void onTerminateAll(); + void onShortPollAuthorizations(); + void onNewAuthorization(); + +protected: + void resizeEvent(QResizeEvent *e) override; + void paintEvent(QPaintEvent *e) override; + + void showAll() override; + +private: + struct Data { + uint64 hash; + + int32 activeTime; + int32 nameWidth, activeWidth, infoWidth, ipWidth; + QString name, active, info, ip; + }; + using List = QList; + + void gotAuthorizations(const MTPaccount_Authorizations &result); + + bool _loading; + + Data _current; + List _list; + + class Inner; + ChildWidget _inner; + ScrollableBoxShadow _shadow; + BoxButton _done; + + SingleTimer _shortPollTimer; + mtpRequestId _shortPollRequest; + +}; + +// This class is hold in header because it requires Qt preprocessing. +class SessionsBox::Inner : public ScrolledWidget, public RPCSender { + Q_OBJECT + +public: + Inner(QWidget *parent, SessionsBox::List *list, SessionsBox::Data *current); void listUpdated(); @@ -65,8 +103,8 @@ private: void terminateAllDone(const MTPBool &res); bool terminateAllFail(const RPCError &error); - SessionsList *_list; - SessionData *_current; + SessionsBox::List *_list; + SessionsBox::Data *_current; typedef QMap TerminateButtons; TerminateButtons _terminateButtons; @@ -76,39 +114,3 @@ private: ConfirmBox *_terminateBox; }; - -class SessionsBox : public ScrollableBox, public RPCSender { - Q_OBJECT - -public: - SessionsBox(); - -public slots: - void onOneTerminated(); - void onAllTerminated(); - void onTerminateAll(); - void onShortPollAuthorizations(); - void onNewAuthorization(); - -protected: - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - - void showAll() override; - -private: - void gotAuthorizations(const MTPaccount_Authorizations &result); - - bool _loading; - - SessionData _current; - SessionsList _list; - - SessionsInner _inner; - ScrollableBoxShadow _shadow; - BoxButton _done; - - SingleTimer _shortPollTimer; - mtpRequestId _shortPollRequest; - -}; diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 15185e72f..bc8135fda 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -32,6 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "apiwrap.h" #include "ui/toast/toast.h" +#include "ui/buttons/icon_button.h" #include "history/history_media_types.h" #include "boxes/contactsbox.h" @@ -241,9 +242,7 @@ void ShareBox::onScroll() { _inner->setVisibleTopBottom(scrollTop, scrollTop + scroll->height()); } -namespace internal { - -ShareInner::ShareInner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : ScrolledWidget(parent) +ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : ScrolledWidget(parent) , _filterCallback(std_::move(filterCallback)) , _chatsIndexed(std_::make_unique(Dialogs::SortMode::Add)) { _rowsTop = st::shareRowsTop; @@ -269,19 +268,19 @@ ShareInner::ShareInner(QWidget *parent, ShareBox::FilterCallback &&filterCallbac subscribe(FileDownload::ImageLoaded(), [this] { update(); }); } -void ShareInner::setVisibleTopBottom(int visibleTop, int visibleBottom) { +void ShareBox::Inner::setVisibleTopBottom(int visibleTop, int visibleBottom) { loadProfilePhotos(visibleTop); } -void ShareInner::activateSkipRow(int direction) { +void ShareBox::Inner::activateSkipRow(int direction) { activateSkipColumn(direction * _columnCount); } -int ShareInner::displayedChatsCount() const { +int ShareBox::Inner::displayedChatsCount() const { return _filter.isEmpty() ? _chatsIndexed->size() : (_filtered.size() + d_byUsernameFiltered.size()); } -void ShareInner::activateSkipColumn(int direction) { +void ShareBox::Inner::activateSkipColumn(int direction) { if (_active < 0) { if (direction > 0) { setActive(0); @@ -299,11 +298,11 @@ void ShareInner::activateSkipColumn(int direction) { setActive(active); } -void ShareInner::activateSkipPage(int pageHeight, int direction) { +void ShareBox::Inner::activateSkipPage(int pageHeight, int direction) { activateSkipRow(direction * (pageHeight / _rowHeight)); } -void ShareInner::notifyPeerUpdated(const Notify::PeerUpdate &update) { +void ShareBox::Inner::notifyPeerUpdated(const Notify::PeerUpdate &update) { if (update.flags & Notify::PeerUpdate::Flag::NameChanged) { _chatsIndexed->peerNameChanged(update.peer, update.oldNames, update.oldNameFirstChars); } @@ -311,7 +310,7 @@ void ShareInner::notifyPeerUpdated(const Notify::PeerUpdate &update) { updateChat(update.peer); } -void ShareInner::updateChat(PeerData *peer) { +void ShareBox::Inner::updateChat(PeerData *peer) { auto i = _dataMap.find(peer); if (i != _dataMap.cend()) { updateChatName(i.value(), peer); @@ -319,11 +318,11 @@ void ShareInner::updateChat(PeerData *peer) { } } -void ShareInner::updateChatName(Chat *chat, PeerData *peer) { +void ShareBox::Inner::updateChatName(Chat *chat, PeerData *peer) { chat->name.setText(st::shareNameFont, peer->name, _textNameOptions); } -void ShareInner::repaintChatAtIndex(int index) { +void ShareBox::Inner::repaintChatAtIndex(int index) { if (index < 0) return; auto row = index / _columnCount; @@ -331,7 +330,7 @@ void ShareInner::repaintChatAtIndex(int index) { update(rtlrect(_rowsLeft + qFloor(column * _rowWidthReal), row * _rowHeight, _rowWidth, _rowHeight, width())); } -ShareInner::Chat *ShareInner::getChatAtIndex(int index) { +ShareBox::Inner::Chat *ShareBox::Inner::getChatAtIndex(int index) { if (index < 0) return nullptr; auto row = ([this, index]() -> Dialogs::Row* { if (_filter.isEmpty()) return _chatsIndexed->rowAtY(index, 1); @@ -350,11 +349,11 @@ ShareInner::Chat *ShareInner::getChatAtIndex(int index) { return nullptr; } -void ShareInner::repaintChat(PeerData *peer) { +void ShareBox::Inner::repaintChat(PeerData *peer) { repaintChatAtIndex(chatIndex(peer)); } -int ShareInner::chatIndex(PeerData *peer) const { +int ShareBox::Inner::chatIndex(PeerData *peer) const { int index = 0; if (_filter.isEmpty()) { for_const (auto row, _chatsIndexed->all()) { @@ -380,7 +379,7 @@ int ShareInner::chatIndex(PeerData *peer) const { return -1; } -void ShareInner::loadProfilePhotos(int yFrom) { +void ShareBox::Inner::loadProfilePhotos(int yFrom) { if (yFrom < 0) { yFrom = 0; } @@ -419,7 +418,7 @@ void ShareInner::loadProfilePhotos(int yFrom) { } } -ShareInner::Chat *ShareInner::getChat(Dialogs::Row *row) { +ShareBox::Inner::Chat *ShareBox::Inner::getChat(Dialogs::Row *row) { auto data = static_cast(row->attached); if (!data) { auto peer = row->history()->peer; @@ -436,7 +435,7 @@ ShareInner::Chat *ShareInner::getChat(Dialogs::Row *row) { return data; } -void ShareInner::setActive(int active) { +void ShareBox::Inner::setActive(int active) { if (active != _active) { auto changeNameFg = [this](int index, style::color from, style::color to) { if (auto chat = getChatAtIndex(index)) { @@ -453,7 +452,7 @@ void ShareInner::setActive(int active) { emit mustScrollTo(y, y + _rowHeight); } -void ShareInner::paintChat(Painter &p, Chat *chat, int index) { +void ShareBox::Inner::paintChat(Painter &p, Chat *chat, int index) { auto x = _rowsLeft + qFloor((index % _columnCount) * _rowWidthReal); auto y = _rowsTop + (index / _columnCount) * _rowHeight; @@ -474,13 +473,13 @@ void ShareInner::paintChat(Painter &p, Chat *chat, int index) { chat->name.drawLeftElided(p, x + nameLeft, y + nameTop, nameWidth, outerWidth, 2, style::al_top, 0, -1, 0, true); } -ShareInner::Chat::Chat(PeerData *peer, Ui::RoundImageCheckbox::UpdateCallback &&updateCallback) +ShareBox::Inner::Chat::Chat(PeerData *peer, Ui::RoundImageCheckbox::UpdateCallback &&updateCallback) : peer(peer) , checkbox(st::sharePhotoCheckbox, std_::move(updateCallback), PaintUserpicCallback(peer)) , name(st::sharePhotoCheckbox.imageRadius * 2) { } -void ShareInner::paintEvent(QPaintEvent *e) { +void ShareBox::Inner::paintEvent(QPaintEvent *e) { Painter p(this); auto r = e->rect(); @@ -539,20 +538,20 @@ void ShareInner::paintEvent(QPaintEvent *e) { } } -void ShareInner::enterEvent(QEvent *e) { +void ShareBox::Inner::enterEvent(QEvent *e) { setMouseTracking(true); } -void ShareInner::leaveEvent(QEvent *e) { +void ShareBox::Inner::leaveEvent(QEvent *e) { setMouseTracking(false); } -void ShareInner::mouseMoveEvent(QMouseEvent *e) { +void ShareBox::Inner::mouseMoveEvent(QMouseEvent *e) { updateUpon(e->pos()); setCursor((_upon >= 0) ? style::cur_pointer : style::cur_default); } -void ShareInner::updateUpon(const QPoint &pos) { +void ShareBox::Inner::updateUpon(const QPoint &pos) { auto x = pos.x(), y = pos.y(); auto row = (y - _rowsTop) / _rowHeight; auto column = qFloor((x - _rowsLeft) / _rowWidthReal); @@ -567,18 +566,18 @@ void ShareInner::updateUpon(const QPoint &pos) { _upon = upon; } -void ShareInner::mousePressEvent(QMouseEvent *e) { +void ShareBox::Inner::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { updateUpon(e->pos()); changeCheckState(getChatAtIndex(_upon)); } } -void ShareInner::onSelectActive() { +void ShareBox::Inner::onSelectActive() { changeCheckState(getChatAtIndex(_active > 0 ? _active : 0)); } -void ShareInner::resizeEvent(QResizeEvent *e) { +void ShareBox::Inner::resizeEvent(QResizeEvent *e) { _columnSkip = (width() - _columnCount * st::sharePhotoCheckbox.imageRadius * 2) / float64(_columnCount + 1); _rowWidthReal = st::sharePhotoCheckbox.imageRadius * 2 + _columnSkip; _rowsLeft = qFloor(_columnSkip / 2); @@ -586,7 +585,7 @@ void ShareInner::resizeEvent(QResizeEvent *e) { update(); } -void ShareInner::changeCheckState(Chat *chat) { +void ShareBox::Inner::changeCheckState(Chat *chat) { if (!chat) return; if (!_filter.isEmpty()) { @@ -611,11 +610,11 @@ void ShareInner::changeCheckState(Chat *chat) { emit selectedChanged(); } -bool ShareInner::hasSelected() const { +bool ShareBox::Inner::hasSelected() const { return _selected.size(); } -void ShareInner::updateFilter(QString filter) { +void ShareBox::Inner::updateFilter(QString filter) { _lastQuery = filter.toLower().trimmed(); filter = textSearchKey(filter); @@ -694,7 +693,7 @@ void ShareInner::updateFilter(QString filter) { } } -void ShareInner::peopleReceived(const QString &query, const QVector &people) { +void ShareBox::Inner::peopleReceived(const QString &query, const QVector &people) { _lastQuery = query.toLower().trimmed(); if (_lastQuery.at(0) == '@') _lastQuery = _lastQuery.mid(1); int32 already = _byUsernameFiltered.size(); @@ -724,7 +723,7 @@ void ShareInner::peopleReceived(const QString &query, const QVector &pe refresh(); } -void ShareInner::refresh() { +void ShareBox::Inner::refresh() { auto count = displayedChatsCount(); if (count) { auto rows = (count / _columnCount) + (count % _columnCount ? 1 : 0); @@ -735,13 +734,13 @@ void ShareInner::refresh() { update(); } -ShareInner::~ShareInner() { +ShareBox::Inner::~Inner() { for_const (auto chat, _dataMap) { delete chat; } } -QVector ShareInner::selected() const { +QVector ShareBox::Inner::selected() const { QVector result; result.reserve(_dataMap.size()); for_const (auto chat, _dataMap) { @@ -752,8 +751,6 @@ QVector ShareInner::selected() const { return result; } -} // namespace internal - QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) { auto shareHashData = QByteArray(0x10, Qt::Uninitialized); auto shareHashDataInts = reinterpret_cast(shareHashData.data()); diff --git a/Telegram/SourceFiles/boxes/sharebox.h b/Telegram/SourceFiles/boxes/sharebox.h index a463597b3..2e4252fc2 100644 --- a/Telegram/SourceFiles/boxes/sharebox.h +++ b/Telegram/SourceFiles/boxes/sharebox.h @@ -31,14 +31,14 @@ class Row; class IndexedList; } // namespace Dialogs -namespace internal { -class ShareInner; -} // namespace internal - namespace Notify { struct PeerUpdate; } // namespace Notify +namespace Ui { +class IconButton; +} // namespace Ui + QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId); void shareGameScoreByHash(const QString &hash); @@ -82,9 +82,10 @@ private: CopyCallback _copyCallback; SubmitCallback _submitCallback; - ChildWidget _inner; + class Inner; + ChildWidget _inner; ChildWidget _filter; - ChildWidget _filterCancel; + ChildWidget _filterCancel; ChildWidget _copy; ChildWidget _share; @@ -108,13 +109,12 @@ private: }; -namespace internal { - -class ShareInner : public ScrolledWidget, public RPCSender, private base::Subscriber { +// This class is hold in header because it requires Qt preprocessing. +class ShareBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: - ShareInner(QWidget *parent, ShareBox::FilterCallback &&filterCallback); + Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback); QVector selected() const; bool hasSelected() const; @@ -127,7 +127,7 @@ public: void setVisibleTopBottom(int visibleTop, int visibleBottom) override; void updateFilter(QString filter = QString()); - ~ShareInner(); + ~Inner(); public slots: void onSelectActive(); @@ -208,5 +208,3 @@ private: ByUsernameDatas d_byUsernameFiltered; }; - -} // namespace internal diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp new file mode 100644 index 000000000..907a5f9d8 --- /dev/null +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -0,0 +1,1254 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "stickers_box.h" + +#include "lang.h" +#include "mainwidget.h" +#include "stickers/stickers.h" +#include "boxes/confirmbox.h" +#include "boxes/stickersetbox.h" +#include "apiwrap.h" +#include "localstorage.h" +#include "dialogs/dialogs_layout.h" +#include "styles/style_stickers.h" + +namespace { + +constexpr int kArchivedLimitFirstRequest = 10; +constexpr int kArchivedLimitPerPage = 30; + +} // namespace + +int32 stickerPacksCount(bool includeDisabledOfficial) { + int32 result = 0; + auto &order = Global::StickerSetsOrder(); + auto &sets = Global::StickerSets(); + for (int i = 0, l = order.size(); i < l; ++i) { + auto it = sets.constFind(order.at(i)); + if (it != sets.cend()) { + if (!(it->flags & MTPDstickerSet::Flag::f_archived) || ((it->flags & MTPDstickerSet::Flag::f_official) && includeDisabledOfficial)) { + ++result; + } + } + } + return result; +} + +StickersBox::StickersBox(Section section) : ItemListBox(st::boxScroll) +, _section(section) +, _inner(this, section) +, _aboutWidth(st::boxWideWidth - 2 * st::stickersReorderPadding.top()) +, _about(st::boxTextFont, lang((section == Section::Archived) ? lng_stickers_packs_archived : lng_stickers_reorder), _defaultOptions, _aboutWidth) { + setup(); +} + +StickersBox::StickersBox(const Stickers::Order &archivedIds) : ItemListBox(st::boxScroll) +, _section(Section::ArchivedPart) +, _inner(this, archivedIds) +, _aboutWidth(st::boxWideWidth - 2 * st::stickersReorderPadding.top()) +, _about(st::boxTextFont, lang(lng_stickers_packs_archived), _defaultOptions, _aboutWidth) { + setup(); +} + +void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result) { + _archivedRequestId = 0; + if (result.type() != mtpc_messages_archivedStickers) { + return; + } + + auto &stickers = result.c_messages_archivedStickers(); + auto &archived = Global::RefArchivedStickerSetsOrder(); + if (offsetId) { + auto index = archived.indexOf(offsetId); + if (index >= 0) { + archived = archived.mid(0, index + 1); + } + } else { + archived.clear(); + } + + bool addedSet = false; + auto &v = stickers.vsets.c_vector().v; + for_const (auto &stickerSet, v) { + const MTPDstickerSet *setData = nullptr; + switch (stickerSet.type()) { + case mtpc_stickerSetCovered: { + auto &d = stickerSet.c_stickerSetCovered(); + if (d.vset.type() == mtpc_stickerSet) { + setData = &d.vset.c_stickerSet(); + } + } break; + case mtpc_stickerSetMultiCovered: { + auto &d = stickerSet.c_stickerSetMultiCovered(); + if (d.vset.type() == mtpc_stickerSet) { + setData = &d.vset.c_stickerSet(); + } + } break; + } + if (!setData) continue; + + if (auto set = Stickers::feedSet(*setData)) { + auto index = archived.indexOf(set->id); + if (archived.isEmpty() || index != archived.size() - 1) { + if (index < archived.size() - 1) { + archived.removeAt(index); + } + archived.push_back(set->id); + } + if (_section == Section::Archived) { + if (_inner->appendSet(*set)) { + addedSet = true; + if (set->stickers.isEmpty() || (set->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { + App::api()->scheduleStickerSetRequest(set->id, set->access); + } + } + } + } + } + if (_section == Section::Installed && !archived.isEmpty()) { + Local::writeArchivedStickers(); + rebuildList(); + } else if (_section == Section::Archived) { + if (addedSet) { + _inner->updateSize(); + setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); + _inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); + App::api()->requestStickerSets(); + } else { + _allArchivedLoaded = v.isEmpty() || (offsetId != 0); + } + } + checkLoadMoreArchived(); +} + +void StickersBox::setup() { + if (_section == Section::Installed) { + Local::readArchivedStickers(); + if (Global::ArchivedStickerSetsOrder().isEmpty()) { + MTPmessages_GetArchivedStickers::Flags flags = 0; + _archivedRequestId = MTP::send(MTPmessages_GetArchivedStickers(MTP_flags(flags), MTP_long(0), MTP_int(kArchivedLimitFirstRequest)), rpcDone(&StickersBox::getArchivedDone, 0ULL)); + } + } else if (_section == Section::Archived) { + // Reload the archived list. + MTPmessages_GetArchivedStickers::Flags flags = 0; + _archivedRequestId = MTP::send(MTPmessages_GetArchivedStickers(MTP_flags(flags), MTP_long(0), MTP_int(kArchivedLimitFirstRequest)), rpcDone(&StickersBox::getArchivedDone, 0ULL)); + + auto &sets = Global::StickerSets(); + for_const (auto setId, Global::ArchivedStickerSetsOrder()) { + auto it = sets.constFind(setId); + if (it != sets.cend()) { + if (it->stickers.isEmpty() && (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { + App::api()->scheduleStickerSetRequest(setId, it->access); + } + } + } + App::api()->requestStickerSets(); + } + + int bottomSkip = st::boxPadding.bottom(); + if (_section == Section::Installed) { + _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); + _topShadow.create(this, st::contactsAboutShadow); + + _save.create(this, lang(lng_settings_save), st::defaultBoxButton); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + + _cancel.create(this, lang(lng_cancel), st::cancelBoxButton); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + + _bottomShadow.create(this); + bottomSkip = st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); + } else if (_section == Section::ArchivedPart) { + _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); + _topShadow.create(this, st::contactsAboutShadow); + + _save.create(this, lang(lng_box_ok), st::defaultBoxButton); + connect(_save, SIGNAL(clicked()), this, SLOT(onClose())); + } else if (_section == Section::Archived) { + _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); + _topShadow.create(this, st::contactsAboutShadow); + } + ItemListBox::init(_inner, bottomSkip, st::boxTitleHeight + _aboutHeight); + setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); + + connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); + App::main()->updateStickers(); + + connect(_inner, SIGNAL(checkDraggingScroll(int)), this, SLOT(onCheckDraggingScroll(int))); + connect(_inner, SIGNAL(noDraggingScroll()), this, SLOT(onNoDraggingScroll())); + connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); + connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); + _scrollTimer.setSingleShot(false); + + rebuildList(); + + prepare(); +} + +void StickersBox::onScroll() { + updateVisibleTopBottom(); + checkLoadMoreArchived(); +} + +void StickersBox::updateVisibleTopBottom() { + auto visibleTop = scrollArea()->scrollTop(); + auto visibleBottom = visibleTop + scrollArea()->height(); + _inner->setVisibleTopBottom(visibleTop, visibleBottom); +} + +void StickersBox::checkLoadMoreArchived() { + if (_section != Section::Archived) return; + + int scrollTop = scrollArea()->scrollTop(), scrollTopMax = scrollArea()->scrollTopMax(); + if (scrollTop + PreloadHeightsCount * scrollArea()->height() >= scrollTopMax) { + if (!_archivedRequestId && !_allArchivedLoaded) { + uint64 lastId = 0; + for (auto setId = Global::ArchivedStickerSetsOrder().cend(), e = Global::ArchivedStickerSetsOrder().cbegin(); setId != e;) { + --setId; + auto it = Global::StickerSets().constFind(*setId); + if (it != Global::StickerSets().cend()) { + if (it->flags & MTPDstickerSet::Flag::f_archived) { + lastId = it->id; + break; + } + } + } + MTPmessages_GetArchivedStickers::Flags flags = 0; + _archivedRequestId = MTP::send(MTPmessages_GetArchivedStickers(MTP_flags(flags), MTP_long(lastId), MTP_int(kArchivedLimitPerPage)), rpcDone(&StickersBox::getArchivedDone, lastId)); + } + } +} + +int32 StickersBox::countHeight() const { + int bottomSkip = st::boxPadding.bottom(); + if (_section == Section::Installed) { + bottomSkip = st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); + } + return st::boxTitleHeight + _aboutHeight + _inner->height() + bottomSkip; +} + +void StickersBox::disenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req) { + _disenableRequests.remove(req); + if (_disenableRequests.isEmpty()) { + saveOrder(); + } +} + +bool StickersBox::disenableFail(const RPCError &error, mtpRequestId req) { + if (MTP::isDefaultHandledError(error)) return false; + _disenableRequests.remove(req); + if (_disenableRequests.isEmpty()) { + saveOrder(); + } + return true; +} + +void StickersBox::saveOrder() { + auto order = _inner->getOrder(); + if (order.size() > 1) { + QVector mtpOrder; + mtpOrder.reserve(order.size()); + for (int i = 0, l = order.size(); i < l; ++i) { + mtpOrder.push_back(MTP_long(order.at(i))); + } + + MTPmessages_ReorderStickerSets::Flags flags = 0; + _reorderRequest = MTP::send(MTPmessages_ReorderStickerSets(MTP_flags(flags), MTP_vector(mtpOrder)), rpcDone(&StickersBox::reorderDone), rpcFail(&StickersBox::reorderFail)); + } else { + reorderDone(MTP_boolTrue()); + } +} + +void StickersBox::reorderDone(const MTPBool &result) { + _reorderRequest = 0; + onClose(); +} + +bool StickersBox::reorderFail(const RPCError &result) { + if (MTP::isDefaultHandledError(result)) return false; + _reorderRequest = 0; + Global::SetLastStickersUpdate(0); + App::main()->updateStickers(); + onClose(); + return true; +} + +void StickersBox::paintEvent(QPaintEvent *e) { + Painter p(this); + if (paint(p)) return; + + auto title = ([this]() { + if (_section == Section::Installed) { + return lang(lng_stickers_packs); + } else if (_section == Section::Featured) { + return lang(lng_stickers_featured); + } + return lang(lng_stickers_archived); + })(); + paintTitle(p, title); + p.translate(0, st::boxTitleHeight); + + if (_aboutHeight > 0) { + p.fillRect(0, 0, width(), _aboutHeight, st::contactsAboutBg); + p.setPen(st::stickersReorderFg); + _about.draw(p, st::stickersReorderPadding.top(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center); + } +} + +void StickersBox::closePressed() { + if (!_disenableRequests.isEmpty()) { + for_const (auto requestId, _disenableRequests) { + MTP::cancel(requestId); + } + _disenableRequests.clear(); + Global::SetLastStickersUpdate(0); + App::main()->updateStickers(); + } else if (_reorderRequest) { + MTP::cancel(_reorderRequest); + _reorderRequest = 0; + Global::SetLastStickersUpdate(0); + App::main()->updateStickers(); + } +} + +StickersBox::~StickersBox() { + if (_section == Section::Archived) { + Local::writeArchivedStickers(); + } +} + +void StickersBox::resizeEvent(QResizeEvent *e) { + ItemListBox::resizeEvent(e); + _inner->resize(width(), _inner->height()); + _inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); + updateVisibleTopBottom(); + if (_topShadow) { + _topShadow->setGeometry(0, st::boxTitleHeight + _aboutHeight, width(), st::lineWidth); + } + if (_save) { + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + } + if (_cancel) { + _cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); + _bottomShadow->setGeometry(0, height() - st::boxButtonPadding.bottom() - _save->height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); + } +} + +void StickersBox::onStickersUpdated() { + if (_section == Section::Installed || _section == Section::Featured) { + rebuildList(); + } else { + _inner->updateRows(); + } +} + +void StickersBox::rebuildList() { + _inner->rebuild(); + setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); + _inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); +} + +void StickersBox::onCheckDraggingScroll(int localY) { + if (localY < scrollArea()->scrollTop()) { + _scrollDelta = localY - scrollArea()->scrollTop(); + } else if (localY >= scrollArea()->scrollTop() + scrollArea()->height()) { + _scrollDelta = localY - scrollArea()->scrollTop() - scrollArea()->height() + 1; + } else { + _scrollDelta = 0; + } + if (_scrollDelta) { + _scrollTimer.start(15); + } else { + _scrollTimer.stop(); + } +} + +void StickersBox::onNoDraggingScroll() { + _scrollTimer.stop(); +} + +void StickersBox::onScrollTimer() { + int32 d = (_scrollDelta > 0) ? qMin(_scrollDelta * 3 / 20 + 1, int32(MaxScrollSpeed)) : qMax(_scrollDelta * 3 / 20 - 1, -int32(MaxScrollSpeed)); + scrollArea()->scrollToY(scrollArea()->scrollTop() + d); +} + +void StickersBox::onSave() { + if (!_inner->savingStart()) { + return; + } + + bool writeRecent = false, writeArchived = false; + auto &recent = cGetRecentStickers(); + auto &sets = Global::RefStickerSets(); + + auto reorder = _inner->getOrder(), disabled = _inner->getDisabledSets(); + for (int32 i = 0, l = disabled.size(); i < l; ++i) { + auto it = sets.find(disabled.at(i)); + if (it != sets.cend()) { + for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) { + if (it->stickers.indexOf(i->first) >= 0) { + i = recent.erase(i); + writeRecent = true; + } else { + ++i; + } + } + if (!(it->flags & MTPDstickerSet::Flag::f_archived)) { + MTPInputStickerSet setId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName)); + if (it->flags & MTPDstickerSet::Flag::f_official) { + _disenableRequests.insert(MTP::send(MTPmessages_InstallStickerSet(setId, MTP_boolTrue()), rpcDone(&StickersBox::disenableDone), rpcFail(&StickersBox::disenableFail), 0, 5)); + it->flags |= MTPDstickerSet::Flag::f_archived; + auto index = Global::RefArchivedStickerSetsOrder().indexOf(it->id); + if (index < 0) { + Global::RefArchivedStickerSetsOrder().push_front(it->id); + writeArchived = true; + } + } else { + _disenableRequests.insert(MTP::send(MTPmessages_UninstallStickerSet(setId), rpcDone(&StickersBox::disenableDone), rpcFail(&StickersBox::disenableFail), 0, 5)); + int removeIndex = Global::StickerSetsOrder().indexOf(it->id); + if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); + if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured) && !(it->flags & MTPDstickerSet_ClientFlag::f_special)) { + sets.erase(it); + } else { + if (it->flags & MTPDstickerSet::Flag::f_archived) { + writeArchived = true; + } + it->flags &= ~(MTPDstickerSet::Flag::f_installed | MTPDstickerSet::Flag::f_archived); + } + } + } + } + } + + // Clear all installed flags, set only for sets from order. + for (auto &set : sets) { + if (!(set.flags & MTPDstickerSet::Flag::f_archived)) { + set.flags &= ~MTPDstickerSet::Flag::f_installed; + } + } + + auto &order(Global::RefStickerSetsOrder()); + order.clear(); + for (int i = 0, l = reorder.size(); i < l; ++i) { + auto it = sets.find(reorder.at(i)); + if (it != sets.cend()) { + if ((it->flags & MTPDstickerSet::Flag::f_archived) && !disabled.contains(it->id)) { + MTPInputStickerSet setId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName)); + _disenableRequests.insert(MTP::send(MTPmessages_InstallStickerSet(setId, MTP_boolFalse()), rpcDone(&StickersBox::disenableDone), rpcFail(&StickersBox::disenableFail), 0, 5)); + it->flags &= ~MTPDstickerSet::Flag::f_archived; + writeArchived = true; + } + order.push_back(reorder.at(i)); + it->flags |= MTPDstickerSet::Flag::f_installed; + } + } + for (auto it = sets.begin(); it != sets.cend();) { + if ((it->flags & MTPDstickerSet_ClientFlag::f_featured) + || (it->flags & MTPDstickerSet::Flag::f_installed) + || (it->flags & MTPDstickerSet::Flag::f_archived) + || (it->flags & MTPDstickerSet_ClientFlag::f_special)) { + ++it; + } else { + it = sets.erase(it); + } + } + + Local::writeInstalledStickers(); + if (writeRecent) Local::writeUserSettings(); + if (writeArchived) Local::writeArchivedStickers(); + emit App::main()->stickersUpdated(); + + if (_disenableRequests.isEmpty()) { + saveOrder(); + } else { + MTP::sendAnything(); + } +} + +void StickersBox::showAll() { + if (_topShadow) { + _topShadow->show(); + } + if (_save) { + _save->show(); + } + if (_cancel) { + _cancel->show(); + _bottomShadow->show(); + } + ItemListBox::showAll(); +} + +StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : ScrolledWidget(parent) +, _section(section) +, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) +, _a_shifting(animation(this, &Inner::step_shifting)) +, _itemsTop(st::membersPadding.top()) +, _clearWidth(st::normalFont->width(lang(lng_stickers_clear_recent))) +, _removeWidth(st::normalFont->width(lang(lng_stickers_remove))) +, _returnWidth(st::normalFont->width(lang(lng_stickers_return))) +, _restoreWidth(st::normalFont->width(lang(lng_stickers_restore))) +, _aboveShadow(st::boxShadow) { + setup(); +} + +StickersBox::Inner::Inner(QWidget *parent, const Stickers::Order &archivedIds) : ScrolledWidget(parent) +, _section(StickersBox::Section::ArchivedPart) +, _archivedIds(archivedIds) +, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) +, _a_shifting(animation(this, &Inner::step_shifting)) +, _itemsTop(st::membersPadding.top()) +, _clearWidth(st::normalFont->width(lang(lng_stickers_clear_recent))) +, _removeWidth(st::normalFont->width(lang(lng_stickers_remove))) +, _returnWidth(st::normalFont->width(lang(lng_stickers_return))) +, _restoreWidth(st::normalFont->width(lang(lng_stickers_restore))) +, _aboveShadow(st::boxShadow) { + setup(); +} + +void StickersBox::Inner::setup() { + subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + setMouseTracking(true); +} + +void StickersBox::Inner::onImageLoaded() { + update(); + readVisibleSets(); +} + +void StickersBox::Inner::paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const { + if (selected) { + p.fillRect(0, y, width(), _buttonHeight, st::contactsBgOver); + } + p.setFont(st::stickersFeaturedFont); + p.setPen(st::stickersFeaturedPen); + p.drawTextLeft(st::stickersFeaturedPosition.x(), y + st::stickersFeaturedPosition.y(), width(), text); + + if (badgeCounter) { + Dialogs::Layout::UnreadBadgeStyle unreadSt; + unreadSt.sizeId = Dialogs::Layout::UnreadBadgeInStickersBox; + unreadSt.size = st::stickersFeaturedBadgeSize; + int unreadRight = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x()); + if (rtl()) unreadRight = width() - unreadRight; + int unreadTop = y + (_buttonHeight - st::stickersFeaturedBadgeSize) / 2; + Dialogs::Layout::paintUnreadCount(p, QString::number(badgeCounter), unreadRight, unreadTop, unreadSt); + } +} + +void StickersBox::Inner::paintEvent(QPaintEvent *e) { + QRect r(e->rect()); + Painter p(this); + + _a_shifting.step(); + + p.fillRect(r, st::white); + p.setClipRect(r); + + int y = st::membersPadding.top(); + if (_hasFeaturedButton) { + auto selected = (_selected == -2); + paintButton(p, y, selected, lang(lng_stickers_featured), Global::FeaturedStickerSetsUnreadCount()); + y += _buttonHeight; + } + if (_hasArchivedButton) { + auto selected = (_selected == -1); + paintButton(p, y, selected, lang(lng_stickers_archived), 0); + y += _buttonHeight; + } + + if (_rows.isEmpty()) { + p.setFont(st::noContactsFont); + p.setPen(st::noContactsColor); + p.drawText(QRect(0, y, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); + } else { + p.translate(0, _itemsTop); + + int32 yFrom = r.y() - _itemsTop, yTo = r.y() + r.height() - _itemsTop; + int32 from = floorclamp(yFrom - _rowHeight, _rowHeight, 0, _rows.size()); + int32 to = ceilclamp(yTo + _rowHeight, _rowHeight, 0, _rows.size()); + p.translate(0, from * _rowHeight); + for (int32 i = from; i < to; ++i) { + if (i != _above) { + paintRow(p, i); + } + p.translate(0, _rowHeight); + } + if (from <= _above && _above < to) { + p.translate(0, (_above - to) * _rowHeight); + paintRow(p, _above); + } + } +} + +void StickersBox::Inner::paintRow(Painter &p, int32 index) { + const StickerSetRow *s(_rows.at(index)); + + int32 xadd = 0, yadd = s->yadd.current(); + if (xadd || yadd) p.translate(xadd, yadd); + + if (_section == Section::Installed) { + bool removeSel = (index == _actionSel && (_actionDown < 0 || index == _actionDown)); + bool removeDown = removeSel && (index == _actionDown); + + p.setFont(removeSel ? st::linkOverFont : st::linkFont); + if (removeDown) { + p.setPen(st::btnDefLink.downColor); + } else { + p.setPen(st::btnDefLink.color); + } + int32 remWidth = s->recent ? _clearWidth : (s->disabled ? (s->official ? _restoreWidth : _returnWidth) : _removeWidth); + QString remText = lang(s->recent ? lng_stickers_clear_recent : (s->disabled ? (s->official ? lng_stickers_restore : lng_stickers_return) : lng_stickers_remove)); + p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), remText, remWidth); + + if (index == _above) { + float64 current = _aboveShadowFadeOpacity.current(); + if (_started >= 0) { + float64 o = aboveShadowOpacity(); + if (o > current) { + _aboveShadowFadeOpacity = anim::fvalue(o, o); + current = o; + } + } + p.setOpacity(current); + QRect row(myrtlrect(_aboveShadow.getDimensions(st::boxShadowShift).left(), st::contactsPadding.top() / 2, width() - (st::contactsPadding.left() / 2) - _scrollbar - _aboveShadow.getDimensions(st::boxShadowShift).right(), _rowHeight - ((st::contactsPadding.top() + st::contactsPadding.bottom()) / 2))); + _aboveShadow.paint(p, row, st::boxShadowShift); + p.fillRect(row, st::white); + p.setOpacity(1); + } + } else if (s->installed && !s->disabled) { + int addw = st::stickersAddSize.width(); + int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (addw + st::stickersFeaturedInstalled.width()) / 2); + int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2; + st::stickersFeaturedInstalled.paint(p, QPoint(checkx, checky), width()); + } else { + int addw = st::stickersAddSize.width(); + int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; + int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2; + QRect add(myrtlrect(addx, addy, addw, st::stickersAddSize.height())); + + auto textBg = (_actionSel == index) ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg; + App::roundRect(p, add, textBg, ImageRoundRadius::Small); + int iconx = addx + (st::stickersAddSize.width() - st::stickersAddIcon.width()) / 2; + int icony = addy + (st::stickersAddSize.height() - st::stickersAddIcon.height()) / 2; + icony += (_actionSel == index && _actionDown == index) ? (st::defaultActiveButton.downTextTop - st::defaultActiveButton.textTop) : 0; + st::stickersAddIcon.paint(p, QPoint(iconx, icony), width()); + } + + if (s->disabled && _section == Section::Installed) { + p.setOpacity(st::stickersRowDisabledOpacity); + } + if (s->sticker) { + s->sticker->thumb->load(); + QPixmap pix(s->sticker->thumb->pix(s->pixw, s->pixh)); + p.drawPixmapLeft(st::contactsPadding.left() + (st::contactsPhotoSize - s->pixw) / 2, st::contactsPadding.top() + (st::contactsPhotoSize - s->pixh) / 2, width(), pix); + } + + int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); + int namey = st::contactsPadding.top() + st::contactsNameTop; + int statusx = namex; + int statusy = st::contactsPadding.top() + st::contactsStatusTop; + + p.setFont(st::contactsNameFont); + p.setPen(st::black); + p.drawTextLeft(namex, namey, width(), s->title, s->titleWidth); + + if (s->unread) { + p.setPen(Qt::NoPen); + p.setBrush(st::stickersFeaturedUnreadBg); + + p.setRenderHint(QPainter::HighQualityAntialiasing, true); + p.drawEllipse(rtlrect(namex + s->titleWidth + st::stickersFeaturedUnreadSkip, namey + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + } + + p.setFont(st::contactsStatusFont); + p.setPen(st::contactsStatusFg); + p.drawTextLeft(statusx, statusy, width(), lng_stickers_count(lt_count, s->count)); + + p.setOpacity(1); + if (xadd || yadd) p.translate(-xadd, -yadd); +} + +void StickersBox::Inner::mousePressEvent(QMouseEvent *e) { + if (_saving) return; + if (_dragging >= 0) mouseReleaseEvent(e); + _mouse = e->globalPos(); + onUpdateSelected(); + + _pressed = _selected; + if (_actionSel >= 0) { + _actionDown = _actionSel; + update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); + } else if (_selected >= 0 && _section == Section::Installed && !_rows.at(_selected)->recent) { + _above = _dragging = _started = _selected; + _dragStart = mapFromGlobal(_mouse); + } +} + +void StickersBox::Inner::mouseMoveEvent(QMouseEvent *e) { + if (_saving) return; + _mouse = e->globalPos(); + onUpdateSelected(); +} + +void StickersBox::Inner::onUpdateSelected() { + if (_saving) return; + QPoint local(mapFromGlobal(_mouse)); + if (_dragging >= 0) { + int32 shift = 0; + uint64 ms = getms(); + int firstSetIndex = 0; + if (_rows.at(firstSetIndex)->recent) { + ++firstSetIndex; + } + if (_dragStart.y() > local.y() && _dragging > 0) { + shift = -floorclamp(_dragStart.y() - local.y() + (_rowHeight / 2), _rowHeight, 0, _dragging - firstSetIndex); + for (int32 from = _dragging, to = _dragging + shift; from > to; --from) { + qSwap(_rows[from], _rows[from - 1]); + _rows.at(from)->yadd = anim::ivalue(_rows.at(from)->yadd.current() - _rowHeight, 0); + _animStartTimes[from] = ms; + } + } else if (_dragStart.y() < local.y() && _dragging + 1 < _rows.size()) { + shift = floorclamp(local.y() - _dragStart.y() + (_rowHeight / 2), _rowHeight, 0, _rows.size() - _dragging - 1); + for (int32 from = _dragging, to = _dragging + shift; from < to; ++from) { + qSwap(_rows[from], _rows[from + 1]); + _rows.at(from)->yadd = anim::ivalue(_rows.at(from)->yadd.current() + _rowHeight, 0); + _animStartTimes[from] = ms; + } + } + if (shift) { + _dragging += shift; + _above = _dragging; + _dragStart.setY(_dragStart.y() + shift * _rowHeight); + if (!_a_shifting.animating()) { + _a_shifting.start(); + } + } + _rows.at(_dragging)->yadd = anim::ivalue(local.y() - _dragStart.y(), local.y() - _dragStart.y()); + _animStartTimes[_dragging] = 0; + _a_shifting.step(getms(), true); + + emit checkDraggingScroll(local.y()); + } else { + bool in = rect().marginsRemoved(QMargins(0, _itemsTop, 0, st::membersPadding.bottom())).contains(local); + int selected = -2; + int actionSel = -1; + if (in) { + selected = floorclamp(local.y() - _itemsTop, _rowHeight, 0, _rows.size() - 1); + + if (_section == Section::Installed) { + int remw = _rows.at(selected)->recent ? _clearWidth : (_rows.at(selected)->disabled ? (_rows.at(selected)->official ? _restoreWidth : _returnWidth) : _removeWidth); + QRect rem(myrtlrect(width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - remw, st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, remw, st::normalFont->height)); + actionSel = rem.contains(local.x(), local.y() - _itemsTop - selected * _rowHeight) ? selected : -1; + } else if (_rows.at(selected)->installed && !_rows.at(selected)->disabled) { + actionSel = -1; + } else { + int addw = st::stickersAddSize.width(); + int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; + int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2; + QRect add(myrtlrect(addx, addy, addw, st::stickersAddSize.height())); + actionSel = add.contains(local.x(), local.y() - _itemsTop - selected * _rowHeight) ? selected : -1; + } + } else if (_hasFeaturedButton && QRect(0, st::membersPadding.top(), width(), _buttonHeight).contains(local)) { + selected = -2; + } else if (_hasArchivedButton && QRect(0, st::membersPadding.top() + (_hasFeaturedButton ? _buttonHeight : 0), width(), _buttonHeight).contains(local)) { + selected = -1; + } else { + selected = -3; + } + if (_selected != selected) { + if (((_selected == -1) != (selected == -1)) || ((_selected == -2) != (selected == -2))) { + update(); + } + if (_section != Section::Installed && ((_selected >= 0 || _pressed >= 0) != (selected >= 0 || _pressed >= 0))) { + setCursor((selected >= 0 || _pressed >= 0) ? style::cur_pointer : style::cur_default); + } + _selected = selected; + } + setActionSel(actionSel); + emit noDraggingScroll(); + } +} + +void StickersBox::Inner::onClearRecent() { + if (_clearBox) { + _clearBox->onClose(); + } + + auto &sets = Global::RefStickerSets(); + bool removedCloud = (sets.remove(Stickers::CloudRecentSetId) != 0); + bool removedCustom = (sets.remove(Stickers::CustomSetId) != 0); + + auto &recent = cGetRecentStickers(); + if (!recent.isEmpty()) { + recent.clear(); + Local::writeUserSettings(); + } + + if (removedCustom) Local::writeInstalledStickers(); + if (removedCloud) Local::writeRecentStickers(); + emit App::main()->updateStickers(); + rebuild(); + + MTPmessages_ClearRecentStickers::Flags flags = 0; + MTP::send(MTPmessages_ClearRecentStickers(MTP_flags(flags))); +} + +void StickersBox::Inner::onClearBoxDestroyed(QObject *box) { + if (box == _clearBox) { + _clearBox = nullptr; + } +} + +float64 StickersBox::Inner::aboveShadowOpacity() const { + if (_above < 0) return 0; + + int32 dx = 0; + int32 dy = qAbs(_above * _rowHeight + _rows.at(_above)->yadd.current() - _started * _rowHeight); + return qMin((dx + dy) * 2. / _rowHeight, 1.); +} + +void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { + auto pressed = _pressed; + _pressed = -2; + + if (_section != Section::Installed && _selected < 0 && pressed >= 0) { + setCursor(style::cur_default); + } + + if (_saving) return; + + _mouse = e->globalPos(); + onUpdateSelected(); + if (_actionDown == _actionSel && _actionSel >= 0) { + if (_section == Section::Installed) { + if (_rows[_actionDown]->recent) { + _clearBox = new ConfirmBox(lang(lng_stickers_clear_recent_sure), lang(lng_stickers_clear_recent)); + connect(_clearBox, SIGNAL(confirmed()), this, SLOT(onClearRecent())); + connect(_clearBox, SIGNAL(destroyed(QObject*)), this, SLOT(onClearBoxDestroyed(QObject*))); + Ui::showLayer(_clearBox, KeepOtherLayers); + } else { + _rows[_actionDown]->disabled = !_rows[_actionDown]->disabled; + } + } else { + installSet(_rows[_actionDown]->id); + } + } else if (_dragging >= 0) { + QPoint local(mapFromGlobal(_mouse)); + _rows[_dragging]->yadd.start(0); + _aboveShadowFadeStart = _animStartTimes[_dragging] = getms(); + _aboveShadowFadeOpacity = anim::fvalue(aboveShadowOpacity(), 0); + if (!_a_shifting.animating()) { + _a_shifting.start(); + } + + _dragging = _started = -1; + } else if (pressed == _selected && _actionSel < 0 && _actionDown < 0) { + if (_selected == -2) { + _selected = -3; + Ui::showLayer(new StickersBox(Section::Featured), KeepOtherLayers); + } else if (_selected == -1) { + _selected = -3; + Ui::showLayer(new StickersBox(Section::Archived), KeepOtherLayers); + } else if (_selected >= 0 && _section != Section::Installed) { + auto &sets = Global::RefStickerSets(); + auto it = sets.find(_rows.at(pressed)->id); + if (it != sets.cend()) { + _selected = -3; + Ui::showLayer(new StickerSetBox(Stickers::inputSetId(*it)), KeepOtherLayers); + } + } + } + if (_actionDown >= 0) { + update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight); + _actionDown = -1; + } +} + +void StickersBox::Inner::leaveEvent(QEvent *e) { + _mouse = QPoint(-1, -1); + onUpdateSelected(); +} + +void StickersBox::Inner::installSet(uint64 setId) { + auto &sets = Global::RefStickerSets(); + auto it = sets.find(setId); + if (it == sets.cend()) { + rebuild(); + return; + } + + MTP::send(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_boolFalse()), rpcDone(&Inner::installDone), rpcFail(&Inner::installFail, setId)); + + Stickers::installLocally(setId); +} + +void StickersBox::Inner::installDone(const MTPmessages_StickerSetInstallResult &result) { + if (result.type() == mtpc_messages_stickerSetInstallResultArchive) { + Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive()); + } +} + +bool StickersBox::Inner::installFail(uint64 setId, const RPCError &error) { + if (MTP::isDefaultHandledError(error)) return false; + + auto &sets = Global::RefStickerSets(); + auto it = sets.find(setId); + if (it == sets.cend()) { + rebuild(); + return true; + } + + Stickers::undoInstallLocally(setId); + return true; +} + +void StickersBox::Inner::step_shifting(uint64 ms, bool timer) { + bool animating = false; + int32 updateMin = -1, updateMax = 0; + for (int32 i = 0, l = _animStartTimes.size(); i < l; ++i) { + uint64 start = _animStartTimes.at(i); + if (start) { + if (updateMin < 0) updateMin = i; + updateMax = i; + if (start + st::stickersRowDuration > ms && ms >= start) { + _rows.at(i)->yadd.update(float64(ms - start) / st::stickersRowDuration, anim::sineInOut); + animating = true; + } else { + _rows.at(i)->yadd.finish(); + _animStartTimes[i] = 0; + } + } + } + if (_aboveShadowFadeStart) { + if (updateMin < 0 || updateMin > _above) updateMin = _above; + if (updateMax < _above) updateMin = _above; + if (_aboveShadowFadeStart + st::stickersRowDuration > ms && ms > _aboveShadowFadeStart) { + _aboveShadowFadeOpacity.update(float64(ms - _aboveShadowFadeStart) / st::stickersRowDuration, anim::sineInOut); + animating = true; + } else { + _aboveShadowFadeOpacity.finish(); + _aboveShadowFadeStart = 0; + } + } + if (timer) { + if (_dragging >= 0) { + if (updateMin < 0 || updateMin > _dragging) updateMin = _dragging; + if (updateMax < _dragging) updateMax = _dragging; + } + if (updateMin >= 0) { + update(0, _itemsTop + _rowHeight * (updateMin - 1), width(), _rowHeight * (updateMax - updateMin + 3)); + } + } + if (!animating) { + _above = _dragging; + _a_shifting.stop(); + } +} + +void StickersBox::Inner::clear() { + for (int32 i = 0, l = _rows.size(); i < l; ++i) { + delete _rows.at(i); + } + _rows.clear(); + _animStartTimes.clear(); + _aboveShadowFadeStart = 0; + _aboveShadowFadeOpacity = anim::fvalue(0, 0); + _a_shifting.stop(); + _above = _dragging = _started = -1; + _selected = -3; + _pressed = -3; + _actionDown = -1; + setActionSel(-1); + update(); +} + +void StickersBox::Inner::setActionSel(int32 actionSel) { + if (actionSel != _actionSel) { + if (_actionSel >= 0) update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); + _actionSel = actionSel; + if (_actionSel >= 0) update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); + if (_section == Section::Installed) { + setCursor((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel)) ? style::cur_pointer : style::cur_default); + } + } +} + +void StickersBox::Inner::rebuild() { + _hasFeaturedButton = _hasArchivedButton = false; + _itemsTop = st::membersPadding.top(); + _buttonHeight = st::stickersFeaturedHeight; + if (_section == Section::Installed) { + if (!Global::FeaturedStickerSetsOrder().isEmpty()) { + _itemsTop += _buttonHeight; + _hasFeaturedButton = true; + } + if (!Global::ArchivedStickerSetsOrder().isEmpty()) { + _itemsTop += _buttonHeight; + _hasArchivedButton = true; + } + if (_itemsTop > st::membersPadding.top()) { + _itemsTop += st::membersPadding.top(); + } + } + + int maxNameWidth = countMaxNameWidth(); + + clear(); + auto &order = ([this]() -> const Stickers::Order & { + if (_section == Section::Installed) { + return Global::StickerSetsOrder(); + } else if (_section == Section::Featured) { + return Global::FeaturedStickerSetsOrder(); + } else if (_section == Section::Archived) { + return Global::ArchivedStickerSetsOrder(); + } + return _archivedIds; + })(); + _rows.reserve(order.size() + 1); + _animStartTimes.reserve(order.size() + 1); + + auto &sets = Global::StickerSets(); + if (_section == Section::Installed) { + auto cloudIt = sets.constFind(Stickers::CloudRecentSetId); + if (cloudIt != sets.cend() && !cloudIt->stickers.isEmpty()) { + rebuildAppendSet(cloudIt.value(), maxNameWidth); + } + } + for_const (auto setId, order) { + auto it = sets.constFind(setId); + if (it == sets.cend()) { + continue; + } + + rebuildAppendSet(it.value(), maxNameWidth); + + if (it->stickers.isEmpty() || (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { + App::api()->scheduleStickerSetRequest(it->id, it->access); + } + } + App::api()->requestStickerSets(); + updateSize(); +} + +void StickersBox::Inner::updateSize() { + resize(width(), _itemsTop + _rows.size() * _rowHeight + st::membersPadding.bottom()); +} + +void StickersBox::Inner::updateRows() { + int maxNameWidth = countMaxNameWidth(); + auto &sets = Global::StickerSets(); + for_const (auto row, _rows) { + auto it = sets.constFind(row->id); + if (it != sets.cend()) { + auto &set = it.value(); + if (!row->sticker) { + DocumentData *sticker = nullptr; + int pixw = 0, pixh = 0; + fillSetCover(set, &sticker, &pixw, &pixh); + if (sticker) { + row->sticker = sticker; + row->pixw = pixw; + row->pixh = pixh; + } + } + fillSetFlags(set, &row->recent, &row->installed, &row->official, &row->unread, &row->disabled); + if (_section == Section::Installed) { + row->disabled = false; + } + row->title = fillSetTitle(set, maxNameWidth, &row->titleWidth); + row->count = fillSetCount(set); + } + } + update(); +} + +bool StickersBox::Inner::appendSet(const Stickers::Set &set) { + for_const (auto row, _rows) { + if (row->id == set.id) { + return false; + } + } + rebuildAppendSet(set, countMaxNameWidth()); + return true; +} + +int StickersBox::Inner::countMaxNameWidth() const { + int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); + int namew = st::boxWideWidth - namex - st::contactsPadding.right() - st::contactsCheckPosition.x(); + if (_section == Section::Installed) { + namew -= qMax(qMax(qMax(_returnWidth, _removeWidth), _restoreWidth), _clearWidth); + } else { + namew -= st::stickersAddIcon.width() - st::defaultActiveButton.width; + namew -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip; + } + return namew; +} + +void StickersBox::Inner::rebuildAppendSet(const Stickers::Set &set, int maxNameWidth) { + bool recent = false, installed = false, official = false, unread = false, disabled = false; + fillSetFlags(set, &recent, &installed, &official, &unread, &disabled); + if (_section == Section::Installed && disabled) { + return; + } + + DocumentData *sticker = nullptr; + int pixw = 0, pixh = 0; + fillSetCover(set, &sticker, &pixw, &pixh); + + int titleWidth = 0; + QString title = fillSetTitle(set, maxNameWidth, &titleWidth); + int count = fillSetCount(set); + + _rows.push_back(new StickerSetRow(set.id, sticker, count, title, titleWidth, installed, official, unread, disabled, recent, pixw, pixh)); + _animStartTimes.push_back(0); +} + +void StickersBox::Inner::fillSetCover(const Stickers::Set &set, DocumentData **outSticker, int *outWidth, int *outHeight) const { + if (set.stickers.isEmpty()) { + *outSticker = nullptr; + *outWidth = *outHeight = 0; + return; + } + auto sticker = *outSticker = set.stickers.front(); + + auto pixw = sticker->thumb->width(); + auto pixh = sticker->thumb->height(); + if (pixw > st::contactsPhotoSize) { + if (pixw > pixh) { + pixh = (pixh * st::contactsPhotoSize) / pixw; + pixw = st::contactsPhotoSize; + } else { + pixw = (pixw * st::contactsPhotoSize) / pixh; + pixh = st::contactsPhotoSize; + } + } else if (pixh > st::contactsPhotoSize) { + pixw = (pixw * st::contactsPhotoSize) / pixh; + pixh = st::contactsPhotoSize; + } + *outWidth = pixw; + *outHeight = pixh; +} + +int StickersBox::Inner::fillSetCount(const Stickers::Set &set) const { + int result = set.stickers.isEmpty() ? set.count : set.stickers.size(), added = 0; + if (set.id == Stickers::CloudRecentSetId) { + auto customIt = Global::StickerSets().constFind(Stickers::CustomSetId); + if (customIt != Global::StickerSets().cend()) { + added = customIt->stickers.size(); + for_const (auto &sticker, cGetRecentStickers()) { + if (customIt->stickers.indexOf(sticker.first) < 0) { + ++added; + } + } + } else { + added = cGetRecentStickers().size(); + } + } + return result + added; +} + +QString StickersBox::Inner::fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const { + auto result = set.title; + int titleWidth = st::contactsNameFont->width(result); + if (titleWidth > maxNameWidth) { + result = st::contactsNameFont->elided(result, maxNameWidth); + titleWidth = st::contactsNameFont->width(result); + } + if (outTitleWidth) { + *outTitleWidth = titleWidth; + } + return result; +} + +void StickersBox::Inner::fillSetFlags(const Stickers::Set &set, bool *outRecent, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outDisabled) { + *outRecent = (set.id == Stickers::CloudRecentSetId); + *outInstalled = true; + *outOfficial = true; + *outUnread = false; + *outDisabled = false; + if (!*outRecent) { + *outInstalled = (set.flags & MTPDstickerSet::Flag::f_installed); + *outOfficial = (set.flags & MTPDstickerSet::Flag::f_official); + *outDisabled = (set.flags & MTPDstickerSet::Flag::f_archived); + if (_section == Section::Featured) { + *outUnread = (set.flags & MTPDstickerSet_ClientFlag::f_unread); + } + } +} + +Stickers::Order StickersBox::Inner::getOrder() const { + Stickers::Order result; + result.reserve(_rows.size()); + for (int32 i = 0, l = _rows.size(); i < l; ++i) { + if (_rows.at(i)->disabled || _rows.at(i)->recent) { + continue; + } + result.push_back(_rows.at(i)->id); + } + return result; +} + +Stickers::Order StickersBox::Inner::getDisabledSets() const { + Stickers::Order result; + result.reserve(_rows.size()); + for (int32 i = 0, l = _rows.size(); i < l; ++i) { + if (_rows.at(i)->disabled) { + result.push_back(_rows.at(i)->id); + } + } + return result; +} + +void StickersBox::Inner::setVisibleTopBottom(int visibleTop, int visibleBottom) { + if (_section == Section::Featured) { + _visibleTop = visibleTop; + _visibleBottom = visibleBottom; + readVisibleSets(); + } +} + +void StickersBox::Inner::readVisibleSets() { + auto itemsVisibleTop = _visibleTop - _itemsTop; + auto itemsVisibleBottom = _visibleBottom - _itemsTop; + int rowFrom = floorclamp(itemsVisibleTop, _rowHeight, 0, _rows.size()); + int rowTo = ceilclamp(itemsVisibleBottom, _rowHeight, 0, _rows.size()); + for (int i = rowFrom; i < rowTo; ++i) { + if (!_rows[i]->unread) { + continue; + } + if (i * _rowHeight < itemsVisibleTop || (i + 1) * _rowHeight > itemsVisibleBottom) { + continue; + } + if (!_rows[i]->sticker || _rows[i]->sticker->thumb->loaded() || _rows[i]->sticker->loaded()) { + Stickers::markFeaturedAsRead(_rows[i]->id); + } + } +} + +void StickersBox::Inner::setVisibleScrollbar(int32 width) { + _scrollbar = width; +} + +StickersBox::Inner::~Inner() { + clear(); +} diff --git a/Telegram/SourceFiles/boxes/stickers_box.h b/Telegram/SourceFiles/boxes/stickers_box.h new file mode 100644 index 000000000..3524a7db8 --- /dev/null +++ b/Telegram/SourceFiles/boxes/stickers_box.h @@ -0,0 +1,239 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "abstractbox.h" + +class ConfirmBox; + +namespace Ui { +class PlainShadow; +} // namespace Ui + +class StickersBox : public ItemListBox, public RPCSender { + Q_OBJECT + +public: + enum class Section { + Installed, + Featured, + Archived, + ArchivedPart, + }; + StickersBox(Section section = Section::Installed); + StickersBox(const Stickers::Order &archivedIds); + + ~StickersBox(); + +public slots: + void onStickersUpdated(); + + void onCheckDraggingScroll(int localY); + void onNoDraggingScroll(); + void onScrollTimer(); + + void onSave(); + +private slots: + void onScroll(); + +protected: + void resizeEvent(QResizeEvent *e) override; + void paintEvent(QPaintEvent *e) override; + + void closePressed() override; + void showAll() override; + +private: + void setup(); + int32 countHeight() const; + void rebuildList(); + + void disenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req); + bool disenableFail(const RPCError &error, mtpRequestId req); + void reorderDone(const MTPBool &result); + bool reorderFail(const RPCError &result); + void saveOrder(); + + void updateVisibleTopBottom(); + void checkLoadMoreArchived(); + void getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result); + + Section _section; + + class Inner; + ChildWidget _inner; + ChildWidget _save = { nullptr }; + ChildWidget _cancel = { nullptr }; + OrderedSet _disenableRequests; + mtpRequestId _reorderRequest = 0; + ChildWidget _topShadow = { nullptr }; + ChildWidget _bottomShadow = { nullptr }; + + QTimer _scrollTimer; + int32 _scrollDelta = 0; + + int _aboutWidth = 0; + Text _about; + int _aboutHeight = 0; + + mtpRequestId _archivedRequestId = 0; + bool _allArchivedLoaded = false; + +}; + +int32 stickerPacksCount(bool includeDisabledOfficial = false); + +// This class is hold in header because it requires Qt preprocessing. +class StickersBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { + Q_OBJECT + +public: + using Section = StickersBox::Section; + Inner(QWidget *parent, Section section); + Inner(QWidget *parent, const Stickers::Order &archivedIds); + + void rebuild(); + void updateSize(); + void updateRows(); // refresh only pack cover stickers + bool appendSet(const Stickers::Set &set); + bool savingStart() { + if (_saving) return false; + _saving = true; + return true; + } + + Stickers::Order getOrder() const; + Stickers::Order getDisabledSets() const; + + void setVisibleScrollbar(int32 width); + void setVisibleTopBottom(int visibleTop, int visibleBottom) override; + + ~Inner(); + +protected: + void paintEvent(QPaintEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void leaveEvent(QEvent *e) override; + +signals: + void checkDraggingScroll(int localY); + void noDraggingScroll(); + +public slots: + void onUpdateSelected(); + void onClearRecent(); + void onClearBoxDestroyed(QObject *box); + +private slots: + void onImageLoaded(); + +private: + void setup(); + void paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const; + + void step_shifting(uint64 ms, bool timer); + void paintRow(Painter &p, int32 index); + void clear(); + void setActionSel(int32 actionSel); + float64 aboveShadowOpacity() const; + + void readVisibleSets(); + + void installSet(uint64 setId); + void installDone(const MTPmessages_StickerSetInstallResult &result); + bool installFail(uint64 setId, const RPCError &error); + + Section _section; + Stickers::Order _archivedIds; + + int32 _rowHeight; + struct StickerSetRow { + StickerSetRow(uint64 id, DocumentData *sticker, int32 count, const QString &title, int titleWidth, bool installed, bool official, bool unread, bool disabled, bool recent, int32 pixw, int32 pixh) : id(id) + , sticker(sticker) + , count(count) + , title(title) + , titleWidth(titleWidth) + , installed(installed) + , official(official) + , unread(unread) + , disabled(disabled) + , recent(recent) + , pixw(pixw) + , pixh(pixh) + , yadd(0, 0) { + } + uint64 id; + DocumentData *sticker; + int32 count; + QString title; + int titleWidth; + bool installed, official, unread, disabled, recent; + int32 pixw, pixh; + anim::ivalue yadd; + }; + using StickerSetRows = QList; + + void rebuildAppendSet(const Stickers::Set &set, int maxNameWidth); + void fillSetCover(const Stickers::Set &set, DocumentData **outSticker, int *outWidth, int *outHeight) const; + int fillSetCount(const Stickers::Set &set) const; + QString fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const; + void fillSetFlags(const Stickers::Set &set, bool *outRecent, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outDisabled); + + int countMaxNameWidth() const; + + StickerSetRows _rows; + QList _animStartTimes; + uint64 _aboveShadowFadeStart = 0; + anim::fvalue _aboveShadowFadeOpacity = { 0., 0. }; + Animation _a_shifting; + + int _visibleTop = 0; + int _visibleBottom = 0; + int _itemsTop = 0; + + bool _saving = false; + + int _actionSel = -1; + int _actionDown = -1; + + int _clearWidth, _removeWidth, _returnWidth, _restoreWidth; + + ConfirmBox *_clearBox = nullptr; + + int _buttonHeight = 0; + bool _hasFeaturedButton = false; + bool _hasArchivedButton = false; + + QPoint _mouse; + int _selected = -3; // -2 - featured stickers button, -1 - archived stickers button + int _pressed = -2; + QPoint _dragStart; + int _started = -1; + int _dragging = -1; + int _above = -1; + + Ui::RectShadow _aboveShadow; + + int32 _scrollbar = 0; +}; diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index bbd97414c..3bf665a98 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -19,9 +19,9 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "lang.h" +#include "boxes/stickersetbox.h" -#include "stickersetbox.h" +#include "lang.h" #include "mainwidget.h" #include "mainwindow.h" #include "stickers/stickers.h" @@ -32,20 +32,128 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_boxes.h" #include "styles/style_stickers.h" -namespace { +StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st::stickersScroll) +, _inner(this, set) +, _shadow(this) +, _add(this, lang(lng_stickers_add_pack), st::defaultBoxButton) +, _share(this, lang(lng_stickers_share_pack), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _done(this, lang(lng_about_done), st::defaultBoxButton) { + setMaxHeight(st::stickersMaxHeight); + connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); -constexpr int kArchivedLimitFirstRequest = 10; -constexpr int kArchivedLimitPerPage = 30; + init(_inner, st::boxButtonPadding.bottom() + _cancel.height() + st::boxButtonPadding.top()); -} // namespace + connect(&_add, SIGNAL(clicked()), this, SLOT(onAddStickers())); + connect(&_share, SIGNAL(clicked()), this, SLOT(onShareStickers())); + connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(&_done, SIGNAL(clicked()), this, SLOT(onClose())); -StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : ScrolledWidget() + connect(_inner, SIGNAL(updateButtons()), this, SLOT(onUpdateButtons())); + connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); + + connect(_inner, SIGNAL(installed(uint64)), this, SLOT(onInstalled(uint64))); + + onStickersUpdated(); + + onScroll(); + + prepare(); +} + +void StickerSetBox::onInstalled(uint64 setId) { + emit installed(setId); + onClose(); +} + +void StickerSetBox::onStickersUpdated() { + showAll(); +} + +void StickerSetBox::onAddStickers() { + _inner->install(); +} + +void StickerSetBox::onShareStickers() { + QString url = qsl("https://telegram.me/addstickers/") + _inner->shortName(); + QApplication::clipboard()->setText(url); + Ui::showLayer(new InformBox(lang(lng_stickers_copied))); +} + +void StickerSetBox::onUpdateButtons() { + if (!_cancel.isHidden() || !_done.isHidden()) { + showAll(); + } +} + +void StickerSetBox::onScroll() { + auto scroll = scrollArea(); + auto scrollTop = scroll->scrollTop(); + _inner->setVisibleTopBottom(scrollTop, scrollTop + scroll->height()); +} + +void StickerSetBox::showAll() { + ScrollableBox::showAll(); + int32 cnt = _inner->notInstalled(); + if (_inner->loaded()) { + _shadow.show(); + if (_inner->notInstalled()) { + _add.show(); + _cancel.show(); + _share.hide(); + _done.hide(); + } else if (_inner->official()) { + _add.hide(); + _share.hide(); + _cancel.hide(); + _done.show(); + } else { + _share.show(); + _cancel.show(); + _add.hide(); + _done.hide(); + } + } else { + _shadow.hide(); + _add.hide(); + _share.hide(); + _cancel.show(); + _done.hide(); + } + resizeEvent(0); + update(); +} + +void StickerSetBox::paintEvent(QPaintEvent *e) { + Painter p(this); + if (paint(p)) return; + + paintTitle(p, _inner->title()); +} + +void StickerSetBox::resizeEvent(QResizeEvent *e) { + ScrollableBox::resizeEvent(e); + _inner->resize(width(), _inner->height()); + _shadow.setGeometry(0, height() - st::boxButtonPadding.bottom() - _cancel.height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); + _add.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _add.height()); + _share.moveToRight(st::boxButtonPadding.right(), _add.y()); + _done.moveToRight(st::boxButtonPadding.right(), _add.y()); + if (_add.isHidden() && _share.isHidden()) { + _cancel.moveToRight(st::boxButtonPadding.right(), _add.y()); + } else if (_add.isHidden()) { + _cancel.moveToRight(st::boxButtonPadding.right() + _share.width() + st::boxButtonPadding.left(), _add.y()); + } else { + _cancel.moveToRight(st::boxButtonPadding.right() + _add.width() + st::boxButtonPadding.left(), _add.y()); + } +} + +StickerSetBox::Inner::Inner(QWidget *parent, const MTPInputStickerSet &set) : ScrolledWidget(parent) , _input(set) { switch (set.type()) { case mtpc_inputStickerSetID: _setId = set.c_inputStickerSetID().vid.v; _setAccess = set.c_inputStickerSetID().vaccess_hash.v; break; case mtpc_inputStickerSetShortName: _setShortName = qs(set.c_inputStickerSetShortName().vshort_name); break; } - MTP::send(MTPmessages_GetStickerSet(_input), rpcDone(&StickerSetInner::gotSet), rpcFail(&StickerSetInner::failedSet)); + MTP::send(MTPmessages_GetStickerSet(_input), rpcDone(&Inner::gotSet), rpcFail(&Inner::failedSet)); App::main()->updateStickers(); subscribe(FileDownload::ImageLoaded(), [this] { update(); }); @@ -56,7 +164,7 @@ StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : ScrolledWidget connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); } -void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) { +void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) { _pack.clear(); _emoji.clear(); _packOvers.clear(); @@ -126,7 +234,7 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) { emit updateButtons(); } -bool StickerSetInner::failedSet(const RPCError &error) { +bool StickerSetBox::Inner::failedSet(const RPCError &error) { if (MTP::isDefaultHandledError(error)) return false; _loaded = true; @@ -136,7 +244,7 @@ bool StickerSetInner::failedSet(const RPCError &error) { return true; } -void StickerSetInner::installDone(const MTPmessages_StickerSetInstallResult &result) { +void StickerSetBox::Inner::installDone(const MTPmessages_StickerSetInstallResult &result) { auto &sets = Global::RefStickerSets(); bool wasArchived = (_setFlags & MTPDstickerSet::Flag::f_archived); @@ -189,7 +297,7 @@ void StickerSetInner::installDone(const MTPmessages_StickerSetInstallResult &res emit installed(_setId); } -bool StickerSetInner::installFail(const RPCError &error) { +bool StickerSetBox::Inner::installFail(const RPCError &error) { if (MTP::isDefaultHandledError(error)) return false; Ui::showLayer(new InformBox(lang(lng_stickers_not_found))); @@ -197,14 +305,14 @@ bool StickerSetInner::installFail(const RPCError &error) { return true; } -void StickerSetInner::mousePressEvent(QMouseEvent *e) { +void StickerSetBox::Inner::mousePressEvent(QMouseEvent *e) { int index = stickerFromGlobalPos(e->globalPos()); if (index >= 0 && index < _pack.size()) { _previewTimer.start(QApplication::startDragTime()); } } -void StickerSetInner::mouseMoveEvent(QMouseEvent *e) { +void StickerSetBox::Inner::mouseMoveEvent(QMouseEvent *e) { updateSelected(); if (_previewShown >= 0) { int index = stickerFromGlobalPos(e->globalPos()); @@ -215,7 +323,7 @@ void StickerSetInner::mouseMoveEvent(QMouseEvent *e) { } } -void StickerSetInner::mouseReleaseEvent(QMouseEvent *e) { +void StickerSetBox::Inner::mouseReleaseEvent(QMouseEvent *e) { if (_previewShown >= 0) { _previewShown = -1; return; @@ -233,7 +341,7 @@ void StickerSetInner::mouseReleaseEvent(QMouseEvent *e) { } } -void StickerSetInner::updateSelected() { +void StickerSetBox::Inner::updateSelected() { auto index = stickerFromGlobalPos(QCursor::pos()); if (isMasksSet()) { index = -1; @@ -246,7 +354,7 @@ void StickerSetInner::updateSelected() { } } -void StickerSetInner::startOverAnimation(int index, float64 from, float64 to) { +void StickerSetBox::Inner::startOverAnimation(int index, float64 from, float64 to) { if (index >= 0 && index < _packOvers.size()) { _packOvers[index].start([this, index] { int row = index / StickerPanPerRow; @@ -258,7 +366,7 @@ void StickerSetInner::startOverAnimation(int index, float64 from, float64 to) { } } -void StickerSetInner::onPreview() { +void StickerSetBox::Inner::onPreview() { int index = stickerFromGlobalPos(QCursor::pos()); if (index >= 0 && index < _pack.size()) { _previewShown = index; @@ -266,7 +374,7 @@ void StickerSetInner::onPreview() { } } -int32 StickerSetInner::stickerFromGlobalPos(const QPoint &p) const { +int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const { QPoint l(mapFromGlobal(p)); if (rtl()) l.setX(width() - l.x()); int32 row = (l.y() >= st::stickersPadding.top()) ? qFloor((l.y() - st::stickersPadding.top()) / st::stickersSize.height()) : -1; @@ -278,7 +386,7 @@ int32 StickerSetInner::stickerFromGlobalPos(const QPoint &p) const { return -1; } -void StickerSetInner::paintEvent(QPaintEvent *e) { +void StickerSetBox::Inner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); Painter p(this); @@ -331,1376 +439,42 @@ void StickerSetInner::paintEvent(QPaintEvent *e) { } } -void StickerSetInner::setVisibleTopBottom(int visibleTop, int visibleBottom) { +void StickerSetBox::Inner::setVisibleTopBottom(int visibleTop, int visibleBottom) { _visibleTop = visibleTop; _visibleBottom = visibleBottom; } -bool StickerSetInner::loaded() const { +bool StickerSetBox::Inner::loaded() const { return _loaded && !_pack.isEmpty(); } -int32 StickerSetInner::notInstalled() const { +int32 StickerSetBox::Inner::notInstalled() const { if (!_loaded) return 0; auto it = Global::StickerSets().constFind(_setId); if (it == Global::StickerSets().cend() || !(it->flags & MTPDstickerSet::Flag::f_installed) || (it->flags & MTPDstickerSet::Flag::f_archived)) return _pack.size(); return 0; } -bool StickerSetInner::official() const { +bool StickerSetBox::Inner::official() const { return _loaded && _setShortName.isEmpty(); } -QString StickerSetInner::title() const { +QString StickerSetBox::Inner::title() const { return _loaded ? (_pack.isEmpty() ? lang(lng_attach_failed) : _title) : lang(lng_contacts_loading); } -QString StickerSetInner::shortName() const { +QString StickerSetBox::Inner::shortName() const { return _setShortName; } -void StickerSetInner::install() { +void StickerSetBox::Inner::install() { if (isMasksSet()) { Ui::showLayer(new InformBox(lang(lng_stickers_masks_pack)), KeepOtherLayers); return; } if (_installRequest) return; - _installRequest = MTP::send(MTPmessages_InstallStickerSet(_input, MTP_bool(false)), rpcDone(&StickerSetInner::installDone), rpcFail(&StickerSetInner::installFail)); + _installRequest = MTP::send(MTPmessages_InstallStickerSet(_input, MTP_bool(false)), rpcDone(&Inner::installDone), rpcFail(&Inner::installFail)); } -StickerSetInner::~StickerSetInner() { -} - -StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st::stickersScroll) -, _inner(set) -, _shadow(this) -, _add(this, lang(lng_stickers_add_pack), st::defaultBoxButton) -, _share(this, lang(lng_stickers_share_pack), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) -, _done(this, lang(lng_about_done), st::defaultBoxButton) { - setMaxHeight(st::stickersMaxHeight); - connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); - - init(&_inner, st::boxButtonPadding.bottom() + _cancel.height() + st::boxButtonPadding.top()); - - connect(&_add, SIGNAL(clicked()), this, SLOT(onAddStickers())); - connect(&_share, SIGNAL(clicked()), this, SLOT(onShareStickers())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_done, SIGNAL(clicked()), this, SLOT(onClose())); - - connect(&_inner, SIGNAL(updateButtons()), this, SLOT(onUpdateButtons())); - connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); - - connect(&_inner, SIGNAL(installed(uint64)), this, SLOT(onInstalled(uint64))); - - onStickersUpdated(); - - onScroll(); - - prepare(); -} - -void StickerSetBox::onInstalled(uint64 setId) { - emit installed(setId); - onClose(); -} - -void StickerSetBox::onStickersUpdated() { - showAll(); -} - -void StickerSetBox::onAddStickers() { - _inner.install(); -} - -void StickerSetBox::onShareStickers() { - QString url = qsl("https://telegram.me/addstickers/") + _inner.shortName(); - QApplication::clipboard()->setText(url); - Ui::showLayer(new InformBox(lang(lng_stickers_copied))); -} - -void StickerSetBox::onUpdateButtons() { - if (!_cancel.isHidden() || !_done.isHidden()) { - showAll(); - } -} - -void StickerSetBox::onScroll() { - auto scroll = scrollArea(); - auto scrollTop = scroll->scrollTop(); - _inner.setVisibleTopBottom(scrollTop, scrollTop + scroll->height()); -} - -void StickerSetBox::showAll() { - ScrollableBox::showAll(); - int32 cnt = _inner.notInstalled(); - if (_inner.loaded()) { - _shadow.show(); - if (_inner.notInstalled()) { - _add.show(); - _cancel.show(); - _share.hide(); - _done.hide(); - } else if (_inner.official()) { - _add.hide(); - _share.hide(); - _cancel.hide(); - _done.show(); - } else { - _share.show(); - _cancel.show(); - _add.hide(); - _done.hide(); - } - } else { - _shadow.hide(); - _add.hide(); - _share.hide(); - _cancel.show(); - _done.hide(); - } - resizeEvent(0); - update(); -} - -void StickerSetBox::paintEvent(QPaintEvent *e) { - Painter p(this); - if (paint(p)) return; - - paintTitle(p, _inner.title()); -} - -void StickerSetBox::resizeEvent(QResizeEvent *e) { - ScrollableBox::resizeEvent(e); - _inner.resize(width(), _inner.height()); - _shadow.setGeometry(0, height() - st::boxButtonPadding.bottom() - _cancel.height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); - _add.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _add.height()); - _share.moveToRight(st::boxButtonPadding.right(), _add.y()); - _done.moveToRight(st::boxButtonPadding.right(), _add.y()); - if (_add.isHidden() && _share.isHidden()) { - _cancel.moveToRight(st::boxButtonPadding.right(), _add.y()); - } else if (_add.isHidden()) { - _cancel.moveToRight(st::boxButtonPadding.right() + _share.width() + st::boxButtonPadding.left(), _add.y()); - } else { - _cancel.moveToRight(st::boxButtonPadding.right() + _add.width() + st::boxButtonPadding.left(), _add.y()); - } -} - -namespace internal { - -StickersInner::StickersInner(StickersBox::Section section) : ScrolledWidget() -, _section(section) -, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) -, _a_shifting(animation(this, &StickersInner::step_shifting)) -, _itemsTop(st::membersPadding.top()) -, _clearWidth(st::normalFont->width(lang(lng_stickers_clear_recent))) -, _removeWidth(st::normalFont->width(lang(lng_stickers_remove))) -, _returnWidth(st::normalFont->width(lang(lng_stickers_return))) -, _restoreWidth(st::normalFont->width(lang(lng_stickers_restore))) -, _aboveShadow(st::boxShadow) { - setup(); -} - -StickersInner::StickersInner(const Stickers::Order &archivedIds) : ScrolledWidget() -, _section(StickersBox::Section::ArchivedPart) -, _archivedIds(archivedIds) -, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) -, _a_shifting(animation(this, &StickersInner::step_shifting)) -, _itemsTop(st::membersPadding.top()) -, _clearWidth(st::normalFont->width(lang(lng_stickers_clear_recent))) -, _removeWidth(st::normalFont->width(lang(lng_stickers_remove))) -, _returnWidth(st::normalFont->width(lang(lng_stickers_return))) -, _restoreWidth(st::normalFont->width(lang(lng_stickers_restore))) -, _aboveShadow(st::boxShadow) { - setup(); -} - -void StickersInner::setup() { - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); - setMouseTracking(true); -} - -void StickersInner::onImageLoaded() { - update(); - readVisibleSets(); -} - -void StickersInner::paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const { - if (selected) { - p.fillRect(0, y, width(), _buttonHeight, st::contactsBgOver); - } - p.setFont(st::stickersFeaturedFont); - p.setPen(st::stickersFeaturedPen); - p.drawTextLeft(st::stickersFeaturedPosition.x(), y + st::stickersFeaturedPosition.y(), width(), text); - - if (badgeCounter) { - Dialogs::Layout::UnreadBadgeStyle unreadSt; - unreadSt.sizeId = Dialogs::Layout::UnreadBadgeInStickersBox; - unreadSt.size = st::stickersFeaturedBadgeSize; - int unreadRight = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x()); - if (rtl()) unreadRight = width() - unreadRight; - int unreadTop = y + (_buttonHeight - st::stickersFeaturedBadgeSize) / 2; - Dialogs::Layout::paintUnreadCount(p, QString::number(badgeCounter), unreadRight, unreadTop, unreadSt); - } -} - -void StickersInner::paintEvent(QPaintEvent *e) { - QRect r(e->rect()); - Painter p(this); - - _a_shifting.step(); - - p.fillRect(r, st::white); - p.setClipRect(r); - - int y = st::membersPadding.top(); - if (_hasFeaturedButton) { - auto selected = (_selected == -2); - paintButton(p, y, selected, lang(lng_stickers_featured), Global::FeaturedStickerSetsUnreadCount()); - y += _buttonHeight; - } - if (_hasArchivedButton) { - auto selected = (_selected == -1); - paintButton(p, y, selected, lang(lng_stickers_archived), 0); - y += _buttonHeight; - } - - if (_rows.isEmpty()) { - p.setFont(st::noContactsFont); - p.setPen(st::noContactsColor); - p.drawText(QRect(0, y, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); - } else { - p.translate(0, _itemsTop); - - int32 yFrom = r.y() - _itemsTop, yTo = r.y() + r.height() - _itemsTop; - int32 from = floorclamp(yFrom - _rowHeight, _rowHeight, 0, _rows.size()); - int32 to = ceilclamp(yTo + _rowHeight, _rowHeight, 0, _rows.size()); - p.translate(0, from * _rowHeight); - for (int32 i = from; i < to; ++i) { - if (i != _above) { - paintRow(p, i); - } - p.translate(0, _rowHeight); - } - if (from <= _above && _above < to) { - p.translate(0, (_above - to) * _rowHeight); - paintRow(p, _above); - } - } -} - -void StickersInner::paintRow(Painter &p, int32 index) { - const StickerSetRow *s(_rows.at(index)); - - int32 xadd = 0, yadd = s->yadd.current(); - if (xadd || yadd) p.translate(xadd, yadd); - - if (_section == Section::Installed) { - bool removeSel = (index == _actionSel && (_actionDown < 0 || index == _actionDown)); - bool removeDown = removeSel && (index == _actionDown); - - p.setFont(removeSel ? st::linkOverFont : st::linkFont); - if (removeDown) { - p.setPen(st::btnDefLink.downColor); - } else { - p.setPen(st::btnDefLink.color); - } - int32 remWidth = s->recent ? _clearWidth : (s->disabled ? (s->official ? _restoreWidth : _returnWidth) : _removeWidth); - QString remText = lang(s->recent ? lng_stickers_clear_recent : (s->disabled ? (s->official ? lng_stickers_restore : lng_stickers_return) : lng_stickers_remove)); - p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), remText, remWidth); - - if (index == _above) { - float64 current = _aboveShadowFadeOpacity.current(); - if (_started >= 0) { - float64 o = aboveShadowOpacity(); - if (o > current) { - _aboveShadowFadeOpacity = anim::fvalue(o, o); - current = o; - } - } - p.setOpacity(current); - QRect row(myrtlrect(_aboveShadow.getDimensions(st::boxShadowShift).left(), st::contactsPadding.top() / 2, width() - (st::contactsPadding.left() / 2) - _scrollbar - _aboveShadow.getDimensions(st::boxShadowShift).right(), _rowHeight - ((st::contactsPadding.top() + st::contactsPadding.bottom()) / 2))); - _aboveShadow.paint(p, row, st::boxShadowShift); - p.fillRect(row, st::white); - p.setOpacity(1); - } - } else if (s->installed && !s->disabled) { - int addw = st::stickersAddSize.width(); - int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (addw + st::stickersFeaturedInstalled.width()) / 2); - int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2; - st::stickersFeaturedInstalled.paint(p, QPoint(checkx, checky), width()); - } else { - int addw = st::stickersAddSize.width(); - int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; - int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2; - QRect add(myrtlrect(addx, addy, addw, st::stickersAddSize.height())); - - auto textBg = (_actionSel == index) ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg; - App::roundRect(p, add, textBg, ImageRoundRadius::Small); - int iconx = addx + (st::stickersAddSize.width() - st::stickersAddIcon.width()) / 2; - int icony = addy + (st::stickersAddSize.height() - st::stickersAddIcon.height()) / 2; - icony += (_actionSel == index && _actionDown == index) ? (st::defaultActiveButton.downTextTop - st::defaultActiveButton.textTop) : 0; - st::stickersAddIcon.paint(p, QPoint(iconx, icony), width()); - } - - if (s->disabled && _section == Section::Installed) { - p.setOpacity(st::stickersRowDisabledOpacity); - } - if (s->sticker) { - s->sticker->thumb->load(); - QPixmap pix(s->sticker->thumb->pix(s->pixw, s->pixh)); - p.drawPixmapLeft(st::contactsPadding.left() + (st::contactsPhotoSize - s->pixw) / 2, st::contactsPadding.top() + (st::contactsPhotoSize - s->pixh) / 2, width(), pix); - } - - int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); - int namey = st::contactsPadding.top() + st::contactsNameTop; - int statusx = namex; - int statusy = st::contactsPadding.top() + st::contactsStatusTop; - - p.setFont(st::contactsNameFont); - p.setPen(st::black); - p.drawTextLeft(namex, namey, width(), s->title, s->titleWidth); - - if (s->unread) { - p.setPen(Qt::NoPen); - p.setBrush(st::stickersFeaturedUnreadBg); - - p.setRenderHint(QPainter::HighQualityAntialiasing, true); - p.drawEllipse(rtlrect(namex + s->titleWidth + st::stickersFeaturedUnreadSkip, namey + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); - p.setRenderHint(QPainter::HighQualityAntialiasing, false); - } - - p.setFont(st::contactsStatusFont); - p.setPen(st::contactsStatusFg); - p.drawTextLeft(statusx, statusy, width(), lng_stickers_count(lt_count, s->count)); - - p.setOpacity(1); - if (xadd || yadd) p.translate(-xadd, -yadd); -} - -void StickersInner::mousePressEvent(QMouseEvent *e) { - if (_saving) return; - if (_dragging >= 0) mouseReleaseEvent(e); - _mouse = e->globalPos(); - onUpdateSelected(); - - _pressed = _selected; - if (_actionSel >= 0) { - _actionDown = _actionSel; - update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); - } else if (_selected >= 0 && _section == Section::Installed && !_rows.at(_selected)->recent) { - _above = _dragging = _started = _selected; - _dragStart = mapFromGlobal(_mouse); - } -} - -void StickersInner::mouseMoveEvent(QMouseEvent *e) { - if (_saving) return; - _mouse = e->globalPos(); - onUpdateSelected(); -} - -void StickersInner::onUpdateSelected() { - if (_saving) return; - QPoint local(mapFromGlobal(_mouse)); - if (_dragging >= 0) { - int32 shift = 0; - uint64 ms = getms(); - int firstSetIndex = 0; - if (_rows.at(firstSetIndex)->recent) { - ++firstSetIndex; - } - if (_dragStart.y() > local.y() && _dragging > 0) { - shift = -floorclamp(_dragStart.y() - local.y() + (_rowHeight / 2), _rowHeight, 0, _dragging - firstSetIndex); - for (int32 from = _dragging, to = _dragging + shift; from > to; --from) { - qSwap(_rows[from], _rows[from - 1]); - _rows.at(from)->yadd = anim::ivalue(_rows.at(from)->yadd.current() - _rowHeight, 0); - _animStartTimes[from] = ms; - } - } else if (_dragStart.y() < local.y() && _dragging + 1 < _rows.size()) { - shift = floorclamp(local.y() - _dragStart.y() + (_rowHeight / 2), _rowHeight, 0, _rows.size() - _dragging - 1); - for (int32 from = _dragging, to = _dragging + shift; from < to; ++from) { - qSwap(_rows[from], _rows[from + 1]); - _rows.at(from)->yadd = anim::ivalue(_rows.at(from)->yadd.current() + _rowHeight, 0); - _animStartTimes[from] = ms; - } - } - if (shift) { - _dragging += shift; - _above = _dragging; - _dragStart.setY(_dragStart.y() + shift * _rowHeight); - if (!_a_shifting.animating()) { - _a_shifting.start(); - } - } - _rows.at(_dragging)->yadd = anim::ivalue(local.y() - _dragStart.y(), local.y() - _dragStart.y()); - _animStartTimes[_dragging] = 0; - _a_shifting.step(getms(), true); - - emit checkDraggingScroll(local.y()); - } else { - bool in = rect().marginsRemoved(QMargins(0, _itemsTop, 0, st::membersPadding.bottom())).contains(local); - int selected = -2; - int actionSel = -1; - if (in) { - selected = floorclamp(local.y() - _itemsTop, _rowHeight, 0, _rows.size() - 1); - - if (_section == Section::Installed) { - int remw = _rows.at(selected)->recent ? _clearWidth : (_rows.at(selected)->disabled ? (_rows.at(selected)->official ? _restoreWidth : _returnWidth) : _removeWidth); - QRect rem(myrtlrect(width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - remw, st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, remw, st::normalFont->height)); - actionSel = rem.contains(local.x(), local.y() - _itemsTop - selected * _rowHeight) ? selected : -1; - } else if (_rows.at(selected)->installed && !_rows.at(selected)->disabled) { - actionSel = -1; - } else { - int addw = st::stickersAddSize.width(); - int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; - int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2; - QRect add(myrtlrect(addx, addy, addw, st::stickersAddSize.height())); - actionSel = add.contains(local.x(), local.y() - _itemsTop - selected * _rowHeight) ? selected : -1; - } - } else if (_hasFeaturedButton && QRect(0, st::membersPadding.top(), width(), _buttonHeight).contains(local)) { - selected = -2; - } else if (_hasArchivedButton && QRect(0, st::membersPadding.top() + (_hasFeaturedButton ? _buttonHeight : 0), width(), _buttonHeight).contains(local)) { - selected = -1; - } else { - selected = -3; - } - if (_selected != selected) { - if (((_selected == -1) != (selected == -1)) || ((_selected == -2) != (selected == -2))) { - update(); - } - if (_section != Section::Installed && ((_selected >= 0 || _pressed >= 0) != (selected >= 0 || _pressed >= 0))) { - setCursor((selected >= 0 || _pressed >= 0) ? style::cur_pointer : style::cur_default); - } - _selected = selected; - } - setActionSel(actionSel); - emit noDraggingScroll(); - } -} - -void StickersInner::onClearRecent() { - if (_clearBox) { - _clearBox->onClose(); - } - - auto &sets = Global::RefStickerSets(); - bool removedCloud = (sets.remove(Stickers::CloudRecentSetId) != 0); - bool removedCustom = (sets.remove(Stickers::CustomSetId) != 0); - - auto &recent = cGetRecentStickers(); - if (!recent.isEmpty()) { - recent.clear(); - Local::writeUserSettings(); - } - - if (removedCustom) Local::writeInstalledStickers(); - if (removedCloud) Local::writeRecentStickers(); - emit App::main()->updateStickers(); - rebuild(); - - MTPmessages_ClearRecentStickers::Flags flags = 0; - MTP::send(MTPmessages_ClearRecentStickers(MTP_flags(flags))); -} - -void StickersInner::onClearBoxDestroyed(QObject *box) { - if (box == _clearBox) { - _clearBox = nullptr; - } -} - -float64 StickersInner::aboveShadowOpacity() const { - if (_above < 0) return 0; - - int32 dx = 0; - int32 dy = qAbs(_above * _rowHeight + _rows.at(_above)->yadd.current() - _started * _rowHeight); - return qMin((dx + dy) * 2. / _rowHeight, 1.); -} - -void StickersInner::mouseReleaseEvent(QMouseEvent *e) { - auto pressed = _pressed; - _pressed = -2; - - if (_section != Section::Installed && _selected < 0 && pressed >= 0) { - setCursor(style::cur_default); - } - - if (_saving) return; - - _mouse = e->globalPos(); - onUpdateSelected(); - if (_actionDown == _actionSel && _actionSel >= 0) { - if (_section == Section::Installed) { - if (_rows[_actionDown]->recent) { - _clearBox = new ConfirmBox(lang(lng_stickers_clear_recent_sure), lang(lng_stickers_clear_recent)); - connect(_clearBox, SIGNAL(confirmed()), this, SLOT(onClearRecent())); - connect(_clearBox, SIGNAL(destroyed(QObject*)), this, SLOT(onClearBoxDestroyed(QObject*))); - Ui::showLayer(_clearBox, KeepOtherLayers); - } else { - _rows[_actionDown]->disabled = !_rows[_actionDown]->disabled; - } - } else { - installSet(_rows[_actionDown]->id); - } - } else if (_dragging >= 0) { - QPoint local(mapFromGlobal(_mouse)); - _rows[_dragging]->yadd.start(0); - _aboveShadowFadeStart = _animStartTimes[_dragging] = getms(); - _aboveShadowFadeOpacity = anim::fvalue(aboveShadowOpacity(), 0); - if (!_a_shifting.animating()) { - _a_shifting.start(); - } - - _dragging = _started = -1; - } else if (pressed == _selected && _actionSel < 0 && _actionDown < 0) { - if (_selected == -2) { - _selected = -3; - Ui::showLayer(new StickersBox(Section::Featured), KeepOtherLayers); - } else if (_selected == -1) { - _selected = -3; - Ui::showLayer(new StickersBox(Section::Archived), KeepOtherLayers); - } else if (_selected >= 0 && _section != Section::Installed) { - auto &sets = Global::RefStickerSets(); - auto it = sets.find(_rows.at(pressed)->id); - if (it != sets.cend()) { - _selected = -3; - Ui::showLayer(new StickerSetBox(Stickers::inputSetId(*it)), KeepOtherLayers); - } - } - } - if (_actionDown >= 0) { - update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight); - _actionDown = -1; - } -} - -void StickersInner::leaveEvent(QEvent *e) { - _mouse = QPoint(-1, -1); - onUpdateSelected(); -} - -void StickersInner::installSet(uint64 setId) { - auto &sets = Global::RefStickerSets(); - auto it = sets.find(setId); - if (it == sets.cend()) { - rebuild(); - return; - } - - MTP::send(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_boolFalse()), rpcDone(&StickersInner::installDone), rpcFail(&StickersInner::installFail, setId)); - - Stickers::installLocally(setId); -} - -void StickersInner::installDone(const MTPmessages_StickerSetInstallResult &result) { - if (result.type() == mtpc_messages_stickerSetInstallResultArchive) { - Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive()); - } -} - -bool StickersInner::installFail(uint64 setId, const RPCError &error) { - if (MTP::isDefaultHandledError(error)) return false; - - auto &sets = Global::RefStickerSets(); - auto it = sets.find(setId); - if (it == sets.cend()) { - rebuild(); - return true; - } - - Stickers::undoInstallLocally(setId); - return true; -} - -void StickersInner::step_shifting(uint64 ms, bool timer) { - bool animating = false; - int32 updateMin = -1, updateMax = 0; - for (int32 i = 0, l = _animStartTimes.size(); i < l; ++i) { - uint64 start = _animStartTimes.at(i); - if (start) { - if (updateMin < 0) updateMin = i; - updateMax = i; - if (start + st::stickersRowDuration > ms && ms >= start) { - _rows.at(i)->yadd.update(float64(ms - start) / st::stickersRowDuration, anim::sineInOut); - animating = true; - } else { - _rows.at(i)->yadd.finish(); - _animStartTimes[i] = 0; - } - } - } - if (_aboveShadowFadeStart) { - if (updateMin < 0 || updateMin > _above) updateMin = _above; - if (updateMax < _above) updateMin = _above; - if (_aboveShadowFadeStart + st::stickersRowDuration > ms && ms > _aboveShadowFadeStart) { - _aboveShadowFadeOpacity.update(float64(ms - _aboveShadowFadeStart) / st::stickersRowDuration, anim::sineInOut); - animating = true; - } else { - _aboveShadowFadeOpacity.finish(); - _aboveShadowFadeStart = 0; - } - } - if (timer) { - if (_dragging >= 0) { - if (updateMin < 0 || updateMin > _dragging) updateMin = _dragging; - if (updateMax < _dragging) updateMax = _dragging; - } - if (updateMin >= 0) { - update(0, _itemsTop + _rowHeight * (updateMin - 1), width(), _rowHeight * (updateMax - updateMin + 3)); - } - } - if (!animating) { - _above = _dragging; - _a_shifting.stop(); - } -} - -void StickersInner::clear() { - for (int32 i = 0, l = _rows.size(); i < l; ++i) { - delete _rows.at(i); - } - _rows.clear(); - _animStartTimes.clear(); - _aboveShadowFadeStart = 0; - _aboveShadowFadeOpacity = anim::fvalue(0, 0); - _a_shifting.stop(); - _above = _dragging = _started = -1; - _selected = -3; - _pressed = -3; - _actionDown = -1; - setActionSel(-1); - update(); -} - -void StickersInner::setActionSel(int32 actionSel) { - if (actionSel != _actionSel) { - if (_actionSel >= 0) update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); - _actionSel = actionSel; - if (_actionSel >= 0) update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); - if (_section == Section::Installed) { - setCursor((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel)) ? style::cur_pointer : style::cur_default); - } - } -} - -void StickersInner::rebuild() { - _hasFeaturedButton = _hasArchivedButton = false; - _itemsTop = st::membersPadding.top(); - _buttonHeight = st::stickersFeaturedHeight; - if (_section == Section::Installed) { - if (!Global::FeaturedStickerSetsOrder().isEmpty()) { - _itemsTop += _buttonHeight; - _hasFeaturedButton = true; - } - if (!Global::ArchivedStickerSetsOrder().isEmpty()) { - _itemsTop += _buttonHeight; - _hasArchivedButton = true; - } - if (_itemsTop > st::membersPadding.top()) { - _itemsTop += st::membersPadding.top(); - } - } - - int maxNameWidth = countMaxNameWidth(); - - clear(); - auto &order = ([this]() -> const Stickers::Order & { - if (_section == Section::Installed) { - return Global::StickerSetsOrder(); - } else if (_section == Section::Featured) { - return Global::FeaturedStickerSetsOrder(); - } else if (_section == Section::Archived) { - return Global::ArchivedStickerSetsOrder(); - } - return _archivedIds; - })(); - _rows.reserve(order.size() + 1); - _animStartTimes.reserve(order.size() + 1); - - auto &sets = Global::StickerSets(); - if (_section == Section::Installed) { - auto cloudIt = sets.constFind(Stickers::CloudRecentSetId); - if (cloudIt != sets.cend() && !cloudIt->stickers.isEmpty()) { - rebuildAppendSet(cloudIt.value(), maxNameWidth); - } - } - for_const (auto setId, order) { - auto it = sets.constFind(setId); - if (it == sets.cend()) { - continue; - } - - rebuildAppendSet(it.value(), maxNameWidth); - - if (it->stickers.isEmpty() || (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { - App::api()->scheduleStickerSetRequest(it->id, it->access); - } - } - App::api()->requestStickerSets(); - updateSize(); -} - -void StickersInner::updateSize() { - resize(width(), _itemsTop + _rows.size() * _rowHeight + st::membersPadding.bottom()); -} - -void StickersInner::updateRows() { - int maxNameWidth = countMaxNameWidth(); - auto &sets = Global::StickerSets(); - for_const (auto row, _rows) { - auto it = sets.constFind(row->id); - if (it != sets.cend()) { - auto &set = it.value(); - if (!row->sticker) { - DocumentData *sticker = nullptr; - int pixw = 0, pixh = 0; - fillSetCover(set, &sticker, &pixw, &pixh); - if (sticker) { - row->sticker = sticker; - row->pixw = pixw; - row->pixh = pixh; - } - } - fillSetFlags(set, &row->recent, &row->installed, &row->official, &row->unread, &row->disabled); - if (_section == Section::Installed) { - row->disabled = false; - } - row->title = fillSetTitle(set, maxNameWidth, &row->titleWidth); - row->count = fillSetCount(set); - } - } - update(); -} - -bool StickersInner::appendSet(const Stickers::Set &set) { - for_const (auto row, _rows) { - if (row->id == set.id) { - return false; - } - } - rebuildAppendSet(set, countMaxNameWidth()); - return true; -} - -int StickersInner::countMaxNameWidth() const { - int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); - int namew = st::boxWideWidth - namex - st::contactsPadding.right() - st::contactsCheckPosition.x(); - if (_section == Section::Installed) { - namew -= qMax(qMax(qMax(_returnWidth, _removeWidth), _restoreWidth), _clearWidth); - } else { - namew -= st::stickersAddIcon.width() - st::defaultActiveButton.width; - namew -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip; - } - return namew; -} - -void StickersInner::rebuildAppendSet(const Stickers::Set &set, int maxNameWidth) { - bool recent = false, installed = false, official = false, unread = false, disabled = false; - fillSetFlags(set, &recent, &installed, &official, &unread, &disabled); - if (_section == Section::Installed && disabled) { - return; - } - - DocumentData *sticker = nullptr; - int pixw = 0, pixh = 0; - fillSetCover(set, &sticker, &pixw, &pixh); - - int titleWidth = 0; - QString title = fillSetTitle(set, maxNameWidth, &titleWidth); - int count = fillSetCount(set); - - _rows.push_back(new StickerSetRow(set.id, sticker, count, title, titleWidth, installed, official, unread, disabled, recent, pixw, pixh)); - _animStartTimes.push_back(0); -} - -void StickersInner::fillSetCover(const Stickers::Set &set, DocumentData **outSticker, int *outWidth, int *outHeight) const { - if (set.stickers.isEmpty()) { - *outSticker = nullptr; - *outWidth = *outHeight = 0; - return; - } - auto sticker = *outSticker = set.stickers.front(); - - auto pixw = sticker->thumb->width(); - auto pixh = sticker->thumb->height(); - if (pixw > st::contactsPhotoSize) { - if (pixw > pixh) { - pixh = (pixh * st::contactsPhotoSize) / pixw; - pixw = st::contactsPhotoSize; - } else { - pixw = (pixw * st::contactsPhotoSize) / pixh; - pixh = st::contactsPhotoSize; - } - } else if (pixh > st::contactsPhotoSize) { - pixw = (pixw * st::contactsPhotoSize) / pixh; - pixh = st::contactsPhotoSize; - } - *outWidth = pixw; - *outHeight = pixh; -} - -int StickersInner::fillSetCount(const Stickers::Set &set) const { - int result = set.stickers.isEmpty() ? set.count : set.stickers.size(), added = 0; - if (set.id == Stickers::CloudRecentSetId) { - auto customIt = Global::StickerSets().constFind(Stickers::CustomSetId); - if (customIt != Global::StickerSets().cend()) { - added = customIt->stickers.size(); - for_const (auto &sticker, cGetRecentStickers()) { - if (customIt->stickers.indexOf(sticker.first) < 0) { - ++added; - } - } - } else { - added = cGetRecentStickers().size(); - } - } - return result + added; -} - -QString StickersInner::fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const { - auto result = set.title; - int titleWidth = st::contactsNameFont->width(result); - if (titleWidth > maxNameWidth) { - result = st::contactsNameFont->elided(result, maxNameWidth); - titleWidth = st::contactsNameFont->width(result); - } - if (outTitleWidth) { - *outTitleWidth = titleWidth; - } - return result; -} - -void StickersInner::fillSetFlags(const Stickers::Set &set, bool *outRecent, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outDisabled) { - *outRecent = (set.id == Stickers::CloudRecentSetId); - *outInstalled = true; - *outOfficial = true; - *outUnread = false; - *outDisabled = false; - if (!*outRecent) { - *outInstalled = (set.flags & MTPDstickerSet::Flag::f_installed); - *outOfficial = (set.flags & MTPDstickerSet::Flag::f_official); - *outDisabled = (set.flags & MTPDstickerSet::Flag::f_archived); - if (_section == Section::Featured) { - *outUnread = (set.flags & MTPDstickerSet_ClientFlag::f_unread); - } - } -} - -Stickers::Order StickersInner::getOrder() const { - Stickers::Order result; - result.reserve(_rows.size()); - for (int32 i = 0, l = _rows.size(); i < l; ++i) { - if (_rows.at(i)->disabled || _rows.at(i)->recent) { - continue; - } - result.push_back(_rows.at(i)->id); - } - return result; -} - -Stickers::Order StickersInner::getDisabledSets() const { - Stickers::Order result; - result.reserve(_rows.size()); - for (int32 i = 0, l = _rows.size(); i < l; ++i) { - if (_rows.at(i)->disabled) { - result.push_back(_rows.at(i)->id); - } - } - return result; -} - -void StickersInner::setVisibleTopBottom(int visibleTop, int visibleBottom) { - if (_section == Section::Featured) { - _visibleTop = visibleTop; - _visibleBottom = visibleBottom; - readVisibleSets(); - } -} - -void StickersInner::readVisibleSets() { - auto itemsVisibleTop = _visibleTop - _itemsTop; - auto itemsVisibleBottom = _visibleBottom - _itemsTop; - int rowFrom = floorclamp(itemsVisibleTop, _rowHeight, 0, _rows.size()); - int rowTo = ceilclamp(itemsVisibleBottom, _rowHeight, 0, _rows.size()); - for (int i = rowFrom; i < rowTo; ++i) { - if (!_rows[i]->unread) { - continue; - } - if (i * _rowHeight < itemsVisibleTop || (i + 1) * _rowHeight > itemsVisibleBottom) { - continue; - } - if (!_rows[i]->sticker || _rows[i]->sticker->thumb->loaded() || _rows[i]->sticker->loaded()) { - Stickers::markFeaturedAsRead(_rows[i]->id); - } - } -} - -void StickersInner::setVisibleScrollbar(int32 width) { - _scrollbar = width; -} - -StickersInner::~StickersInner() { - clear(); -} - -} // namespace internal - -StickersBox::StickersBox(Section section) : ItemListBox(st::boxScroll) -, _section(section) -, _inner(section) -, _aboutWidth(st::boxWideWidth - 2 * st::stickersReorderPadding.top()) -, _about(st::boxTextFont, lang((section == Section::Archived) ? lng_stickers_packs_archived : lng_stickers_reorder), _defaultOptions, _aboutWidth) { - setup(); -} - -StickersBox::StickersBox(const Stickers::Order &archivedIds) : ItemListBox(st::boxScroll) -, _section(Section::ArchivedPart) -, _inner(archivedIds) -, _aboutWidth(st::boxWideWidth - 2 * st::stickersReorderPadding.top()) -, _about(st::boxTextFont, lang(lng_stickers_packs_archived), _defaultOptions, _aboutWidth) { - setup(); -} - -void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result) { - _archivedRequestId = 0; - if (result.type() != mtpc_messages_archivedStickers) { - return; - } - - auto &stickers = result.c_messages_archivedStickers(); - auto &archived = Global::RefArchivedStickerSetsOrder(); - if (offsetId) { - auto index = archived.indexOf(offsetId); - if (index >= 0) { - archived = archived.mid(0, index + 1); - } - } else { - archived.clear(); - } - - bool addedSet = false; - auto &v = stickers.vsets.c_vector().v; - for_const (auto &stickerSet, v) { - const MTPDstickerSet *setData = nullptr; - switch (stickerSet.type()) { - case mtpc_stickerSetCovered: { - auto &d = stickerSet.c_stickerSetCovered(); - if (d.vset.type() == mtpc_stickerSet) { - setData = &d.vset.c_stickerSet(); - } - } break; - case mtpc_stickerSetMultiCovered: { - auto &d = stickerSet.c_stickerSetMultiCovered(); - if (d.vset.type() == mtpc_stickerSet) { - setData = &d.vset.c_stickerSet(); - } - } break; - } - if (!setData) continue; - - if (auto set = Stickers::feedSet(*setData)) { - auto index = archived.indexOf(set->id); - if (archived.isEmpty() || index != archived.size() - 1) { - if (index < archived.size() - 1) { - archived.removeAt(index); - } - archived.push_back(set->id); - } - if (_section == Section::Archived) { - if (_inner->appendSet(*set)) { - addedSet = true; - if (set->stickers.isEmpty() || (set->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { - App::api()->scheduleStickerSetRequest(set->id, set->access); - } - } - } - } - } - if (_section == Section::Installed && !archived.isEmpty()) { - Local::writeArchivedStickers(); - rebuildList(); - } else if (_section == Section::Archived) { - if (addedSet) { - _inner->updateSize(); - setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); - _inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); - App::api()->requestStickerSets(); - } else { - _allArchivedLoaded = v.isEmpty() || (offsetId != 0); - } - } - checkLoadMoreArchived(); -} - -void StickersBox::setup() { - if (_section == Section::Installed) { - Local::readArchivedStickers(); - if (Global::ArchivedStickerSetsOrder().isEmpty()) { - MTPmessages_GetArchivedStickers::Flags flags = 0; - _archivedRequestId = MTP::send(MTPmessages_GetArchivedStickers(MTP_flags(flags), MTP_long(0), MTP_int(kArchivedLimitFirstRequest)), rpcDone(&StickersBox::getArchivedDone, 0ULL)); - } - } else if (_section == Section::Archived) { - // Reload the archived list. - MTPmessages_GetArchivedStickers::Flags flags = 0; - _archivedRequestId = MTP::send(MTPmessages_GetArchivedStickers(MTP_flags(flags), MTP_long(0), MTP_int(kArchivedLimitFirstRequest)), rpcDone(&StickersBox::getArchivedDone, 0ULL)); - - auto &sets = Global::StickerSets(); - for_const (auto setId, Global::ArchivedStickerSetsOrder()) { - auto it = sets.constFind(setId); - if (it != sets.cend()) { - if (it->stickers.isEmpty() && (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { - App::api()->scheduleStickerSetRequest(setId, it->access); - } - } - } - App::api()->requestStickerSets(); - } - - int bottomSkip = st::boxPadding.bottom(); - if (_section == Section::Installed) { - _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); - _topShadow.create(this, st::contactsAboutShadow); - - _save.create(this, lang(lng_settings_save), st::defaultBoxButton); - connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); - - _cancel.create(this, lang(lng_cancel), st::cancelBoxButton); - connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - - _bottomShadow.create(this); - bottomSkip = st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); - } else if (_section == Section::ArchivedPart) { - _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); - _topShadow.create(this, st::contactsAboutShadow); - - _save.create(this, lang(lng_box_ok), st::defaultBoxButton); - connect(_save, SIGNAL(clicked()), this, SLOT(onClose())); - } else if (_section == Section::Archived) { - _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); - _topShadow.create(this, st::contactsAboutShadow); - } - ItemListBox::init(_inner, bottomSkip, st::boxTitleHeight + _aboutHeight); - setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); - - connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); - App::main()->updateStickers(); - - connect(_inner, SIGNAL(checkDraggingScroll(int)), this, SLOT(onCheckDraggingScroll(int))); - connect(_inner, SIGNAL(noDraggingScroll()), this, SLOT(onNoDraggingScroll())); - connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); - connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); - _scrollTimer.setSingleShot(false); - - rebuildList(); - - prepare(); -} - -void StickersBox::onScroll() { - updateVisibleTopBottom(); - checkLoadMoreArchived(); -} - -void StickersBox::updateVisibleTopBottom() { - auto visibleTop = scrollArea()->scrollTop(); - auto visibleBottom = visibleTop + scrollArea()->height(); - _inner->setVisibleTopBottom(visibleTop, visibleBottom); -} - -void StickersBox::checkLoadMoreArchived() { - if (_section != Section::Archived) return; - - int scrollTop = scrollArea()->scrollTop(), scrollTopMax = scrollArea()->scrollTopMax(); - if (scrollTop + PreloadHeightsCount * scrollArea()->height() >= scrollTopMax) { - if (!_archivedRequestId && !_allArchivedLoaded) { - uint64 lastId = 0; - for (auto setId = Global::ArchivedStickerSetsOrder().cend(), e = Global::ArchivedStickerSetsOrder().cbegin(); setId != e;) { - --setId; - auto it = Global::StickerSets().constFind(*setId); - if (it != Global::StickerSets().cend()) { - if (it->flags & MTPDstickerSet::Flag::f_archived) { - lastId = it->id; - break; - } - } - } - MTPmessages_GetArchivedStickers::Flags flags = 0; - _archivedRequestId = MTP::send(MTPmessages_GetArchivedStickers(MTP_flags(flags), MTP_long(lastId), MTP_int(kArchivedLimitPerPage)), rpcDone(&StickersBox::getArchivedDone, lastId)); - } - } -} - -int32 StickersBox::countHeight() const { - int bottomSkip = st::boxPadding.bottom(); - if (_section == Section::Installed) { - bottomSkip = st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); - } - return st::boxTitleHeight + _aboutHeight + _inner->height() + bottomSkip; -} - -void StickersBox::disenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req) { - _disenableRequests.remove(req); - if (_disenableRequests.isEmpty()) { - saveOrder(); - } -} - -bool StickersBox::disenableFail(const RPCError &error, mtpRequestId req) { - if (MTP::isDefaultHandledError(error)) return false; - _disenableRequests.remove(req); - if (_disenableRequests.isEmpty()) { - saveOrder(); - } - return true; -} - -void StickersBox::saveOrder() { - auto order = _inner->getOrder(); - if (order.size() > 1) { - QVector mtpOrder; - mtpOrder.reserve(order.size()); - for (int i = 0, l = order.size(); i < l; ++i) { - mtpOrder.push_back(MTP_long(order.at(i))); - } - - MTPmessages_ReorderStickerSets::Flags flags = 0; - _reorderRequest = MTP::send(MTPmessages_ReorderStickerSets(MTP_flags(flags), MTP_vector(mtpOrder)), rpcDone(&StickersBox::reorderDone), rpcFail(&StickersBox::reorderFail)); - } else { - reorderDone(MTP_boolTrue()); - } -} - -void StickersBox::reorderDone(const MTPBool &result) { - _reorderRequest = 0; - onClose(); -} - -bool StickersBox::reorderFail(const RPCError &result) { - if (MTP::isDefaultHandledError(result)) return false; - _reorderRequest = 0; - Global::SetLastStickersUpdate(0); - App::main()->updateStickers(); - onClose(); - return true; -} - -void StickersBox::paintEvent(QPaintEvent *e) { - Painter p(this); - if (paint(p)) return; - - auto title = ([this]() { - if (_section == Section::Installed) { - return lang(lng_stickers_packs); - } else if (_section == Section::Featured) { - return lang(lng_stickers_featured); - } - return lang(lng_stickers_archived); - })(); - paintTitle(p, title); - p.translate(0, st::boxTitleHeight); - - if (_aboutHeight > 0) { - p.fillRect(0, 0, width(), _aboutHeight, st::contactsAboutBg); - p.setPen(st::stickersReorderFg); - _about.draw(p, st::stickersReorderPadding.top(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center); - } -} - -void StickersBox::closePressed() { - if (!_disenableRequests.isEmpty()) { - for_const (auto requestId, _disenableRequests) { - MTP::cancel(requestId); - } - _disenableRequests.clear(); - Global::SetLastStickersUpdate(0); - App::main()->updateStickers(); - } else if (_reorderRequest) { - MTP::cancel(_reorderRequest); - _reorderRequest = 0; - Global::SetLastStickersUpdate(0); - App::main()->updateStickers(); - } -} - -StickersBox::~StickersBox() { - if (_section == Section::Archived) { - Local::writeArchivedStickers(); - } -} - -void StickersBox::resizeEvent(QResizeEvent *e) { - ItemListBox::resizeEvent(e); - _inner->resize(width(), _inner->height()); - _inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); - updateVisibleTopBottom(); - if (_topShadow) { - _topShadow->setGeometry(0, st::boxTitleHeight + _aboutHeight, width(), st::lineWidth); - } - if (_save) { - _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); - } - if (_cancel) { - _cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); - _bottomShadow->setGeometry(0, height() - st::boxButtonPadding.bottom() - _save->height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); - } -} - -void StickersBox::onStickersUpdated() { - if (_section == Section::Installed || _section == Section::Featured) { - rebuildList(); - } else { - _inner->updateRows(); - } -} - -void StickersBox::rebuildList() { - _inner->rebuild(); - setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); - _inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); -} - -void StickersBox::onCheckDraggingScroll(int localY) { - if (localY < scrollArea()->scrollTop()) { - _scrollDelta = localY - scrollArea()->scrollTop(); - } else if (localY >= scrollArea()->scrollTop() + scrollArea()->height()) { - _scrollDelta = localY - scrollArea()->scrollTop() - scrollArea()->height() + 1; - } else { - _scrollDelta = 0; - } - if (_scrollDelta) { - _scrollTimer.start(15); - } else { - _scrollTimer.stop(); - } -} - -void StickersBox::onNoDraggingScroll() { - _scrollTimer.stop(); -} - -void StickersBox::onScrollTimer() { - int32 d = (_scrollDelta > 0) ? qMin(_scrollDelta * 3 / 20 + 1, int32(MaxScrollSpeed)) : qMax(_scrollDelta * 3 / 20 - 1, -int32(MaxScrollSpeed)); - scrollArea()->scrollToY(scrollArea()->scrollTop() + d); -} - -void StickersBox::onSave() { - if (!_inner->savingStart()) { - return; - } - - bool writeRecent = false, writeArchived = false; - auto &recent = cGetRecentStickers(); - auto &sets = Global::RefStickerSets(); - - auto reorder = _inner->getOrder(), disabled = _inner->getDisabledSets(); - for (int32 i = 0, l = disabled.size(); i < l; ++i) { - auto it = sets.find(disabled.at(i)); - if (it != sets.cend()) { - for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) { - if (it->stickers.indexOf(i->first) >= 0) { - i = recent.erase(i); - writeRecent = true; - } else { - ++i; - } - } - if (!(it->flags & MTPDstickerSet::Flag::f_archived)) { - MTPInputStickerSet setId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName)); - if (it->flags & MTPDstickerSet::Flag::f_official) { - _disenableRequests.insert(MTP::send(MTPmessages_InstallStickerSet(setId, MTP_boolTrue()), rpcDone(&StickersBox::disenableDone), rpcFail(&StickersBox::disenableFail), 0, 5)); - it->flags |= MTPDstickerSet::Flag::f_archived; - auto index = Global::RefArchivedStickerSetsOrder().indexOf(it->id); - if (index < 0) { - Global::RefArchivedStickerSetsOrder().push_front(it->id); - writeArchived = true; - } - } else { - _disenableRequests.insert(MTP::send(MTPmessages_UninstallStickerSet(setId), rpcDone(&StickersBox::disenableDone), rpcFail(&StickersBox::disenableFail), 0, 5)); - int removeIndex = Global::StickerSetsOrder().indexOf(it->id); - if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); - if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured) && !(it->flags & MTPDstickerSet_ClientFlag::f_special)) { - sets.erase(it); - } else { - if (it->flags & MTPDstickerSet::Flag::f_archived) { - writeArchived = true; - } - it->flags &= ~(MTPDstickerSet::Flag::f_installed | MTPDstickerSet::Flag::f_archived); - } - } - } - } - } - - // Clear all installed flags, set only for sets from order. - for (auto &set : sets) { - if (!(set.flags & MTPDstickerSet::Flag::f_archived)) { - set.flags &= ~MTPDstickerSet::Flag::f_installed; - } - } - - auto &order(Global::RefStickerSetsOrder()); - order.clear(); - for (int i = 0, l = reorder.size(); i < l; ++i) { - auto it = sets.find(reorder.at(i)); - if (it != sets.cend()) { - if ((it->flags & MTPDstickerSet::Flag::f_archived) && !disabled.contains(it->id)) { - MTPInputStickerSet setId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName)); - _disenableRequests.insert(MTP::send(MTPmessages_InstallStickerSet(setId, MTP_boolFalse()), rpcDone(&StickersBox::disenableDone), rpcFail(&StickersBox::disenableFail), 0, 5)); - it->flags &= ~MTPDstickerSet::Flag::f_archived; - writeArchived = true; - } - order.push_back(reorder.at(i)); - it->flags |= MTPDstickerSet::Flag::f_installed; - } - } - for (auto it = sets.begin(); it != sets.cend();) { - if ((it->flags & MTPDstickerSet_ClientFlag::f_featured) - || (it->flags & MTPDstickerSet::Flag::f_installed) - || (it->flags & MTPDstickerSet::Flag::f_archived) - || (it->flags & MTPDstickerSet_ClientFlag::f_special)) { - ++it; - } else { - it = sets.erase(it); - } - } - - Local::writeInstalledStickers(); - if (writeRecent) Local::writeUserSettings(); - if (writeArchived) Local::writeArchivedStickers(); - emit App::main()->stickersUpdated(); - - if (_disenableRequests.isEmpty()) { - saveOrder(); - } else { - MTP::sendAnything(); - } -} - -void StickersBox::showAll() { - if (_topShadow) { - _topShadow->show(); - } - if (_save) { - _save->show(); - } - if (_cancel) { - _cancel->show(); - _bottomShadow->show(); - } - ItemListBox::showAll(); -} - -int32 stickerPacksCount(bool includeDisabledOfficial) { - int32 result = 0; - auto &order = Global::StickerSetsOrder(); - auto &sets = Global::StickerSets(); - for (int i = 0, l = order.size(); i < l; ++i) { - auto it = sets.constFind(order.at(i)); - if (it != sets.cend()) { - if (!(it->flags & MTPDstickerSet::Flag::f_archived) || ((it->flags & MTPDstickerSet::Flag::f_official) && includeDisabledOfficial)) { - ++result; - } - } - } - return result; +StickerSetBox::Inner::~Inner() { } diff --git a/Telegram/SourceFiles/boxes/stickersetbox.h b/Telegram/SourceFiles/boxes/stickersetbox.h index 1f9cc7561..8252ab4ab 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.h +++ b/Telegram/SourceFiles/boxes/stickersetbox.h @@ -24,15 +24,52 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/vector_of_moveable.h" class ConfirmBox; + namespace Ui { class PlainShadow; } // namespace Ui -class StickerSetInner : public ScrolledWidget, public RPCSender, private base::Subscriber { +class StickerSetBox : public ScrollableBox, public RPCSender { Q_OBJECT public: - StickerSetInner(const MTPInputStickerSet &set); + StickerSetBox(const MTPInputStickerSet &set); + +public slots: + void onStickersUpdated(); + void onAddStickers(); + void onShareStickers(); + void onUpdateButtons(); + + void onScroll(); + +private slots: + void onInstalled(uint64 id); + +signals: + void installed(uint64 id); + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + + void showAll() override; + +private: + class Inner; + ChildWidget _inner; + ScrollableBoxShadow _shadow; + BoxButton _add, _share, _cancel, _done; + QString _title; + +}; + +// This class is hold in header because it requires Qt preprocessing. +class StickerSetBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { + Q_OBJECT + +public: + Inner(QWidget *parent, const MTPInputStickerSet &set); bool loaded() const; int32 notInstalled() const; @@ -43,7 +80,7 @@ public: void setVisibleTopBottom(int visibleTop, int visibleBottom) override; void install(); - ~StickerSetInner(); + ~Inner(); protected: void mousePressEvent(QMouseEvent *e) override; @@ -96,253 +133,3 @@ private: int _previewShown = -1; }; - -class StickerSetBox : public ScrollableBox, public RPCSender { - Q_OBJECT - -public: - StickerSetBox(const MTPInputStickerSet &set); - -public slots: - void onStickersUpdated(); - void onAddStickers(); - void onShareStickers(); - void onUpdateButtons(); - - void onScroll(); - -private slots: - void onInstalled(uint64 id); - -signals: - void installed(uint64 id); - -protected: - void paintEvent(QPaintEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - - void showAll() override; - -private: - StickerSetInner _inner; - ScrollableBoxShadow _shadow; - BoxButton _add, _share, _cancel, _done; - QString _title; - -}; - -namespace internal { -class StickersInner; -} // namespace internal - -class StickersBox : public ItemListBox, public RPCSender { - Q_OBJECT - -public: - enum class Section { - Installed, - Featured, - Archived, - ArchivedPart, - }; - StickersBox(Section section = Section::Installed); - StickersBox(const Stickers::Order &archivedIds); - - ~StickersBox(); - -public slots: - void onStickersUpdated(); - - void onCheckDraggingScroll(int localY); - void onNoDraggingScroll(); - void onScrollTimer(); - - void onSave(); - -private slots: - void onScroll(); - -protected: - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - - void closePressed() override; - void showAll() override; - -private: - void setup(); - int32 countHeight() const; - void rebuildList(); - - void disenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req); - bool disenableFail(const RPCError &error, mtpRequestId req); - void reorderDone(const MTPBool &result); - bool reorderFail(const RPCError &result); - void saveOrder(); - - void updateVisibleTopBottom(); - void checkLoadMoreArchived(); - void getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result); - - Section _section; - - ChildWidget _inner; - ChildWidget _save = { nullptr }; - ChildWidget _cancel = { nullptr }; - OrderedSet _disenableRequests; - mtpRequestId _reorderRequest = 0; - ChildWidget _topShadow = { nullptr }; - ChildWidget _bottomShadow = { nullptr }; - - QTimer _scrollTimer; - int32 _scrollDelta = 0; - - int _aboutWidth = 0; - Text _about; - int _aboutHeight = 0; - - mtpRequestId _archivedRequestId = 0; - bool _allArchivedLoaded = false; - -}; - -int32 stickerPacksCount(bool includeDisabledOfficial = false); - -namespace internal { - -class StickersInner : public ScrolledWidget, public RPCSender, private base::Subscriber { - Q_OBJECT - -public: - using Section = StickersBox::Section; - StickersInner(Section section); - StickersInner(const Stickers::Order &archivedIds); - - void rebuild(); - void updateSize(); - void updateRows(); // refresh only pack cover stickers - bool appendSet(const Stickers::Set &set); - bool savingStart() { - if (_saving) return false; - _saving = true; - return true; - } - - Stickers::Order getOrder() const; - Stickers::Order getDisabledSets() const; - - void setVisibleScrollbar(int32 width); - void setVisibleTopBottom(int visibleTop, int visibleBottom) override; - - ~StickersInner(); - -protected: - void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void leaveEvent(QEvent *e) override; - -signals: - void checkDraggingScroll(int localY); - void noDraggingScroll(); - -public slots: - void onUpdateSelected(); - void onClearRecent(); - void onClearBoxDestroyed(QObject *box); - -private slots: - void onImageLoaded(); - -private: - void setup(); - void paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const; - - void step_shifting(uint64 ms, bool timer); - void paintRow(Painter &p, int32 index); - void clear(); - void setActionSel(int32 actionSel); - float64 aboveShadowOpacity() const; - - void readVisibleSets(); - - void installSet(uint64 setId); - void installDone(const MTPmessages_StickerSetInstallResult &result); - bool installFail(uint64 setId, const RPCError &error); - - Section _section; - Stickers::Order _archivedIds; - - int32 _rowHeight; - struct StickerSetRow { - StickerSetRow(uint64 id, DocumentData *sticker, int32 count, const QString &title, int titleWidth, bool installed, bool official, bool unread, bool disabled, bool recent, int32 pixw, int32 pixh) : id(id) - , sticker(sticker) - , count(count) - , title(title) - , titleWidth(titleWidth) - , installed(installed) - , official(official) - , unread(unread) - , disabled(disabled) - , recent(recent) - , pixw(pixw) - , pixh(pixh) - , yadd(0, 0) { - } - uint64 id; - DocumentData *sticker; - int32 count; - QString title; - int titleWidth; - bool installed, official, unread, disabled, recent; - int32 pixw, pixh; - anim::ivalue yadd; - }; - using StickerSetRows = QList; - - void rebuildAppendSet(const Stickers::Set &set, int maxNameWidth); - void fillSetCover(const Stickers::Set &set, DocumentData **outSticker, int *outWidth, int *outHeight) const; - int fillSetCount(const Stickers::Set &set) const; - QString fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const; - void fillSetFlags(const Stickers::Set &set, bool *outRecent, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outDisabled); - - int countMaxNameWidth() const; - - StickerSetRows _rows; - QList _animStartTimes; - uint64 _aboveShadowFadeStart = 0; - anim::fvalue _aboveShadowFadeOpacity = { 0., 0. }; - Animation _a_shifting; - - int _visibleTop = 0; - int _visibleBottom = 0; - int _itemsTop = 0; - - bool _saving = false; - - int _actionSel = -1; - int _actionDown = -1; - - int _clearWidth, _removeWidth, _returnWidth, _restoreWidth; - - ConfirmBox *_clearBox = nullptr; - - int _buttonHeight = 0; - bool _hasFeaturedButton = false; - bool _hasArchivedButton = false; - - QPoint _mouse; - int _selected = -3; // -2 - featured stickers button, -1 - archived stickers button - int _pressed = -2; - QPoint _dragStart; - int _started = -1; - int _dragging = -1; - int _above = -1; - - Ui::RectShadow _aboveShadow; - - int32 _scrollbar = 0; -}; - -} // namespace internal diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index f45ecebad..1f5ab35f9 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -243,7 +243,7 @@ structure::Variable ParsedFile::readVariable(const QString &name) { structure::Variable result = { composeFullName(name) }; if (auto value = readValue()) { result.value = value; - if (value.type().tag != structure::TypeTag::Struct) { + if (value.type().tag != structure::TypeTag::Struct || !value.copyOf().empty()) { assertNextToken(BasicType::Semicolon); } } diff --git a/Telegram/SourceFiles/profile/profile_cover.cpp b/Telegram/SourceFiles/profile/profile_cover.cpp index d9ac7434d..96dd3da7a 100644 --- a/Telegram/SourceFiles/profile/profile_cover.cpp +++ b/Telegram/SourceFiles/profile/profile_cover.cpp @@ -529,14 +529,14 @@ void CoverWidget::onAddMember() { if (_peerChat->count >= Global::ChatSizeMax() && _peerChat->amCreator()) { Ui::showLayer(new ConvertToSupergroupBox(_peerChat)); } else { - Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); + Ui::showLayer(new ContactsBox(_peerChat, MembersFilter::Recent)); } } else if (_peerChannel && _peerChannel->mgInfo) { MembersAlreadyIn already; for_const (auto user, _peerChannel->mgInfo->lastParticipants) { already.insert(user); } - Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already)); + Ui::showLayer(new ContactsBox(_peerChannel, MembersFilter::Recent, already)); } } diff --git a/Telegram/SourceFiles/profile/profile_members_widget.cpp b/Telegram/SourceFiles/profile/profile_members_widget.cpp index 022f96772..2f9b1a6c8 100644 --- a/Telegram/SourceFiles/profile/profile_members_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_members_widget.cpp @@ -700,13 +700,13 @@ int ChannelMembersWidget::resizeGetHeight(int newWidth) { void ChannelMembersWidget::onAdmins() { if (auto channel = peer()->asChannel()) { - Ui::showLayer(new MembersBox(channel, MembersFilterAdmins)); + Ui::showLayer(new MembersBox(channel, MembersFilter::Admins)); } } void ChannelMembersWidget::onMembers() { if (auto channel = peer()->asChannel()) { - Ui::showLayer(new MembersBox(channel, MembersFilterRecent)); + Ui::showLayer(new MembersBox(channel, MembersFilter::Recent)); } } diff --git a/Telegram/SourceFiles/profile/profile_settings_widget.cpp b/Telegram/SourceFiles/profile/profile_settings_widget.cpp index d94ec9868..4c6d254c8 100644 --- a/Telegram/SourceFiles/profile/profile_settings_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_settings_widget.cpp @@ -164,9 +164,9 @@ void SettingsWidget::onNotificationsChange() { void SettingsWidget::onManageAdmins() { if (auto chat = peer()->asChat()) { - Ui::showLayer(new ContactsBox(chat, MembersFilterAdmins)); + Ui::showLayer(new ContactsBox(chat, MembersFilter::Admins)); } else if (auto channel = peer()->asChannel()) { - Ui::showLayer(new MembersBox(channel, MembersFilterAdmins)); + Ui::showLayer(new MembersBox(channel, MembersFilter::Admins)); } } diff --git a/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp b/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp index 53ae1053a..329da42a4 100644 --- a/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp @@ -29,7 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "mainwindow.h" #include "boxes/emojibox.h" -#include "boxes/stickersetbox.h" +#include "boxes/stickers_box.h" #include "boxes/downloadpathbox.h" #include "boxes/connectionbox.h" #include "boxes/confirmbox.h" diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index a7f0e5563..74dca4181 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_stickers.h" #include "boxes/confirmbox.h" #include "boxes/stickersetbox.h" +#include "boxes/stickers_box.h" #include "inline_bots/inline_bot_result.h" #include "inline_bots/inline_bot_layout_item.h" #include "dialogs/dialogs_layout.h" diff --git a/Telegram/SourceFiles/stickers/stickers.cpp b/Telegram/SourceFiles/stickers/stickers.cpp index 266ede52d..0a47e900d 100644 --- a/Telegram/SourceFiles/stickers/stickers.cpp +++ b/Telegram/SourceFiles/stickers/stickers.cpp @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "stickers.h" -#include "boxes/stickersetbox.h" +#include "boxes/stickers_box.h" #include "boxes/confirmbox.h" #include "lang.h" #include "apiwrap.h" diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp index 40a2c0f16..a13c1712b 100644 --- a/Telegram/SourceFiles/ui/countryinput.cpp +++ b/Telegram/SourceFiles/ui/countryinput.cpp @@ -24,8 +24,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "application.h" #include "ui/scrollarea.h" +#include "ui/buttons/icon_button.h" #include "boxes/contactsbox.h" #include "countries.h" +#include "styles/style_boxes.h" namespace { @@ -192,7 +194,88 @@ void CountryInput::setText(const QString &newText) { _text = _st.font->elided(newText, width() - _st.textMrg.left() - _st.textMrg.right()); } -CountrySelectInner::CountrySelectInner() : TWidget() +CountrySelectBox::CountrySelectBox() : ItemListBox(st::countriesScroll, st::boxWidth) +, _inner(this) +, _filter(this, st::boxSearchField, lang(lng_country_ph)) +, _filterCancel(this, st::boxSearchCancel) +, _topShadow(this) { + ItemListBox::init(_inner, st::boxScrollSkip, st::boxTitleHeight + _filter->height()); + + connect(_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate())); + connect(_filter, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_filterCancel, SIGNAL(clicked()), this, SLOT(onFilterCancel())); + connect(_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int))); + connect(_inner, SIGNAL(countryChosen(const QString&)), this, SIGNAL(countryChosen(const QString&))); + + _filterCancel->setAttribute(Qt::WA_OpaquePaintEvent); + + prepare(); +} + +void CountrySelectBox::onSubmit() { + _inner->chooseCountry(); +} + +void CountrySelectBox::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Down) { + _inner->selectSkip(1); + } else if (e->key() == Qt::Key_Up) { + _inner->selectSkip(-1); + } else if (e->key() == Qt::Key_PageDown) { + _inner->selectSkipPage(scrollArea()->height(), 1); + } else if (e->key() == Qt::Key_PageUp) { + _inner->selectSkipPage(scrollArea()->height(), -1); + } else { + ItemListBox::keyPressEvent(e); + } +} + +void CountrySelectBox::paintEvent(QPaintEvent *e) { + Painter p(this); + if (paint(p)) return; + + paintTitle(p, lang(lng_country_select)); +} + +void CountrySelectBox::resizeEvent(QResizeEvent *e) { + ItemListBox::resizeEvent(e); + _filter->resize(width(), _filter->height()); + _filter->moveToLeft(0, st::boxTitleHeight); + _filterCancel->moveToRight(0, st::boxTitleHeight); + _inner->resize(width(), _inner->height()); + _topShadow.setGeometry(0, st::boxTitleHeight + _filter->height(), width(), st::lineWidth); +} + +void CountrySelectBox::showAll() { + _filter->show(); + if (_filter->getLastText().isEmpty()) { + _filterCancel->hide(); + } else { + _filterCancel->show(); + } + _topShadow.show(); + ItemListBox::showAll(); +} + +void CountrySelectBox::onFilterCancel() { + _filter->setText(QString()); +} + +void CountrySelectBox::onFilterUpdate() { + scrollArea()->scrollToY(0); + if (_filter->getLastText().isEmpty()) { + _filterCancel->hide(); + } else { + _filterCancel->show(); + } + _inner->updateFilter(_filter->getLastText()); +} + +void CountrySelectBox::doSetInnerFocus() { + _filter->setFocus(); +} + +CountrySelectBox::Inner::Inner(QWidget *parent) : ScrolledWidget(parent) , _rowHeight(st::countryRowHeight) , _sel(0) , _mouseSel(false) { @@ -239,7 +322,7 @@ CountrySelectInner::CountrySelectInner() : TWidget() updateFilter(); } -void CountrySelectInner::paintEvent(QPaintEvent *e) { +void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) { Painter p(this); QRect r(e->rect()); p.setClipRect(r); @@ -283,11 +366,11 @@ void CountrySelectInner::paintEvent(QPaintEvent *e) { } } -void CountrySelectInner::enterEvent(QEvent *e) { +void CountrySelectBox::Inner::enterEvent(QEvent *e) { setMouseTracking(true); } -void CountrySelectInner::leaveEvent(QEvent *e) { +void CountrySelectBox::Inner::leaveEvent(QEvent *e) { _mouseSel = false; setMouseTracking(false); if (_sel >= 0) { @@ -296,13 +379,13 @@ void CountrySelectInner::leaveEvent(QEvent *e) { } } -void CountrySelectInner::mouseMoveEvent(QMouseEvent *e) { +void CountrySelectBox::Inner::mouseMoveEvent(QMouseEvent *e) { _mouseSel = true; _lastMousePos = e->globalPos(); updateSel(); } -void CountrySelectInner::mousePressEvent(QMouseEvent *e) { +void CountrySelectBox::Inner::mousePressEvent(QMouseEvent *e) { _mouseSel = true; _lastMousePos = e->globalPos(); updateSel(); @@ -311,7 +394,7 @@ void CountrySelectInner::mousePressEvent(QMouseEvent *e) { } } -void CountrySelectInner::updateFilter(QString filter) { +void CountrySelectBox::Inner::updateFilter(QString filter) { filter = textSearchKey(filter); QStringList f; @@ -366,7 +449,7 @@ void CountrySelectInner::updateFilter(QString filter) { } } -void CountrySelectInner::selectSkip(int32 dir) { +void CountrySelectBox::Inner::selectSkip(int32 dir) { _mouseSel = false; int cur = (_sel >= 0) ? _sel : -1; @@ -384,13 +467,13 @@ void CountrySelectInner::selectSkip(int32 dir) { update(); } -void CountrySelectInner::selectSkipPage(int32 h, int32 dir) { +void CountrySelectBox::Inner::selectSkipPage(int32 h, int32 dir) { int32 points = h / _rowHeight; if (!points) return; selectSkip(points * dir); } -void CountrySelectInner::chooseCountry() { +void CountrySelectBox::Inner::chooseCountry() { QString result; if (_filter.isEmpty()) { if (_sel >= 0 && _sel < countriesAll.size()) { @@ -404,11 +487,11 @@ void CountrySelectInner::chooseCountry() { emit countryChosen(result); } -void CountrySelectInner::refresh() { +void CountrySelectBox::Inner::refresh() { resize(width(), countriesNow->length() ? (countriesNow->length() * _rowHeight + st::countriesSkip) : st::noContactsHeight); } -void CountrySelectInner::updateSel() { +void CountrySelectBox::Inner::updateSel() { if (!_mouseSel) return; QPoint p(mapFromGlobal(_lastMousePos)); @@ -422,89 +505,8 @@ void CountrySelectInner::updateSel() { } } -void CountrySelectInner::updateSelectedRow() { +void CountrySelectBox::Inner::updateSelectedRow() { if (_sel >= 0) { update(0, st::countriesSkip + _sel * _rowHeight, width(), _rowHeight); } } - -CountrySelectBox::CountrySelectBox() : ItemListBox(st::countriesScroll, st::boxWidth) -, _inner() -, _filter(this, st::boxSearchField, lang(lng_country_ph)) -, _filterCancel(this, st::boxSearchCancel) -, _topShadow(this) { - ItemListBox::init(&_inner, st::boxScrollSkip, st::boxTitleHeight + _filter.height()); - - connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate())); - connect(&_filter, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_filterCancel, SIGNAL(clicked()), this, SLOT(onFilterCancel())); - connect(&_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int))); - connect(&_inner, SIGNAL(countryChosen(const QString&)), this, SIGNAL(countryChosen(const QString&))); - - _filterCancel.setAttribute(Qt::WA_OpaquePaintEvent); - - prepare(); -} - -void CountrySelectBox::onSubmit() { - _inner.chooseCountry(); -} - -void CountrySelectBox::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Down) { - _inner.selectSkip(1); - } else if (e->key() == Qt::Key_Up) { - _inner.selectSkip(-1); - } else if (e->key() == Qt::Key_PageDown) { - _inner.selectSkipPage(scrollArea()->height(), 1); - } else if (e->key() == Qt::Key_PageUp) { - _inner.selectSkipPage(scrollArea()->height(), -1); - } else { - ItemListBox::keyPressEvent(e); - } -} - -void CountrySelectBox::paintEvent(QPaintEvent *e) { - Painter p(this); - if (paint(p)) return; - - paintTitle(p, lang(lng_country_select)); -} - -void CountrySelectBox::resizeEvent(QResizeEvent *e) { - ItemListBox::resizeEvent(e); - _filter.resize(width(), _filter.height()); - _filter.moveToLeft(0, st::boxTitleHeight); - _filterCancel.moveToRight(0, st::boxTitleHeight); - _inner.resize(width(), _inner.height()); - _topShadow.setGeometry(0, st::boxTitleHeight + _filter.height(), width(), st::lineWidth); -} - -void CountrySelectBox::showAll() { - _filter.show(); - if (_filter.getLastText().isEmpty()) { - _filterCancel.hide(); - } else { - _filterCancel.show(); - } - _topShadow.show(); - ItemListBox::showAll(); -} - -void CountrySelectBox::onFilterCancel() { - _filter.setText(QString()); -} - -void CountrySelectBox::onFilterUpdate() { - scrollArea()->scrollToY(0); - if (_filter.getLastText().isEmpty()) { - _filterCancel.hide(); - } else { - _filterCancel.show(); - } - _inner.updateFilter(_filter.getLastText()); -} - -void CountrySelectBox::doSetInnerFocus() { - _filter.setFocus(); -} diff --git a/Telegram/SourceFiles/ui/countryinput.h b/Telegram/SourceFiles/ui/countryinput.h index 8292f0187..971b192cb 100644 --- a/Telegram/SourceFiles/ui/countryinput.h +++ b/Telegram/SourceFiles/ui/countryinput.h @@ -29,6 +29,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org QString findValidCode(QString fullCode); class CountrySelect; +class InputField; + +namespace Ui { +class IconButton; +} // namespace Ui class CountryInput : public QWidget { Q_OBJECT @@ -63,11 +68,48 @@ private: }; -class CountrySelectInner : public TWidget { +namespace internal { +class CountrySelectInner; +} // namespace internal + +class CountrySelectBox : public ItemListBox { Q_OBJECT public: - CountrySelectInner(); + CountrySelectBox(); + +signals: + void countryChosen(const QString &iso); + +public slots: + void onFilterUpdate(); + void onFilterCancel(); + void onSubmit(); + +protected: + void keyPressEvent(QKeyEvent *e) override; + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + + void doSetInnerFocus() override; + void showAll() override; + +private: + class Inner; + ChildWidget _inner; + ChildWidget _filter; + ChildWidget _filterCancel; + + ScrollableBoxShadow _topShadow; + +}; + +// This class is hold in header because it requires Qt preprocessing. +class CountrySelectBox::Inner : public ScrolledWidget { + Q_OBJECT + +public: + Inner(QWidget *parent); void updateFilter(QString filter = QString()); @@ -104,34 +146,3 @@ private: QPoint _lastMousePos; }; - -class CountrySelectBox : public ItemListBox { - Q_OBJECT - -public: - CountrySelectBox(); - -signals: - void countryChosen(const QString &iso); - -public slots: - void onFilterUpdate(); - void onFilterCancel(); - void onSubmit(); - -protected: - void keyPressEvent(QKeyEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - - void doSetInnerFocus() override; - void showAll() override; - -private: - CountrySelectInner _inner; - InputField _filter; - IconedButton _filterCancel; - - ScrollableBoxShadow _topShadow; - -}; diff --git a/Telegram/SourceFiles/ui/widgets/filled_slider.h b/Telegram/SourceFiles/ui/widgets/filled_slider.h index 7037b9544..87bb6e7ed 100644 --- a/Telegram/SourceFiles/ui/widgets/filled_slider.h +++ b/Telegram/SourceFiles/ui/widgets/filled_slider.h @@ -21,10 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/widgets/continuous_slider.h" - -namespace style { -struct FilledSlider; -} // namespace style +#include "styles/style_widgets.h" namespace Ui { diff --git a/Telegram/SourceFiles/ui/widgets/media_slider.h b/Telegram/SourceFiles/ui/widgets/media_slider.h index 4bf1967d3..e01e29af5 100644 --- a/Telegram/SourceFiles/ui/widgets/media_slider.h +++ b/Telegram/SourceFiles/ui/widgets/media_slider.h @@ -21,10 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/widgets/continuous_slider.h" - -namespace style { -struct MediaSlider; -} // namespace style +#include "styles/style_widgets.h" namespace Ui { diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 3c86f2e6d..974e779ae 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -183,6 +183,8 @@ '<(src_loc)/boxes/languagebox.h', '<(src_loc)/boxes/localstoragebox.cpp', '<(src_loc)/boxes/localstoragebox.h', + '<(src_loc)/boxes/members_box.cpp', + '<(src_loc)/boxes/members_box.h', '<(src_loc)/boxes/notifications_box.cpp', '<(src_loc)/boxes/notifications_box.h', '<(src_loc)/boxes/passcodebox.cpp', @@ -199,6 +201,8 @@ '<(src_loc)/boxes/sharebox.h', '<(src_loc)/boxes/stickersetbox.cpp', '<(src_loc)/boxes/stickersetbox.h', + '<(src_loc)/boxes/stickers_box.cpp', + '<(src_loc)/boxes/stickers_box.h', '<(src_loc)/boxes/usernamebox.cpp', '<(src_loc)/boxes/usernamebox.h', '<(src_loc)/core/basic_types.h',