Scrollable boxes reorganized: _scroll always owns _inner.

Also some boxes moved to separate modules: MembersBox, StickersBox.
This commit is contained in:
John Preston 2016-10-20 19:32:15 +03:00
parent 2bd561821a
commit e5a5273b3a
34 changed files with 3676 additions and 3566 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -343,48 +343,6 @@ boxScroll: flatScroll(solidScroll) {
boxScrollSkip: 6px; boxScrollSkip: 6px;
boxScrollShadowBg: #00000012; 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; titleBg: #6389a8;
titleHeight: 39px; titleHeight: 39px;
titleIconPos: point(7px, 7px); titleIconPos: point(7px, 7px);

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

View file

@ -205,15 +205,7 @@ void ScrollableBox::resizeEvent(QResizeEvent *e) {
AbstractBox::resizeEvent(e); AbstractBox::resizeEvent(e);
} }
void ScrollableBox::init(QWidget *inner, int bottomSkip, int topSkip) { void ScrollableBox::init(ScrolledWidget *inner, int bottomSkip, int topSkip) {
_bottomSkip = bottomSkip;
_topSkip = topSkip;
_scroll->setWidget(inner);
_scroll->setFocusPolicy(Qt::NoFocus);
ScrollableBox::resizeEvent(nullptr);
}
void ScrollableBox::initOwned(QWidget *inner, int bottomSkip, int topSkip) {
_bottomSkip = bottomSkip; _bottomSkip = bottomSkip;
_topSkip = topSkip; _topSkip = topSkip;
_scroll->setOwnedWidget(inner); _scroll->setOwnedWidget(inner);

View file

@ -105,8 +105,7 @@ public:
ScrollableBox(const style::flatScroll &scroll, int w = st::boxWideWidth); ScrollableBox(const style::flatScroll &scroll, int w = st::boxWideWidth);
protected: protected:
void init(QWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight); void init(ScrolledWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight);
void initOwned(QWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight);
void resizeEvent(QResizeEvent *e) override; void resizeEvent(QResizeEvent *e) override;

View file

@ -27,11 +27,41 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "window/chat_background.h" #include "window/chat_background.h"
#include "styles/style_overview.h" #include "styles/style_overview.h"
BackgroundInner::BackgroundInner() : BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll)
_bgCount(0), _rows(0), _over(-1), _overDown(-1) { , _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()) { if (App::cServerBackgrounds().isEmpty()) {
resize(BackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding); 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 { } else {
updateWallpapers(); updateWallpapers();
} }
@ -40,7 +70,7 @@ _bgCount(0), _rows(0), _over(-1), _overDown(-1) {
setMouseTracking(true); setMouseTracking(true);
} }
void BackgroundInner::gotWallpapers(const MTPVector<MTPWallPaper> &result) { void BackgroundBox::Inner::gotWallpapers(const MTPVector<MTPWallPaper> &result) {
App::WallPapers wallpapers; App::WallPapers wallpapers;
wallpapers.push_back(App::WallPaper(0, ImagePtr(st::msgBG0), ImagePtr(st::msgBG0))); wallpapers.push_back(App::WallPaper(0, ImagePtr(st::msgBG0), ImagePtr(st::msgBG0)));
@ -98,7 +128,7 @@ void BackgroundInner::gotWallpapers(const MTPVector<MTPWallPaper> &result) {
updateWallpapers(); updateWallpapers();
} }
void BackgroundInner::updateWallpapers() { void BackgroundBox::Inner::updateWallpapers() {
_bgCount = App::cServerBackgrounds().size(); _bgCount = App::cServerBackgrounds().size();
_rows = _bgCount / BackgroundsInRow; _rows = _bgCount / BackgroundsInRow;
if (_bgCount % BackgroundsInRow) ++_rows; 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()); QRect r(e->rect());
Painter p(this); 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 x = e->pos().x(), y = e->pos().y();
int row = int((y - st::backgroundPadding) / (st::backgroundSize.height() + st::backgroundPadding)); 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; 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; _overDown = _over;
} }
void BackgroundInner::mouseReleaseEvent(QMouseEvent *e) { void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
if (_overDown == _over && _over >= 0) { if (_overDown == _over && _over >= 0) {
emit backgroundChosen(_over); emit backgroundChosen(_over);
} else if (_over < 0) { } else if (_over < 0) {
@ -173,33 +203,5 @@ void BackgroundInner::mouseReleaseEvent(QMouseEvent *e) {
} }
} }
void BackgroundInner::resizeEvent(QResizeEvent *e) { void BackgroundBox::Inner::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();
} }

View file

