2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-31 06:26:18 +00:00

Select exception users in EditPrivacyBox.

This commit is contained in:
John Preston
2017-03-17 15:05:50 +03:00
parent 85fd117675
commit 61c5b45d7a
17 changed files with 622 additions and 227 deletions

View File

@@ -1012,7 +1012,7 @@ void ContactsBox::Inner::paintDisabledCheckUserpic(Painter &p, PeerData *peer, i
auto iconBorderPen = st::contactsPhotoCheckbox.check.border->p;
iconBorderPen.setWidth(st::contactsPhotoCheckbox.selectWidth);
peer->paintUserpicLeft(p, userpicLeft, userpicTop, width(), userpicRadius * 2);
peer->paintUserpicLeft(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
{
PainterHighQualityEnabler hq(p);
@@ -1446,7 +1446,7 @@ void ContactsBox::Inner::changeCheckState(Dialogs::Row *row) {
}
void ContactsBox::Inner::changeCheckState(ContactData *data, PeerData *peer) {
t_assert(usingMultiSelect());
Expects(usingMultiSelect());
if (isRowDisabled(peer, data)) {
} else if (data->checkbox->checked()) {

View File

@@ -25,8 +25,69 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/effects/widget_slide_wrap.h"
#include "boxes/peer_list_box.h"
#include "lang.h"
namespace {
class PrivacyExceptionsBoxController : public ChatsListBoxController {
public:
PrivacyExceptionsBoxController(const QString &title, const QVector<UserData*> &selected, base::lambda_once<void(QVector<UserData*> &&result)> saveCallback);
void rowClicked(PeerListBox::Row *row) override;
protected:
void prepareViewHook() override;
std::unique_ptr<Row> createRow(History *history) override;
private:
QString _title;
QVector<UserData*> _selected;
base::lambda_once<void(QVector<UserData*> &&result)> _saveCallback;
};
PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(const QString &title, const QVector<UserData*> &selected, base::lambda_once<void(QVector<UserData*> &&result)> saveCallback)
: _title(title)
, _selected(selected)
, _saveCallback(std::move(saveCallback)) {
}
void PrivacyExceptionsBoxController::prepareViewHook() {
view()->setTitle(_title);
view()->addButton(lang(lng_settings_save), [this] {
auto peers = view()->collectSelectedRows();
auto users = QVector<UserData*>();
if (!peers.empty()) {
users.reserve(peers.size());
for_const (auto peer, peers) {
auto user = peer->asUser();
t_assert(user != nullptr);
users.push_back(user);
}
}
_saveCallback(std::move(users));
view()->closeBox();
});
view()->addButton(lang(lng_cancel), [this] { view()->closeBox(); });
view()->addSelectedRows(_selected);
}
void PrivacyExceptionsBoxController::rowClicked(PeerListBox::Row *row) {
view()->setRowChecked(row, !row->checked());
view()->updateRow(row);
}
std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxController::createRow(History *history) {
if (auto user = history->peer->asUser()) {
if (!user->isSelf()) {
return std::make_unique<Row>(history);
}
}
return std::unique_ptr<Row>();
}
} // namespace
class EditPrivacyBox::OptionWidget : public TWidget {
public:
OptionWidget(QWidget *parent, int value, bool selected, const QString &text, const QString &description);
@@ -149,12 +210,34 @@ int EditPrivacyBox::countDefaultHeight(int newWidth) {
return height;
}
void EditPrivacyBox::editAlwaysUsers() {
// not implemented
void EditPrivacyBox::editExceptionUsers(Exception exception) {
auto controller = std::make_unique<PrivacyExceptionsBoxController>(_controller->exceptionBoxTitle(exception), exceptionUsers(exception), base::lambda_guarded(this, [this, exception](QVector<UserData*> &&users) {
exceptionUsers(exception) = std::move(users);
exceptionLink(exception)->entity()->setText(exceptionLinkText(exception));
auto removeFrom = ([exception] {
switch (exception) {
case Exception::Always: return Exception::Never;
case Exception::Never: return Exception::Always;
}
Unexpected("Invalid exception value.");
})();
auto &removeFromUsers = exceptionUsers(removeFrom);
auto removedSome = false;
for (auto user : exceptionUsers(exception)) {
if (removeFromUsers.contains(user)) {
removeFromUsers.erase(std::remove(removeFromUsers.begin(), removeFromUsers.end(), user), removeFromUsers.end());
removedSome = true;
}
}
if (removedSome) {
exceptionLink(removeFrom)->entity()->setText(exceptionLinkText(removeFrom));
}
}));
Ui::show(Box<PeerListBox>(std::move(controller)), KeepOtherLayers);
}
void EditPrivacyBox::editNeverUsers() {
// not implemented
QString EditPrivacyBox::exceptionLinkText(Exception exception) {
return _controller->exceptionLinkText(exception, exceptionUsers(exception).size());
}
QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
@@ -169,10 +252,10 @@ QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
auto result = QVector<MTPInputPrivacyRule>();
result.reserve(3);
if (showAlwaysLink() && !_alwaysUsers.empty()) {
if (showExceptionLink(Exception::Always) && !_alwaysUsers.empty()) {
result.push_back(MTP_inputPrivacyValueAllowUsers(MTP_vector<MTPInputUser>(collectInputUsers(_alwaysUsers))));
}
if (showNeverLink() && !_neverUsers.empty()) {
if (showExceptionLink(Exception::Never) && !_neverUsers.empty()) {
result.push_back(MTP_inputPrivacyValueDisallowUsers(MTP_vector<MTPInputUser>(collectInputUsers(_neverUsers))));
}
switch (_option) {
@@ -188,12 +271,28 @@ style::margins EditPrivacyBox::exceptionLinkMargins() const {
return st::editPrivacyLinkMargin;
}
bool EditPrivacyBox::showAlwaysLink() const {
return (_option == Option::Contacts) || (_option == Option::Nobody);
QVector<UserData*> &EditPrivacyBox::exceptionUsers(Exception exception) {
switch (exception) {
case Exception::Always: return _alwaysUsers;
case Exception::Never: return _neverUsers;
}
Unexpected("Invalid exception value.");
}
bool EditPrivacyBox::showNeverLink() const {
return (_option == Option::Everyone) || (_option == Option::Contacts);
object_ptr<Ui::WidgetSlideWrap<Ui::LinkButton>> &EditPrivacyBox::exceptionLink(Exception exception) {
switch (exception) {
case Exception::Always: return _alwaysLink;
case Exception::Never: return _neverLink;
}
Unexpected("Invalid exception value.");
}
bool EditPrivacyBox::showExceptionLink(Exception exception) const {
switch (exception) {
case Exception::Always: return (_option == Option::Contacts) || (_option == Option::Nobody);
case Exception::Never: return (_option == Option::Everyone) || (_option == Option::Contacts);
}
Unexpected("Invalid exception value.");
}
void EditPrivacyBox::createOption(Option option, object_ptr<OptionWidget> &widget, const QString &label) {
@@ -205,8 +304,8 @@ void EditPrivacyBox::createOption(Option option, object_ptr<OptionWidget> &widge
widget->setChangedCallback([this, option, widget = widget.data()] {
if (widget->checked()) {
_option = option;
_alwaysLink->toggleAnimated(showAlwaysLink());
_neverLink->toggleAnimated(showNeverLink());
_alwaysLink->toggleAnimated(showExceptionLink(Exception::Always));
_neverLink->toggleAnimated(showExceptionLink(Exception::Never));
}
});
}
@@ -221,22 +320,23 @@ void EditPrivacyBox::createWidgets() {
_description.create(this, _controller->description(), Ui::FlatLabel::InitType::Simple, st::editPrivacyLabel);
_exceptionsTitle.create(this, lang(lng_edit_privacy_exceptions), Ui::FlatLabel::InitType::Simple, st::editPrivacyTitle);
auto linkResizedCallback = [this] {
resizeGetHeight(width());
auto createExceptionLink = [this](Exception exception) {
exceptionLink(exception).create(this, object_ptr<Ui::LinkButton>(this, exceptionLinkText(exception)), exceptionLinkMargins(), [this] {
resizeGetHeight(width());
});
exceptionLink(exception)->entity()->setClickedCallback([this, exception] { editExceptionUsers(exception); });
};
_alwaysLink.create(this, object_ptr<Ui::LinkButton>(this, _controller->alwaysLinkText(_alwaysUsers.size())), exceptionLinkMargins(), linkResizedCallback);
_alwaysLink->entity()->setClickedCallback([this] { editAlwaysUsers(); });
_neverLink.create(this, object_ptr<Ui::LinkButton>(this, _controller->neverLinkText(_neverUsers.size())), exceptionLinkMargins(), linkResizedCallback);
_neverLink->entity()->setClickedCallback([this] { editNeverUsers(); });
createExceptionLink(Exception::Always);
createExceptionLink(Exception::Never);
_exceptionsDescription.create(this, _controller->exceptionsDescription(), Ui::FlatLabel::InitType::Simple, st::editPrivacyLabel);
addButton(lang(lng_settings_save), [this] {
_controller->save(collectResult());
});
clearButtons();
addButton(lang(lng_settings_save), [this] { _controller->save(collectResult()); });
addButton(lang(lng_cancel), [this] { closeBox(); });
showChildren();
_alwaysLink->toggleFast(showAlwaysLink());
_neverLink->toggleFast(showNeverLink());
_alwaysLink->toggleFast(showExceptionLink(Exception::Always));
_neverLink->toggleFast(showExceptionLink(Exception::Never));
setDimensions(st::boxWideWidth, resizeGetHeight(st::boxWideWidth));
}

View File

@@ -32,10 +32,14 @@ class WidgetSlideWrap;
class EditPrivacyBox : public BoxContent {
public:
enum class Option {
Everyone = 0,
Everyone,
Contacts,
Nobody,
};
enum class Exception {
Always,
Never,
};
class Controller {
public:
@@ -47,8 +51,8 @@ public:
return QString();
}
virtual QString description() = 0;
virtual QString alwaysLinkText(int count) = 0;
virtual QString neverLinkText(int count) = 0;
virtual QString exceptionLinkText(Exception exception, int count) = 0;
virtual QString exceptionBoxTitle(Exception exception) = 0;
virtual QString exceptionsDescription() = 0;
virtual ~Controller() = default;
@@ -81,17 +85,20 @@ private:
class OptionWidget;
style::margins exceptionLinkMargins() const;
bool showAlwaysLink() const;
bool showNeverLink() const;
bool showExceptionLink(Exception exception) const;
void createWidgets();
void createOption(Option option, object_ptr<OptionWidget> &widget, const QString &label);
QVector<MTPInputPrivacyRule> collectResult();
void loadDone(const MTPaccount_PrivacyRules &result);
int countDefaultHeight(int newWidth);
void editAlwaysUsers();
void editNeverUsers();
void editExceptionUsers(Exception exception);
QString exceptionLinkText(Exception exception);
QVector<UserData*> &exceptionUsers(Exception exception);
object_ptr<Ui::WidgetSlideWrap<Ui::LinkButton>> &exceptionLink(Exception exception);
std::unique_ptr<Controller> _controller;
Option _option = Option::Everyone;
object_ptr<Ui::FlatLabel> _loading;
object_ptr<OptionWidget> _everyone = { nullptr };
@@ -105,7 +112,6 @@ private:
mtpRequestId _loadRequestId = 0;
Option _option = Option::Everyone;
QVector<UserData*> _alwaysUsers;
QVector<UserData*> _neverUsers;

View File

@@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_dialogs.h"
#include "ui/widgets/scroll_area.h"
#include "ui/effects/ripple_animation.h"
#include "dialogs/dialogs_indexed_list.h"
#include "observer_peer.h"
#include "auth_session.h"
#include "mainwidget.h"
@@ -32,6 +33,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/labels.h"
#include "ui/effects/widget_slide_wrap.h"
#include "lang.h"
#include "ui/effects/round_checkbox.h"
#include "boxes/contactsbox.h"
#include "window/themes/window_theme.h"
PeerListBox::PeerListBox(QWidget*, std::unique_ptr<Controller> controller)
: _controller(std::move(controller)) {
@@ -132,6 +136,17 @@ void PeerListBox::removeRow(Row *row) {
_inner->removeRow(row);
}
void PeerListBox::setRowChecked(Row *row, bool checked) {
auto peer = row->peer();
if (checked) {
addSelectItem(peer, Row::SetStyle::Animated);
_inner->changeCheckState(row, checked, Row::SetStyle::Animated);
} else {
// The itemRemovedCallback will call changeCheckState() here.
_select->entity()->removeItem(peer->id);
}
}
int PeerListBox::fullRowsCount() const {
return _inner->fullRowsCount();
}
@@ -158,7 +173,15 @@ void PeerListBox::setSearchMode(SearchMode mode) {
_select = createMultiSelect();
_select->entity()->setSubmittedCallback([this](bool chtrlShiftEnter) { _inner->submitted(); });
_select->entity()->setQueryChangedCallback([this](const QString &query) { searchQueryChanged(query); });
_select->resizeToWidth(width());
_select->entity()->setItemRemovedCallback([this](uint64 itemId) {
if (auto peer = App::peerLoaded(itemId)) {
if (auto row = findRow(peer)) {
_inner->changeCheckState(row, false, Row::SetStyle::Animated);
update();
}
}
});
_select->resizeToWidth(st::boxWideWidth);
_select->moveToLeft(0, 0);
}
if (_select) {
@@ -190,11 +213,43 @@ void PeerListBox::setSearchLoading(object_ptr<Ui::FlatLabel> searchLoading) {
_inner->setSearchLoading(std::move(searchLoading));
}
QVector<PeerData*> PeerListBox::collectSelectedRows() const {
Expects(_select != nullptr);
auto result = QVector<PeerData*>();
auto items = _select->entity()->getItems();
if (!items.empty()) {
result.reserve(items.size());
for_const (auto itemId, items) {
result.push_back(App::peer(itemId));
}
}
return result;
}
void PeerListBox::addSelectItem(PeerData *peer, Row::SetStyle style) {
Expects(_select != nullptr);
if (style == Row::SetStyle::Fast) {
_select->entity()->addItemInBunch(peer->id, peer->shortName(), st::activeButtonBg, PaintUserpicCallback(peer));
} else {
_select->entity()->addItem(peer->id, peer->shortName(), st::activeButtonBg, PaintUserpicCallback(peer), Ui::MultiSelect::AddItemWay::Default);
}
}
void PeerListBox::finishSelectItemsBunch() {
Expects(_select != nullptr);
_select->entity()->finishItemsBunch();
}
bool PeerListBox::isRowSelected(PeerData *peer) const {
Expects(_select != nullptr);
return _select->entity()->hasItem(peer->id);
}
PeerListBox::Row::Row(PeerData *peer) : _peer(peer) {
}
void PeerListBox::Row::setDisabled(bool disabled) {
_disabled = disabled;
bool PeerListBox::Row::checked() const {
return _checkbox && _checkbox->checked();
}
void PeerListBox::Row::setActionLink(const QString &action) {
@@ -253,6 +308,12 @@ int PeerListBox::Row::actionWidth() const {
PeerListBox::Row::~Row() = default;
void PeerListBox::Row::invalidatePixmapsCache() {
if (_checkbox) {
_checkbox->invalidateCache();
}
}
template <typename UpdateCallback>
void PeerListBox::Row::addRipple(QSize size, QPoint point, UpdateCallback updateCallback) {
if (!_ripple) {
@@ -268,7 +329,7 @@ void PeerListBox::Row::stopLastRipple() {
}
}
void PeerListBox::Row::paintRipple(Painter &p, int x, int y, int outerWidth, TimeMs ms) {
void PeerListBox::Row::paintRipple(Painter &p, TimeMs ms, int x, int y, int outerWidth) {
if (_ripple) {
_ripple->paint(p, x, y, outerWidth, ms);
if (_ripple->empty()) {
@@ -277,6 +338,57 @@ void PeerListBox::Row::paintRipple(Painter &p, int x, int y, int outerWidth, Tim
}
}
void PeerListBox::Row::paintUserpic(Painter &p, TimeMs ms, int x, int y, int outerWidth) {
if (_checkbox) {
if (disabled() && checked()) {
paintDisabledCheckUserpic(p, x, y, outerWidth);
} else {
_checkbox->paint(p, ms, x, y, outerWidth);
}
} else {
peer()->paintUserpicLeft(p, x, y, outerWidth, st::contactsPhotoSize);
}
}
// Emulates Ui::RoundImageCheckbox::paint() in a checked state.
void PeerListBox::Row::paintDisabledCheckUserpic(Painter &p, int x, int y, int outerWidth) const {
auto userpicRadius = st::contactsPhotoCheckbox.imageSmallRadius;
auto userpicShift = st::contactsPhotoCheckbox.imageRadius - userpicRadius;
auto userpicDiameter = st::contactsPhotoCheckbox.imageRadius * 2;
auto userpicLeft = x + userpicShift;
auto userpicTop = y + userpicShift;
auto userpicEllipse = rtlrect(x, y, userpicDiameter, userpicDiameter, outerWidth);
auto userpicBorderPen = st::contactsPhotoDisabledCheckFg->p;
userpicBorderPen.setWidth(st::contactsPhotoCheckbox.selectWidth);
auto iconDiameter = st::contactsPhotoCheckbox.check.size;
auto iconLeft = x + userpicDiameter + st::contactsPhotoCheckbox.selectWidth - iconDiameter;
auto iconTop = y + userpicDiameter + st::contactsPhotoCheckbox.selectWidth - iconDiameter;
auto iconEllipse = rtlrect(iconLeft, iconTop, iconDiameter, iconDiameter, outerWidth);
auto iconBorderPen = st::contactsPhotoCheckbox.check.border->p;
iconBorderPen.setWidth(st::contactsPhotoCheckbox.selectWidth);
peer()->paintUserpicLeft(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
{
PainterHighQualityEnabler hq(p);
p.setPen(userpicBorderPen);
p.setBrush(Qt::NoBrush);
p.drawEllipse(userpicEllipse);
p.setPen(iconBorderPen);
p.setBrush(st::contactsPhotoDisabledCheckFg);
p.drawEllipse(iconEllipse);
}
st::contactsPhotoCheckbox.check.check.paint(p, iconEllipse.topLeft(), outerWidth);
}
float64 PeerListBox::Row::checkedRatio() {
return _checkbox ? _checkbox->checkedAnimationRatio() : 0.;
}
void PeerListBox::Row::lazyInitialize() {
if (_initialized) {
return;
@@ -287,6 +399,17 @@ void PeerListBox::Row::lazyInitialize() {
refreshStatus();
}
void PeerListBox::Row::createCheckbox(base::lambda<void()> updateCallback) {
_checkbox = std::make_unique<Ui::RoundImageCheckbox>(st::contactsPhotoCheckbox, std::move(updateCallback), PaintUserpicCallback(_peer));
}
void PeerListBox::Row::setCheckedInternal(bool checked, SetStyle style) {
Expects(_checkbox != nullptr);
using CheckboxStyle = Ui::RoundCheckbox::SetStyle;
auto speed = (style == SetStyle::Animated) ? CheckboxStyle::Animated : CheckboxStyle::Fast;
_checkbox->setChecked(checked, speed);
}
PeerListBox::Inner::Inner(QWidget *parent, Controller *controller) : TWidget(parent)
, _controller(controller)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) {
@@ -294,6 +417,12 @@ PeerListBox::Inner::Inner(QWidget *parent, Controller *controller) : TWidget(par
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*)));
subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) {
if (update.paletteChanged()) {
invalidatePixmapsCache();
}
});
}
void PeerListBox::Inner::appendRow(std::unique_ptr<Row> row) {
@@ -305,7 +434,7 @@ void PeerListBox::Inner::appendRow(std::unique_ptr<Row> row) {
}
void PeerListBox::Inner::appendGlobalSearchRow(std::unique_ptr<Row> row) {
t_assert(showingSearch());
Expects(showingSearch());
if (_rowsByPeer.find(row->peer()) == _rowsByPeer.cend()) {
row->setAbsoluteIndex(_globalSearchRows.size());
row->setIsGlobalSearchResult(true);
@@ -315,11 +444,31 @@ void PeerListBox::Inner::appendGlobalSearchRow(std::unique_ptr<Row> row) {
}
}
void PeerListBox::Inner::changeCheckState(Row *row, bool checked, Row::SetStyle style) {
row->setChecked(checked, style, [this, row] {
updateRow(row);
});
}
void PeerListBox::Inner::addRowEntry(Row *row) {
_rowsByPeer.emplace(row->peer(), row);
if (addingToSearchIndex()) {
addToSearchIndex(row);
}
if (_searchMode != SearchMode::None) {
if (_controller->view()->isRowSelected(row->peer())) {
changeCheckState(row, true, Row::SetStyle::Fast);
}
}
}
void PeerListBox::Inner::invalidatePixmapsCache() {
for_const (auto &row, _rows) {
row->invalidatePixmapsCache();
}
for_const (auto &row, _globalSearchRows) {
row->invalidatePixmapsCache();
}
}
bool PeerListBox::Inner::addingToSearchIndex() const {
@@ -554,11 +703,10 @@ void PeerListBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
setPressed(Selected());
if (e->button() == Qt::LeftButton && pressed == _selected) {
if (auto row = getRow(pressed.index)) {
auto peer = row->peer();
if (pressed.action) {
_controller->rowActionClicked(peer);
_controller->rowActionClicked(row);
} else {
_controller->rowClicked(peer);
_controller->rowClicked(row);
}
}
}
@@ -583,8 +731,8 @@ void PeerListBox::Inner::paintRow(Painter &p, TimeMs ms, RowIndex index) {
auto actionSelected = (selected && active.action);
p.fillRect(0, 0, width(), _rowHeight, selected ? st::contactsBgOver : st::contactsBg);
row->paintRipple(p, 0, 0, width(), ms);
peer->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize);
row->paintRipple(p, ms, 0, 0, width());
row->paintUserpic(p, ms, st::contactsPadding.left(), st::contactsPadding.top(), width());
p.setPen(st::contactsNameFg);
@@ -597,6 +745,7 @@ void PeerListBox::Inner::paintRow(Painter &p, TimeMs ms, RowIndex index) {
namew -= icon->width();
icon->paint(p, namex + qMin(name.maxWidth(), namew), st::contactsPadding.top() + st::contactsNameTop, width());
}
p.setPen(anim::pen(st::contactsNameFg, st::contactsNameCheckedFg, row->checkedRatio()));
name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width());
if (actionWidth) {
@@ -885,7 +1034,7 @@ bool PeerListBox::Inner::globalSearchLoading() const {
void PeerListBox::Inner::submitted() {
if (auto row = getRow(_selected.index)) {
_controller->rowClicked(row->peer());
_controller->rowClicked(row);
}
}
@@ -954,17 +1103,19 @@ void PeerListBox::Inner::updateRow(Row *row, RowIndex hint) {
}
void PeerListBox::Inner::updateRow(RowIndex index) {
if (index.value >= 0) {
if (getRow(index)->disabled()) {
if (index == _selected.index) {
setSelected(Selected());
}
if (index == _pressed.index) {
setPressed(Selected());
}
}
update(0, getRowTop(index), width(), _rowHeight);
if (index.value < 0) {
return;
}
auto row = getRow(index);
if (row->disabled()) {
if (index == _selected.index) {
setSelected(Selected());
}
if (index == _pressed.index) {
setPressed(Selected());
}
}
update(0, getRowTop(index), width(), _rowHeight);
}
template <typename Callback>
@@ -1037,3 +1188,79 @@ void PeerListBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names
updateRow(row);
}
}
void ChatsListBoxController::prepare() {
view()->setSearchNoResultsText(lang(lng_blocked_list_not_found));
view()->setSearchMode(PeerListBox::SearchMode::Global);
prepareViewHook();
rebuildRows();
auto &sessionData = AuthSession::Current().data();
subscribe(sessionData.contactsLoaded(), [this](bool loaded) {
rebuildRows();
});
subscribe(sessionData.moreChatsLoaded(), [this] {
rebuildRows();
});
subscribe(sessionData.allChatsLoaded(), [this](bool loaded) {
checkForEmptyRows();
});
}
void ChatsListBoxController::rebuildRows() {
auto ms = getms();
auto wasEmpty = !view()->fullRowsCount();
auto appendList = [this](auto chats) {
auto count = 0;
for_const (auto row, chats->all()) {
auto history = row->history();
if (history->peer->isUser()) {
if (appendRow(history)) {
++count;
}
}
}
return count;
};
auto added = appendList(App::main()->dialogsList());
added += appendList(App::main()->contactsNoDialogsList());
if (!wasEmpty && added > 0) {
view()->reorderRows([](auto &&begin, auto &&end) {
// Place dialogs list before contactsNoDialogs list.
std::stable_partition(begin, end, [](auto &row) {
auto history = static_cast<Row&>(*row).history();
return history->inChatList(Dialogs::Mode::All);
});
});
}
checkForEmptyRows();
view()->refreshRows();
}
void ChatsListBoxController::checkForEmptyRows() {
if (view()->fullRowsCount()) {
view()->setAboutText(QString());
} else {
auto &sessionData = AuthSession::Current().data();
auto loaded = sessionData.contactsLoaded().value() && sessionData.allChatsLoaded().value();
view()->setAboutText(lang(loaded ? lng_contacts_not_found : lng_contacts_loading));
}
}
std::unique_ptr<PeerListBox::Row> ChatsListBoxController::createGlobalRow(PeerData *peer) {
return createRow(App::history(peer));
}
bool ChatsListBoxController::appendRow(History *history) {
if (auto row = view()->findRow(history->peer)) {
updateRowHook(static_cast<Row*>(row));
return false;
}
if (auto row = createRow(history)) {
view()->appendRow(std::move(row));
return true;
}
return false;
}

View File

@@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Ui {
class RippleAnimation;
class RoundImageCheckbox;
class MultiSelect;
template <typename Widget>
class WidgetSlideWrap;
@@ -40,7 +41,15 @@ public:
public:
Row(PeerData *peer);
void setDisabled(bool disabled);
void setDisabled(bool disabled) {
_disabled = disabled;
}
// Checked state is controlled by the box with multiselect,
// not by the row itself, so there is no setChecked() method.
// We can query the checked state from row, but before it is
// added to the box it is always false.
bool checked() const;
void setActionLink(const QString &action);
PeerData *peer() const {
@@ -54,6 +63,7 @@ public:
private:
// Inner interface.
friend class PeerListBox;
friend class Inner;
void refreshName();
@@ -90,10 +100,25 @@ public:
_isGlobalSearchResult = isGlobalSearchResult;
}
enum class SetStyle {
Animated,
Fast,
};
template <typename UpdateCallback>
void setChecked(bool checked, SetStyle style, UpdateCallback callback) {
if (checked && !_checkbox) {
createCheckbox(std::move(callback));
}
setCheckedInternal(checked, style);
}
void invalidatePixmapsCache();
template <typename UpdateCallback>
void addRipple(QSize size, QPoint point, UpdateCallback updateCallback);
void stopLastRipple();
void paintRipple(Painter &p, int x, int y, int outerWidth, TimeMs ms);
void paintRipple(Painter &p, TimeMs ms, int x, int y, int outerWidth);
void paintUserpic(Painter &p, TimeMs ms, int x, int y, int outerWidth);
float64 checkedRatio();
void setNameFirstChars(const OrderedSet<QChar> &nameFirstChars) {
_nameFirstChars = nameFirstChars;
@@ -105,9 +130,14 @@ public:
void lazyInitialize();
private:
void createCheckbox(base::lambda<void()> updateCallback);
void setCheckedInternal(bool checked, SetStyle style);
void paintDisabledCheckUserpic(Painter &p, int x, int y, int outerWidth) const;
PeerData *_peer = nullptr;
bool _initialized = false;
std::unique_ptr<Ui::RippleAnimation> _ripple;
std::unique_ptr<Ui::RoundImageCheckbox> _checkbox;
Text _name;
QString _status;
StatusType _statusType = StatusType::Online;
@@ -123,8 +153,8 @@ public:
class Controller {
public:
virtual void prepare() = 0;
virtual void rowClicked(PeerData *peer) = 0;
virtual void rowActionClicked(PeerData *peer) {
virtual void rowClicked(Row *row) = 0;
virtual void rowActionClicked(Row *row) {
}
virtual void preloadRows() {
}
@@ -148,6 +178,7 @@ public:
PeerListBox *_view = nullptr;
friend class PeerListBox;
friend class Inner;
};
PeerListBox(QWidget*, std::unique_ptr<Controller> controller);
@@ -158,6 +189,7 @@ public:
Row *findRow(PeerData *peer);
void updateRow(Row *row);
void removeRow(Row *row);
void setRowChecked(Row *row, bool checked);
int fullRowsCount() const;
void setAboutText(const QString &aboutText);
void setAbout(object_ptr<Ui::FlatLabel> about);
@@ -173,10 +205,22 @@ public:
void setSearchLoadingText(const QString &searchLoadingText);
void setSearchLoading(object_ptr<Ui::FlatLabel> searchLoading);
template <typename PeerDataRange>
void addSelectedRows(PeerDataRange &&range) {
Expects(_select != nullptr);
for (auto peer : range) {
addSelectItem(peer, Row::SetStyle::Fast);
}
finishSelectItemsBunch();
}
QVector<PeerData*> collectSelectedRows() const;
// callback takes two iterators, like [](auto &begin, auto &end).
template <typename ReorderCallback>
void reorderRows(ReorderCallback &&callback);
bool isRowSelected(PeerData *peer) const;
protected:
void prepare() override;
void setInnerFocus() override;
@@ -185,6 +229,8 @@ protected:
void resizeEvent(QResizeEvent *e) override;
private:
void addSelectItem(PeerData *peer, Row::SetStyle style);
void finishSelectItemsBunch();
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> createMultiSelect();
int getTopScrollSkip() const;
void updateScrollSkips();
@@ -230,6 +276,8 @@ public:
void setSearchNoResults(object_ptr<Ui::FlatLabel> searchNoResults);
void setSearchLoading(object_ptr<Ui::FlatLabel> searchLoading);
void changeCheckState(Row *row, bool checked, Row::SetStyle style);
template <typename ReorderCallback>
void reorderRows(ReorderCallback &&callback) {
callback(_rows.begin(), _rows.end());
@@ -258,6 +306,8 @@ private:
void refreshIndices();
void appendGlobalSearchRow(std::unique_ptr<Row> row);
void invalidatePixmapsCache();
struct RowIndex {
RowIndex() {
}
@@ -367,3 +417,33 @@ template <typename ReorderCallback>
inline void PeerListBox::reorderRows(ReorderCallback &&callback) {
_inner->reorderRows(std::forward<ReorderCallback>(callback));
}
class ChatsListBoxController : public PeerListBox::Controller, protected base::Subscriber {
public:
void prepare() override final;
std::unique_ptr<PeerListBox::Row> createGlobalRow(PeerData *peer) override final;
protected:
class Row : public PeerListBox::Row {
public:
Row(History *history) : PeerListBox::Row(history->peer), _history(history) {
}
History *history() const {
return _history;
}
private:
History *_history = nullptr;
};
virtual std::unique_ptr<Row> createRow(History *history) = 0;
virtual void prepareViewHook() = 0;
virtual void updateRowHook(Row *row) {
}
private:
void rebuildRows();
void checkForEmptyRows();
bool appendRow(History *history);
};