@ -23,11 +23,30 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "abstractbox.h" #include "abstractbox.h"
#include "core/lambda_wrap.h" #include "core/lambda_wrap.h"
class BackgroundInner : public TWidget, public RPCSender, private base::Subscriber { class BackgroundBox : public ItemListBox {
Q_OBJECT Q_OBJECT
public: public:
BackgroundInner(); BackgroundBox();
public slots:
void onBackgroundChosen(int index);
protected:
void paintEvent(QPaintEvent *e) override;
private:
class Inner;
ChildWidget<Inner> _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: signals:
void backgroundChosen(int index); void backgroundChosen(int index);
@ -47,20 +66,3 @@ private:
int32 _over, _overDown; 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;
};

View file

@ -68,6 +68,38 @@ aboutRevokePublicLabel: flatLabel(labelDefFlat) {
textFg: windowTextFg; 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 { contactsPhotoCheckbox: RoundImageCheckbox {
imageRadius: 21px; imageRadius: 21px;
imageSmallRadius: 18px; imageSmallRadius: 18px;

File diff suppressed because it is too large Load diff

View file

@ -23,186 +23,24 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "abstractbox.h" #include "abstractbox.h"
#include "core/single_timer.h" #include "core/single_timer.h"
#include "ui/effects/round_image_checkbox.h" #include "ui/effects/round_image_checkbox.h"
#include "boxes/members_box.h"
namespace Dialogs { namespace Dialogs {
class Row; class Row;
class IndexedList; class IndexedList;
} // namespace Dialogs } // namespace Dialogs
enum MembersFilter { namespace Ui {
MembersFilterRecent, class IconButton;
MembersFilterAdmins, } // namespace Ui
};
using MembersAlreadyIn = OrderedSet<UserData*>;
QString cantInviteError(); QString cantInviteError();
class ConfirmBox; inline Ui::RoundImageCheckbox::PaintRoundImage PaintUserpicCallback(PeerData *peer) {
class ContactsInner : public TWidget, public RPCSender, private base::Subscriber { return [peer](Painter &p, int x, int y, int outerWidth, int size) {
Q_OBJECT peer->paintUserpicLeft(p, size, x, y, outerWidth);
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<UserData*> selected();
QVector<MTPInputUser> 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<MTPPeer> &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 <typename FilterCallback>
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<Dialogs::IndexedList> _customList;
Dialogs::IndexedList *_contacts = nullptr;
Dialogs::Row *_sel = nullptr;
QString _filter;
typedef QVector<Dialogs::Row*> 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<Ui::RoundImageCheckbox> checkbox;
Text name;
QString statusText;
bool statusHasOnlineColor = false;
bool disabledChecked = false;
}; };
typedef QMap<PeerData*, ContactData*> ContactsData; }
ContactsData _contactsData;
typedef QMap<PeerData*, bool> CheckedContacts;
CheckedContacts _checkedContacts;
ContactData *contactData(Dialogs::Row *row);
bool _searching = false;
QString _lastQuery;
typedef QVector<PeerData*> ByUsernameRows;
typedef QVector<ContactData*> 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 { class ContactsBox : public ItemListBox, public RPCSender {
Q_OBJECT Q_OBJECT
@ -245,9 +83,10 @@ protected:
private: private:
void init(); void init();
ContactsInner _inner; class Inner;
InputField _filter; ChildWidget<Inner> _inner;
IconedButton _filterCancel; ChildWidget<InputField> _filter;
ChildWidget<Ui::IconButton> _filterCancel;
BoxButton _next, _cancel; BoxButton _next, _cancel;
MembersFilter _membersFilter; 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 Q_OBJECT
private:
struct MemberData;
public: 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 selectSkip(int32 dir);
void selectSkipPage(int32 h, int32 dir); void selectSkipPage(int32 h, int32 dir);
QVector<UserData*> selected();
QVector<MTPInputUser> selectedInputs();
bool allAdmins() const {
return _allAdmins.checked();
}
void loadProfilePhotos(int32 yFrom); void loadProfilePhotos(int32 yFrom);
void chooseParticipant(); void chooseParticipant();
void peopleReceived(const QString &query, const QVector<MTPPeer> &people);
void refresh(); void refresh();
ChatData *chat() const;
ChannelData *channel() const; ChannelData *channel() const;
MembersFilter filter() const; MembersFilter membersFilter() const;
UserData *bot() const;
CreatingGroupType creating() const;
bool isLoaded() const { bool sharingBotGame() const;
return !_loading;
int32 selectedCount() const;
bool hasAlreadyMembersInChannel() const {
return !_already.isEmpty();
} }
void clearSel();
MembersAlreadyIn already() const; void saving(bool flag);
~MembersInner(); ~Inner();
signals: signals:
void mustScrollTo(int ymin, int ymax); void mustScrollTo(int ymin, int ymax);
void selectAllQuery();
void searchByUsername();
void chosenChanged();
void adminAdded();
void addRequested(); void addRequested();
void loaded();
public slots: public slots:
void load(); void onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow);
void updateSel(); void updateSel();
void peerUpdated(PeerData *peer); void peerUpdated(PeerData *peer);
void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); 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: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
@ -340,102 +203,94 @@ protected:
void leaveEvent(QEvent *e) override; void leaveEvent(QEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(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<UserData*> MemberRows;
typedef QVector<QDateTime> MemberDates;
typedef QVector<MemberRole> MemberRoles;
typedef QVector<MemberData*> 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; void resizeEvent(QResizeEvent *e) override;
private: private:
MembersInner _inner; struct ContactData {
ContactData();
ContactData(PeerData *peer, Ui::RoundImageCheckbox::UpdateCallback &&updateCallback);
ContactsBox *_addBox; std_::unique_ptr<Ui::RoundImageCheckbox> 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 <typename FilterCallback>
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<Dialogs::IndexedList> _customList;
Dialogs::IndexedList *_contacts = nullptr;
Dialogs::Row *_sel = nullptr;
QString _filter;
typedef QVector<Dialogs::Row*> FilteredDialogs;
FilteredDialogs _filtered;
int _filteredSel = -1;
bool _mouseSel = false;
int _selCount = 0;
typedef QMap<PeerData*, ContactData*> ContactsData;
ContactsData _contactsData;
typedef QMap<PeerData*, bool> CheckedContacts;
CheckedContacts _checkedContacts;
ContactData *contactData(Dialogs::Row *row);
bool _searching = false;
QString _lastQuery;
typedef QVector<PeerData*> ByUsernameRows;
typedef QVector<ContactData*> 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);
};
}

View file

@ -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<MTPChannelParticipant>::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;
}

View file

@ -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<UserData*>;
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> _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<UserData*> MemberRows;
typedef QVector<QDateTime> MemberDates;
typedef QVector<MemberRole> MemberRoles;
typedef QVector<MemberData*> MemberDatas;
MemberRows _rows;
MemberDates _dates;
MemberRoles _roles;
MemberDatas _datas;
int32 _aboutWidth;
Text _about;
int32 _aboutHeight;
QPoint _lastMousePos;
};

View file

@ -30,213 +30,23 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "countries.h" #include "countries.h"
#include "confirmbox.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) SessionsBox::SessionsBox() : ScrollableBox(st::sessionsScroll)
, _loading(true) , _loading(true)
, _inner(&_list, &_current) , _inner(this, &_list, &_current)
, _shadow(this) , _shadow(this)
, _done(this, lang(lng_about_done), st::defaultBoxButton) , _done(this, lang(lng_about_done), st::defaultBoxButton)
, _shortPollRequest(0) { , _shortPollRequest(0) {
setMaxHeight(st::sessionsHeight); setMaxHeight(st::sessionsHeight);
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose())); connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_inner, SIGNAL(oneTerminated()), this, SLOT(onOneTerminated())); connect(_inner, SIGNAL(oneTerminated()), this, SLOT(onOneTerminated()));
connect(&_inner, SIGNAL(allTerminated()), this, SLOT(onAllTerminated())); connect(_inner, SIGNAL(allTerminated()), this, SLOT(onAllTerminated()));
connect(&_inner, SIGNAL(terminateAll()), this, SLOT(onTerminateAll())); connect(_inner, SIGNAL(terminateAll()), this, SLOT(onTerminateAll()));
connect(App::wnd(), SIGNAL(newAuthorization()), this, SLOT(onNewAuthorization())); connect(App::wnd(), SIGNAL(newAuthorization()), this, SLOT(onNewAuthorization()));
connect(&_shortPollTimer, SIGNAL(timeout()), this, SLOT(onShortPollAuthorizations())); connect(&_shortPollTimer, SIGNAL(timeout()), this, SLOT(onShortPollAuthorizations()));
init(&_inner, st::boxButtonPadding.bottom() + _done.height() + st::boxButtonPadding.top(), st::boxTitleHeight); init(_inner, st::boxButtonPadding.bottom() + _done.height() + st::boxButtonPadding.top(), st::boxTitleHeight);
_inner.resize(width(), st::noContactsHeight); _inner->resize(width(), st::noContactsHeight);
prepare(); prepare();
@ -291,7 +101,7 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
for (int32 i = 0; i < l; ++i) { for (int32 i = 0; i < l; ++i) {
const auto &d(v.at(i).c_authorization()); const auto &d(v.at(i).c_authorization());
SessionData data; Data data;
data.hash = d.vhash.v; data.hash = d.vhash.v;
QString appName, appVer = qs(d.vapp_version), systemVer = qs(d.vsystem_version), deviceModel = qs(d.vdevice_model); 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()) { if (!_done.isHidden()) {
showAll(); showAll();
update(); update();
@ -431,3 +241,193 @@ void SessionsBox::onTerminateAll() {
update(); update();
} }
} }
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();
}

View file

@ -25,20 +25,58 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
class ConfirmBox; class ConfirmBox;
struct SessionData { class SessionsBox : public ScrollableBox, public RPCSender {
uint64 hash;
int32 activeTime;
int32 nameWidth, activeWidth, infoWidth, ipWidth;
QString name, active, info, ip;
};
typedef QList<SessionData> SessionsList;
class SessionsInner : public TWidget, public RPCSender {
Q_OBJECT Q_OBJECT
public: 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<Data>;
void gotAuthorizations(const MTPaccount_Authorizations &result);
bool _loading;
Data _current;
List _list;
class Inner;
ChildWidget<Inner> _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(); void listUpdated();
@ -65,8 +103,8 @@ private:
void terminateAllDone(const MTPBool &res); void terminateAllDone(const MTPBool &res);
bool terminateAllFail(const RPCError &error); bool terminateAllFail(const RPCError &error);
SessionsList *_list; SessionsBox::List *_list;
SessionData *_current; SessionsBox::Data *_current;
typedef QMap<uint64, IconedButton*> TerminateButtons; typedef QMap<uint64, IconedButton*> TerminateButtons;
TerminateButtons _terminateButtons; TerminateButtons _terminateButtons;
@ -76,39 +114,3 @@ private:
ConfirmBox *_terminateBox; 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;
};

View file

@ -32,6 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/buttons/icon_button.h"
#include "history/history_media_types.h" #include "history/history_media_types.h"
#include "boxes/contactsbox.h" #include "boxes/contactsbox.h"
@ -241,9 +242,7 @@ void ShareBox::onScroll() {
_inner->setVisibleTopBottom(scrollTop, scrollTop + scroll->height()); _inner->setVisibleTopBottom(scrollTop, scrollTop + scroll->height());
} }
namespace internal { ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : ScrolledWidget(parent)
ShareInner::ShareInner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : ScrolledWidget(parent)
, _filterCallback(std_::move(filterCallback)) , _filterCallback(std_::move(filterCallback))
, _chatsIndexed(std_::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Add)) { , _chatsIndexed(std_::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Add)) {
_rowsTop = st::shareRowsTop; _rowsTop = st::shareRowsTop;
@ -269,19 +268,19 @@ ShareInner::ShareInner(QWidget *parent, ShareBox::FilterCallback &&filterCallbac
subscribe(FileDownload::ImageLoaded(), [this] { update(); }); subscribe(FileDownload::ImageLoaded(), [this] { update(); });
} }
void ShareInner::setVisibleTopBottom(int visibleTop, int visibleBottom) { void ShareBox::Inner::setVisibleTopBottom(int visibleTop, int visibleBottom) {
loadProfilePhotos(visibleTop); loadProfilePhotos(visibleTop);
} }
void ShareInner::activateSkipRow(int direction) { void ShareBox::Inner::activateSkipRow(int direction) {
activateSkipColumn(direction * _columnCount); activateSkipColumn(direction * _columnCount);
} }
int ShareInner::displayedChatsCount() const { int ShareBox::Inner::displayedChatsCount() const {
return _filter.isEmpty() ? _chatsIndexed->size() : (_filtered.size() + d_byUsernameFiltered.size()); 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 (_active < 0) {
if (direction > 0) { if (direction > 0) {
setActive(0); setActive(0);
@ -299,11 +298,11 @@ void ShareInner::activateSkipColumn(int direction) {
setActive(active); setActive(active);
} }
void ShareInner::activateSkipPage(int pageHeight, int direction) { void ShareBox::Inner::activateSkipPage(int pageHeight, int direction) {
activateSkipRow(direction * (pageHeight / _rowHeight)); 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) { if (update.flags & Notify::PeerUpdate::Flag::NameChanged) {
_chatsIndexed->peerNameChanged(update.peer, update.oldNames, update.oldNameFirstChars); _chatsIndexed->peerNameChanged(update.peer, update.oldNames, update.oldNameFirstChars);
} }
@ -311,7 +310,7 @@ void ShareInner::notifyPeerUpdated(const Notify::PeerUpdate &update) {
updateChat(update.peer); updateChat(update.peer);
} }
void ShareInner::updateChat(PeerData *peer) { void ShareBox::Inner::updateChat(PeerData *peer) {
auto i = _dataMap.find(peer); auto i = _dataMap.find(peer);
if (i != _dataMap.cend()) { if (i != _dataMap.cend()) {
updateChatName(i.value(), peer); 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); chat->name.setText(st::shareNameFont, peer->name, _textNameOptions);
} }
void ShareInner::repaintChatAtIndex(int index) { void ShareBox::Inner::repaintChatAtIndex(int index) {
if (index < 0) return; if (index < 0) return;
auto row = index / _columnCount; auto row = index / _columnCount;
@ -331,7 +330,7 @@ void ShareInner::repaintChatAtIndex(int index) {
update(rtlrect(_rowsLeft + qFloor(column * _rowWidthReal), row * _rowHeight, _rowWidth, _rowHeight, width())); 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; if (index < 0) return nullptr;
auto row = ([this, index]() -> Dialogs::Row* { auto row = ([this, index]() -> Dialogs::Row* {
if (_filter.isEmpty()) return _chatsIndexed->rowAtY(index, 1); if (_filter.isEmpty()) return _chatsIndexed->rowAtY(index, 1);
@ -350,11 +349,11 @@ ShareInner::Chat *ShareInner::getChatAtIndex(int index) {
return nullptr; return nullptr;
} }
void ShareInner::repaintChat(PeerData *peer) { void ShareBox::Inner::repaintChat(PeerData *peer) {
repaintChatAtIndex(chatIndex(peer)); repaintChatAtIndex(chatIndex(peer));
} }
int ShareInner::chatIndex(PeerData *peer) const { int ShareBox::Inner::chatIndex(PeerData *peer) const {
int index = 0; int index = 0;
if (_filter.isEmpty()) { if (_filter.isEmpty()) {
for_const (auto row, _chatsIndexed->all()) { for_const (auto row, _chatsIndexed->all()) {
@ -380,7 +379,7 @@ int ShareInner::chatIndex(PeerData *peer) const {
return -1; return -1;
} }
void ShareInner::loadProfilePhotos(int yFrom) { void ShareBox::Inner::loadProfilePhotos(int yFrom) {
if (yFrom < 0) { if (yFrom < 0) {
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<Chat*>(row->attached); auto data = static_cast<Chat*>(row->attached);
if (!data) { if (!data) {
auto peer = row->history()->peer; auto peer = row->history()->peer;
@ -436,7 +435,7 @@ ShareInner::Chat *ShareInner::getChat(Dialogs::Row *row) {
return data; return data;
} }
void ShareInner::setActive(int active) { void ShareBox::Inner::setActive(int active) {
if (active != _active) { if (active != _active) {
auto changeNameFg = [this](int index, style::color from, style::color to) { auto changeNameFg = [this](int index, style::color from, style::color to) {
if (auto chat = getChatAtIndex(index)) { if (auto chat = getChatAtIndex(index)) {
@ -453,7 +452,7 @@ void ShareInner::setActive(int active) {
emit mustScrollTo(y, y + _rowHeight); 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 x = _rowsLeft + qFloor((index % _columnCount) * _rowWidthReal);
auto y = _rowsTop + (index / _columnCount) * _rowHeight; 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); 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) : peer(peer)
, checkbox(st::sharePhotoCheckbox, std_::move(updateCallback), PaintUserpicCallback(peer)) , checkbox(st::sharePhotoCheckbox, std_::move(updateCallback), PaintUserpicCallback(peer))
, name(st::sharePhotoCheckbox.imageRadius * 2) { , name(st::sharePhotoCheckbox.imageRadius * 2) {
} }
void ShareInner::paintEvent(QPaintEvent *e) { void ShareBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
auto r = e->rect(); 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); setMouseTracking(true);
} }
void ShareInner::leaveEvent(QEvent *e) { void ShareBox::Inner::leaveEvent(QEvent *e) {
setMouseTracking(false); setMouseTracking(false);
} }
void ShareInner::mouseMoveEvent(QMouseEvent *e) { void ShareBox::Inner::mouseMoveEvent(QMouseEvent *e) {
updateUpon(e->pos()); updateUpon(e->pos());
setCursor((_upon >= 0) ? style::cur_pointer : style::cur_default); 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 x = pos.x(), y = pos.y();
auto row = (y - _rowsTop) / _rowHeight; auto row = (y - _rowsTop) / _rowHeight;
auto column = qFloor((x - _rowsLeft) / _rowWidthReal); auto column = qFloor((x - _rowsLeft) / _rowWidthReal);
@ -567,18 +566,18 @@ void ShareInner::updateUpon(const QPoint &pos) {
_upon = upon; _upon = upon;
} }
void ShareInner::mousePressEvent(QMouseEvent *e) { void ShareBox::Inner::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) { if (e->button() == Qt::LeftButton) {
updateUpon(e->pos()); updateUpon(e->pos());
changeCheckState(getChatAtIndex(_upon)); changeCheckState(getChatAtIndex(_upon));
} }
} }
void ShareInner::onSelectActive() { void ShareBox::Inner::onSelectActive() {
changeCheckState(getChatAtIndex(_active > 0 ? _active : 0)); 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); _columnSkip = (width() - _columnCount * st::sharePhotoCheckbox.imageRadius * 2) / float64(_columnCount + 1);
_rowWidthReal = st::sharePhotoCheckbox.imageRadius * 2 + _columnSkip; _rowWidthReal = st::sharePhotoCheckbox.imageRadius * 2 + _columnSkip;
_rowsLeft = qFloor(_columnSkip / 2); _rowsLeft = qFloor(_columnSkip / 2);
@ -586,7 +585,7 @@ void ShareInner::resizeEvent(QResizeEvent *e) {
update(); update();
} }
void ShareInner::changeCheckState(Chat *chat) { void ShareBox::Inner::changeCheckState(Chat *chat) {
if (!chat) return; if (!chat) return;
if (!_filter.isEmpty()) { if (!_filter.isEmpty()) {
@ -611,11 +610,11 @@ void ShareInner::changeCheckState(Chat *chat) {
emit selectedChanged(); emit selectedChanged();
} }
bool ShareInner::hasSelected() const { bool ShareBox::Inner::hasSelected() const {
return _selected.size(); return _selected.size();
} }
void ShareInner::updateFilter(QString filter) { void ShareBox::Inner::updateFilter(QString filter) {
_lastQuery = filter.toLower().trimmed(); _lastQuery = filter.toLower().trimmed();
filter = textSearchKey(filter); filter = textSearchKey(filter);
@ -694,7 +693,7 @@ void ShareInner::updateFilter(QString filter) {
} }
} }
void ShareInner::peopleReceived(const QString &query, const QVector<MTPPeer> &people) { void ShareBox::Inner::peopleReceived(const QString &query, const QVector<MTPPeer> &people) {
_lastQuery = query.toLower().trimmed(); _lastQuery = query.toLower().trimmed();
if (_lastQuery.at(0) == '@') _lastQuery = _lastQuery.mid(1); if (_lastQuery.at(0) == '@') _lastQuery = _lastQuery.mid(1);
int32 already = _byUsernameFiltered.size(); int32 already = _byUsernameFiltered.size();
@ -724,7 +723,7 @@ void ShareInner::peopleReceived(const QString &query, const QVector<MTPPeer> &pe
refresh(); refresh();
} }
void ShareInner::refresh() { void ShareBox::Inner::refresh() {
auto count = displayedChatsCount(); auto count = displayedChatsCount();
if (count) { if (count) {
auto rows = (count / _columnCount) + (count % _columnCount ? 1 : 0); auto rows = (count / _columnCount) + (count % _columnCount ? 1 : 0);
@ -735,13 +734,13 @@ void ShareInner::refresh() {
update(); update();
} }
ShareInner::~ShareInner() { ShareBox::Inner::~Inner() {
for_const (auto chat, _dataMap) { for_const (auto chat, _dataMap) {
delete chat; delete chat;
} }
} }
QVector<PeerData*> ShareInner::selected() const { QVector<PeerData*> ShareBox::Inner::selected() const {
QVector<PeerData*> result; QVector<PeerData*> result;
result.reserve(_dataMap.size()); result.reserve(_dataMap.size());
for_const (auto chat, _dataMap) { for_const (auto chat, _dataMap) {
@ -752,8 +751,6 @@ QVector<PeerData*> ShareInner::selected() const {
return result; return result;
} }
} // namespace internal
QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) { QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) {
auto shareHashData = QByteArray(0x10, Qt::Uninitialized); auto shareHashData = QByteArray(0x10, Qt::Uninitialized);
auto shareHashDataInts = reinterpret_cast<int32*>(shareHashData.data()); auto shareHashDataInts = reinterpret_cast<int32*>(shareHashData.data());

View file

@ -31,14 +31,14 @@ class Row;
class IndexedList; class IndexedList;
} // namespace Dialogs } // namespace Dialogs
namespace internal {
class ShareInner;
} // namespace internal
namespace Notify { namespace Notify {
struct PeerUpdate; struct PeerUpdate;
} // namespace Notify } // namespace Notify
namespace Ui {
class IconButton;
} // namespace Ui
QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId); QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId);
void shareGameScoreByHash(const QString &hash); void shareGameScoreByHash(const QString &hash);
@ -82,9 +82,10 @@ private:
CopyCallback _copyCallback; CopyCallback _copyCallback;
SubmitCallback _submitCallback; SubmitCallback _submitCallback;
ChildWidget<internal::ShareInner> _inner; class Inner;
ChildWidget<Inner> _inner;
ChildWidget<InputField> _filter; ChildWidget<InputField> _filter;
ChildWidget<IconedButton> _filterCancel; ChildWidget<Ui::IconButton> _filterCancel;
ChildWidget<BoxButton> _copy; ChildWidget<BoxButton> _copy;
ChildWidget<BoxButton> _share; ChildWidget<BoxButton> _share;
@ -108,13 +109,12 @@ private:
}; };
namespace internal { // This class is hold in header because it requires Qt preprocessing.
class ShareBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber {
class ShareInner : public ScrolledWidget, public RPCSender, private base::Subscriber {
Q_OBJECT Q_OBJECT
public: public:
ShareInner(QWidget *parent, ShareBox::FilterCallback &&filterCallback); Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback);
QVector<PeerData*> selected() const; QVector<PeerData*> selected() const;
bool hasSelected() const; bool hasSelected() const;
@ -127,7 +127,7 @@ public:
void setVisibleTopBottom(int visibleTop, int visibleBottom) override; void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
void updateFilter(QString filter = QString()); void updateFilter(QString filter = QString());
~ShareInner(); ~Inner();
public slots: public slots:
void onSelectActive(); void onSelectActive();
@ -208,5 +208,3 @@ private:
ByUsernameDatas d_byUsernameFiltered; ByUsernameDatas d_byUsernameFiltered;
}; };
} // namespace internal

File diff suppressed because it is too large Load diff

View file

@ -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> _inner;
ChildWidget<BoxButton> _save = { nullptr };
ChildWidget<BoxButton> _cancel = { nullptr };
OrderedSet<mtpRequestId> _disenableRequests;
mtpRequestId _reorderRequest = 0;
ChildWidget<Ui::PlainShadow> _topShadow = { nullptr };
ChildWidget<ScrollableBoxShadow> _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<StickerSetRow*>;
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<uint64> _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;
};

File diff suppressed because it is too large Load diff

View file

@ -24,15 +24,52 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "core/vector_of_moveable.h" #include "core/vector_of_moveable.h"
class ConfirmBox; class ConfirmBox;
namespace Ui { namespace Ui {
class PlainShadow; class PlainShadow;
} // namespace Ui } // namespace Ui
class StickerSetInner : public ScrolledWidget, public RPCSender, private base::Subscriber { class StickerSetBox : public ScrollableBox, public RPCSender {
Q_OBJECT Q_OBJECT
public: 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> _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; bool loaded() const;
int32 notInstalled() const; int32 notInstalled() const;
@ -43,7 +80,7 @@ public:
void setVisibleTopBottom(int visibleTop, int visibleBottom) override; void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
void install(); void install();
~StickerSetInner(); ~Inner();
protected: protected:
void mousePressEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override;
@ -96,253 +133,3 @@ private:
int _previewShown = -1; 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<internal::StickersInner> _inner;
ChildWidget<BoxButton> _save = { nullptr };
ChildWidget<BoxButton> _cancel = { nullptr };
OrderedSet<mtpRequestId> _disenableRequests;
mtpRequestId _reorderRequest = 0;
ChildWidget<Ui::PlainShadow> _topShadow = { nullptr };
ChildWidget<ScrollableBoxShadow> _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<StickerSetRow*>;
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<uint64> _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

View file

@ -243,7 +243,7 @@ structure::Variable ParsedFile::readVariable(const QString &name) {
structure::Variable result = { composeFullName(name) }; structure::Variable result = { composeFullName(name) };
if (auto value = readValue()) { if (auto value = readValue()) {
result.value = value; result.value = value;
if (value.type().tag != structure::TypeTag::Struct) { if (value.type().tag != structure::TypeTag::Struct || !value.copyOf().empty()) {
assertNextToken(BasicType::Semicolon); assertNextToken(BasicType::Semicolon);
} }
} }

View file

@ -529,14 +529,14 @@ void CoverWidget::onAddMember() {
if (_peerChat->count >= Global::ChatSizeMax() && _peerChat->amCreator()) { if (_peerChat->count >= Global::ChatSizeMax() && _peerChat->amCreator()) {
Ui::showLayer(new ConvertToSupergroupBox(_peerChat)); Ui::showLayer(new ConvertToSupergroupBox(_peerChat));
} else { } else {
Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); Ui::showLayer(new ContactsBox(_peerChat, MembersFilter::Recent));
} }
} else if (_peerChannel && _peerChannel->mgInfo) { } else if (_peerChannel && _peerChannel->mgInfo) {
MembersAlreadyIn already; MembersAlreadyIn already;
for_const (auto user, _peerChannel->mgInfo->lastParticipants) { for_const (auto user, _peerChannel->mgInfo->lastParticipants) {
already.insert(user); already.insert(user);
} }
Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already)); Ui::showLayer(new ContactsBox(_peerChannel, MembersFilter::Recent, already));
} }
} }

View file

@ -700,13 +700,13 @@ int ChannelMembersWidget::resizeGetHeight(int newWidth) {
void ChannelMembersWidget::onAdmins() { void ChannelMembersWidget::onAdmins() {
if (auto channel = peer()->asChannel()) { if (auto channel = peer()->asChannel()) {
Ui::showLayer(new MembersBox(channel, MembersFilterAdmins)); Ui::showLayer(new MembersBox(channel, MembersFilter::Admins));
} }
} }
void ChannelMembersWidget::onMembers() { void ChannelMembersWidget::onMembers() {
if (auto channel = peer()->asChannel()) { if (auto channel = peer()->asChannel()) {
Ui::showLayer(new MembersBox(channel, MembersFilterRecent)); Ui::showLayer(new MembersBox(channel, MembersFilter::Recent));
} }
} }

View file

@ -164,9 +164,9 @@ void SettingsWidget::onNotificationsChange() {
void SettingsWidget::onManageAdmins() { void SettingsWidget::onManageAdmins() {
if (auto chat = peer()->asChat()) { if (auto chat = peer()->asChat()) {
Ui::showLayer(new ContactsBox(chat, MembersFilterAdmins)); Ui::showLayer(new ContactsBox(chat, MembersFilter::Admins));
} else if (auto channel = peer()->asChannel()) { } else if (auto channel = peer()->asChannel()) {
Ui::showLayer(new MembersBox(channel, MembersFilterAdmins)); Ui::showLayer(new MembersBox(channel, MembersFilter::Admins));
} }
} }

View file

@ -29,7 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwidget.h" #include "mainwidget.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "boxes/emojibox.h" #include "boxes/emojibox.h"
#include "boxes/stickersetbox.h" #include "boxes/stickers_box.h"
#include "boxes/downloadpathbox.h" #include "boxes/downloadpathbox.h"
#include "boxes/connectionbox.h" #include "boxes/connectionbox.h"
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"

View file

@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_stickers.h" #include "styles/style_stickers.h"
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
#include "boxes/stickersetbox.h" #include "boxes/stickersetbox.h"
#include "boxes/stickers_box.h"
#include "inline_bots/inline_bot_result.h" #include "inline_bots/inline_bot_result.h"
#include "inline_bots/inline_bot_layout_item.h" #include "inline_bots/inline_bot_layout_item.h"
#include "dialogs/dialogs_layout.h" #include "dialogs/dialogs_layout.h"

View file

@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h" #include "stdafx.h"
#include "stickers.h" #include "stickers.h"
#include "boxes/stickersetbox.h" #include "boxes/stickers_box.h"
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
#include "lang.h" #include "lang.h"
#include "apiwrap.h" #include "apiwrap.h"

View file

@ -24,8 +24,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "lang.h" #include "lang.h"
#include "application.h" #include "application.h"
#include "ui/scrollarea.h" #include "ui/scrollarea.h"
#include "ui/buttons/icon_button.h"
#include "boxes/contactsbox.h" #include "boxes/contactsbox.h"
#include "countries.h" #include "countries.h"
#include "styles/style_boxes.h"
namespace { namespace {
@ -192,7 +194,88 @@ void CountryInput::setText(const QString &newText) {
_text = _st.font->elided(newText, width() - _st.textMrg.left() - _st.textMrg.right()); _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) , _rowHeight(st::countryRowHeight)
, _sel(0) , _sel(0)
, _mouseSel(false) { , _mouseSel(false) {
@ -239,7 +322,7 @@ CountrySelectInner::CountrySelectInner() : TWidget()
updateFilter(); updateFilter();
} }
void CountrySelectInner::paintEvent(QPaintEvent *e) { void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
QRect r(e->rect()); QRect r(e->rect());
p.setClipRect(r); 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); setMouseTracking(true);
} }
void CountrySelectInner::leaveEvent(QEvent *e) { void CountrySelectBox::Inner::leaveEvent(QEvent *e) {
_mouseSel = false; _mouseSel = false;
setMouseTracking(false); setMouseTracking(false);
if (_sel >= 0) { 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; _mouseSel = true;
_lastMousePos = e->globalPos(); _lastMousePos = e->globalPos();
updateSel(); updateSel();
} }
void CountrySelectInner::mousePressEvent(QMouseEvent *e) { void CountrySelectBox::Inner::mousePressEvent(QMouseEvent *e) {
_mouseSel = true; _mouseSel = true;
_lastMousePos = e->globalPos(); _lastMousePos = e->globalPos();
updateSel(); updateSel();
@ -311,7 +394,7 @@ void CountrySelectInner::mousePressEvent(QMouseEvent *e) {
} }
} }
void CountrySelectInner::updateFilter(QString filter) { void CountrySelectBox::Inner::updateFilter(QString filter) {
filter = textSearchKey(filter); filter = textSearchKey(filter);
QStringList f; QStringList f;
@ -366,7 +449,7 @@ void CountrySelectInner::updateFilter(QString filter) {
} }
} }
void CountrySelectInner::selectSkip(int32 dir) { void CountrySelectBox::Inner::selectSkip(int32 dir) {
_mouseSel = false; _mouseSel = false;
int cur = (_sel >= 0) ? _sel : -1; int cur = (_sel >= 0) ? _sel : -1;
@ -384,13 +467,13 @@ void CountrySelectInner::selectSkip(int32 dir) {
update(); update();
} }
void CountrySelectInner::selectSkipPage(int32 h, int32 dir) { void CountrySelectBox::Inner::selectSkipPage(int32 h, int32 dir) {
int32 points = h / _rowHeight; int32 points = h / _rowHeight;
if (!points) return; if (!points) return;
selectSkip(points * dir); selectSkip(points * dir);
} }
void CountrySelectInner::chooseCountry() { void CountrySelectBox::Inner::chooseCountry() {
QString result; QString result;
if (_filter.isEmpty()) { if (_filter.isEmpty()) {
if (_sel >= 0 && _sel < countriesAll.size()) { if (_sel >= 0 && _sel < countriesAll.size()) {
@ -404,11 +487,11 @@ void CountrySelectInner::chooseCountry() {
emit countryChosen(result); emit countryChosen(result);
} }
void CountrySelectInner::refresh() { void CountrySelectBox::Inner::refresh() {
resize(width(), countriesNow->length() ? (countriesNow->length() * _rowHeight + st::countriesSkip) : st::noContactsHeight); resize(width(), countriesNow->length() ? (countriesNow->length() * _rowHeight + st::countriesSkip) : st::noContactsHeight);
} }
void CountrySelectInner::updateSel() { void CountrySelectBox::Inner::updateSel() {
if (!_mouseSel) return; if (!_mouseSel) return;
QPoint p(mapFromGlobal(_lastMousePos)); QPoint p(mapFromGlobal(_lastMousePos));
@ -422,89 +505,8 @@ void CountrySelectInner::updateSel() {
} }
} }
void CountrySelectInner::updateSelectedRow() { void CountrySelectBox::Inner::updateSelectedRow() {
if (_sel >= 0) { if (_sel >= 0) {
update(0, st::countriesSkip + _sel * _rowHeight, width(), _rowHeight); 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();
}

View file

@ -29,6 +29,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
QString findValidCode(QString fullCode); QString findValidCode(QString fullCode);
class CountrySelect; class CountrySelect;
class InputField;
namespace Ui {
class IconButton;
} // namespace Ui
class CountryInput : public QWidget { class CountryInput : public QWidget {
Q_OBJECT Q_OBJECT
@ -63,11 +68,48 @@ private:
}; };
class CountrySelectInner : public TWidget { namespace internal {
class CountrySelectInner;
} // namespace internal
class CountrySelectBox : public ItemListBox {
Q_OBJECT Q_OBJECT
public: 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> _inner;
ChildWidget<InputField> _filter;
ChildWidget<Ui::IconButton> _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()); void updateFilter(QString filter = QString());
@ -104,34 +146,3 @@ private:
QPoint _lastMousePos; 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;
};

View file

@ -21,10 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once #pragma once
#include "ui/widgets/continuous_slider.h" #include "ui/widgets/continuous_slider.h"
#include "styles/style_widgets.h"
namespace style {
struct FilledSlider;
} // namespace style
namespace Ui { namespace Ui {

View file

@ -21,10 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once #pragma once
#include "ui/widgets/continuous_slider.h" #include "ui/widgets/continuous_slider.h"
#include "styles/style_widgets.h"
namespace style {
struct MediaSlider;
} // namespace style
namespace Ui { namespace Ui {

View file

@ -183,6 +183,8 @@
'<(src_loc)/boxes/languagebox.h', '<(src_loc)/boxes/languagebox.h',
'<(src_loc)/boxes/localstoragebox.cpp', '<(src_loc)/boxes/localstoragebox.cpp',
'<(src_loc)/boxes/localstoragebox.h', '<(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.cpp',
'<(src_loc)/boxes/notifications_box.h', '<(src_loc)/boxes/notifications_box.h',
'<(src_loc)/boxes/passcodebox.cpp', '<(src_loc)/boxes/passcodebox.cpp',
@ -199,6 +201,8 @@
'<(src_loc)/boxes/sharebox.h', '<(src_loc)/boxes/sharebox.h',
'<(src_loc)/boxes/stickersetbox.cpp', '<(src_loc)/boxes/stickersetbox.cpp',
'<(src_loc)/boxes/stickersetbox.h', '<(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.cpp',
'<(src_loc)/boxes/usernamebox.h', '<(src_loc)/boxes/usernamebox.h',
'<(src_loc)/core/basic_types.h', '<(src_loc)/core/basic_types.h',