Use PeerListBox content in info profile.

This commit is contained in:
John Preston 2017-09-26 20:57:01 +03:00
parent 1a0e524b49
commit 292e57ffc7
27 changed files with 841 additions and 323 deletions

View File

@ -333,6 +333,29 @@ contactsMarginBottom: 4px;
membersMarginTop: 10px;
membersMarginBottom: 10px;
peerListBox: PeerList(defaultPeerList) {
padding: margins(
0px,
membersMarginTop,
0px,
membersMarginBottom);
item: PeerListItem(defaultPeerListItem) {
height: 56px;
photoSize: contactsPhotoSize;
photoPosition: point(16px, 7px);
namePosition: point(74px, 9px);
statusPosition: point(74px, 30px);
button: OutlineButton(defaultPeerListButton) {
textBg: contactsBg;
textBgOver: contactsBgOver;
ripple: contactsRipple;
}
statusFg: contactsStatusFg;
statusFgOver: contactsStatusFgOver;
statusFgActive: contactsStatusFgOnline;
}
}
localStorageBoxSkip: 10px;
shareRowsTop: 12px;

View File

@ -22,10 +22,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
#include "styles/style_widgets.h"
#include "auth_session.h"
#include "mainwidget.h"
#include "ui/widgets/multi_select.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/scroll_area.h"
#include "ui/effects/round_checkbox.h"
#include "ui/effects/ripple_animation.h"
#include "ui/wrap/slide_wrap.h"
@ -34,7 +36,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/file_download.h"
#include "window/themes/window_theme.h"
PeerListBox::PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(not_null<PeerListBox*>)> init)
PeerListBox::PeerListBox(
QWidget*,
std::unique_ptr<PeerListController> controller,
base::lambda<void(not_null<PeerListBox*>)> init)
: _controller(std::move(controller))
, _init(std::move(init)) {
Expects(_controller != nullptr);
@ -49,12 +54,12 @@ void PeerListBox::createMultiSelect() {
| rpl::start(
[this](int) { updateScrollSkips(); },
lifetime());
_select->entity()->setSubmittedCallback([this](bool chtrlShiftEnter) { _inner->submitted(); });
_select->entity()->setSubmittedCallback([this](bool chtrlShiftEnter) { content()->submitted(); });
_select->entity()->setQueryChangedCallback([this](const QString &query) { searchQueryChanged(query); });
_select->entity()->setItemRemovedCallback([this](uint64 itemId) {
if (auto peer = App::peerLoaded(itemId)) {
if (auto row = peerListFindRow(peer->id)) {
_inner->changeCheckState(row, false, PeerListRow::SetStyle::Animated);
content()->changeCheckState(row, false, PeerListRow::SetStyle::Animated);
update();
}
_controller->itemDeselectedHook(peer);
@ -82,7 +87,13 @@ void PeerListBox::updateScrollSkips() {
}
void PeerListBox::prepare() {
_inner = setInnerWidget(object_ptr<Inner>(this, _controller.get()), st::boxLayerScroll);
setContent(setInnerWidget(
object_ptr<PeerListContent>(
this,
_controller.get(),
st::peerListBox),
st::boxLayerScroll));
content()->resizeToWidth(st::boxWideWidth);
_controller->setDelegate(this);
@ -93,7 +104,10 @@ void PeerListBox::prepare() {
onScrollToY(0);
}
connect(_inner, SIGNAL(mustScrollTo(int, int)), this, SLOT(onScrollToY(int, int)));
content()->scrollToRequests()
| rpl::start([this](Ui::ScrollToRequest request) {
onScrollToY(request.ymin, request.ymax);
}, lifetime());
if (_init) {
_init(this);
@ -102,13 +116,13 @@ void PeerListBox::prepare() {
void PeerListBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Down) {
_inner->selectSkip(1);
content()->selectSkip(1);
} else if (e->key() == Qt::Key_Up) {
_inner->selectSkip(-1);
content()->selectSkip(-1);
} else if (e->key() == Qt::Key_PageDown) {
_inner->selectSkipPage(height(), 1);
content()->selectSkipPage(height(), 1);
} else if (e->key() == Qt::Key_PageUp) {
_inner->selectSkipPage(height(), -1);
content()->selectSkipPage(height(), -1);
} else if (e->key() == Qt::Key_Escape && _select && !_select->entity()->getQuery().isEmpty()) {
_select->entity()->clearQuery();
} else {
@ -118,7 +132,7 @@ void PeerListBox::keyPressEvent(QKeyEvent *e) {
void PeerListBox::searchQueryChanged(const QString &query) {
onScrollToY(0);
_inner->searchQueryChanged(query);
content()->searchQueryChanged(query);
}
void PeerListBox::resizeEvent(QResizeEvent *e) {
@ -131,7 +145,7 @@ void PeerListBox::resizeEvent(QResizeEvent *e) {
updateScrollSkips();
}
_inner->resizeToWidth(width());
content()->resizeToWidth(width());
}
void PeerListBox::paintEvent(QPaintEvent *e) {
@ -143,53 +157,19 @@ void PeerListBox::paintEvent(QPaintEvent *e) {
void PeerListBox::setInnerFocus() {
if (!_select || _select->isHiddenOrHiding()) {
_inner->setFocus();
content()->setFocus();
} else {
_select->entity()->setInnerFocus();
}
}
void PeerListBox::peerListAppendRow(std::unique_ptr<PeerListRow> row) {
_inner->appendRow(std::move(row));
}
void PeerListBox::peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) {
_inner->appendSearchRow(std::move(row));
}
void PeerListBox::peerListAppendFoundRow(not_null<PeerListRow*> row) {
_inner->appendFoundRow(row);
}
void PeerListBox::peerListPrependRow(std::unique_ptr<PeerListRow> row) {
_inner->prependRow(std::move(row));
}
void PeerListBox::peerListPrependRowFromSearchResult(not_null<PeerListRow*> row) {
_inner->prependRowFromSearchResult(row);
}
PeerListRow *PeerListBox::peerListFindRow(PeerListRowId id) {
return _inner->findRow(id);
}
void PeerListBox::peerListUpdateRow(not_null<PeerListRow*> row) {
_inner->updateRow(row);
}
void PeerListBox::peerListRemoveRow(not_null<PeerListRow*> row) {
_inner->removeRow(row);
}
void PeerListBox::peerListConvertRowToSearchResult(not_null<PeerListRow*> row) {
_inner->convertRowToSearchResult(row);
}
void PeerListBox::peerListSetRowChecked(not_null<PeerListRow*> row, bool checked) {
void PeerListBox::peerListSetRowChecked(
not_null<PeerListRow*> row,
bool checked) {
auto peer = row->peer();
if (checked) {
addSelectItem(peer, PeerListRow::SetStyle::Animated);
_inner->changeCheckState(row, checked, PeerListRow::SetStyle::Animated);
PeerListContentDelegate::peerListSetRowChecked(row, checked);
peerListUpdateRow(row);
// This call deletes row from _searchRows.
@ -201,40 +181,13 @@ void PeerListBox::peerListSetRowChecked(not_null<PeerListRow*> row, bool checked
}
}
int PeerListBox::peerListFullRowsCount() {
return _inner->fullRowsCount();
}
not_null<PeerListRow*> PeerListBox::peerListRowAt(int index) {
return _inner->rowAt(index);
}
void PeerListBox::peerListRefreshRows() {
_inner->refreshRows();
}
void PeerListBox::peerListScrollToTop() {
onScrollToY(0);
}
void PeerListBox::peerListSetDescription(object_ptr<Ui::FlatLabel> description) {
_inner->setDescription(std::move(description));
}
void PeerListBox::peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) {
_inner->setSearchLoading(std::move(loading));
}
void PeerListBox::peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) {
_inner->setSearchNoResults(std::move(noResults));
}
void PeerListBox::peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) {
_inner->setAboveWidget(std::move(aboveWidget));
}
void PeerListBox::peerListSetSearchMode(PeerListSearchMode mode) {
_inner->setSearchMode(mode);
PeerListContentDelegate::peerListSetSearchMode(mode);
auto selectVisible = (mode != PeerListSearchMode::Disabled);
if (selectVisible && !_select) {
createMultiSelect();
@ -247,22 +200,6 @@ void PeerListBox::peerListSetSearchMode(PeerListSearchMode mode) {
}
}
void PeerListBox::peerListSortRows(base::lambda<bool(PeerListRow &a, PeerListRow &b)> compare) {
_inner->reorderRows([compare = std::move(compare)](auto &&begin, auto &&end) {
std::sort(begin, end, [&compare](auto &&a, auto &&b) {
return compare(*a, *b);
});
});
}
void PeerListBox::peerListPartitionRows(base::lambda<bool(PeerListRow &a)> border) {
_inner->reorderRows([border = std::move(border)](auto &&begin, auto &&end) {
std::stable_partition(begin, end, [&border](auto &&current) {
return border(*current);
});
});
}
PeerListController::PeerListController(std::unique_ptr<PeerListSearchController> searchController) : _searchController(std::move(searchController)) {
if (_searchController) {
_searchController->setDelegate(this);
@ -418,18 +355,25 @@ void PeerListRow::invalidatePixmapsCache() {
}
}
void PeerListRow::paintStatusText(Painter &p, int x, int y, int availableWidth, int outerWidth, bool selected) {
void PeerListRow::paintStatusText(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int availableWidth,
int outerWidth,
bool selected) {
auto statusHasOnlineColor = (_statusType == PeerListRow::StatusType::Online);
p.setFont(st::contactsStatusFont);
p.setPen(statusHasOnlineColor ? st::contactsStatusFgOnline : (selected ? st::contactsStatusFgOver : st::contactsStatusFg));
p.setPen(statusHasOnlineColor ? st.statusFgActive : (selected ? st.statusFgOver : st.statusFg));
_status.drawLeftElided(p, x, y, availableWidth, outerWidth);
}
template <typename UpdateCallback>
void PeerListRow::addRipple(QSize size, QPoint point, UpdateCallback updateCallback) {
void PeerListRow::addRipple(const style::PeerListItem &st, QSize size, QPoint point, UpdateCallback updateCallback) {
if (!_ripple) {
auto mask = Ui::RippleAnimation::rectMask(size);
_ripple = std::make_unique<Ui::RippleAnimation>(st::contactsRipple, std::move(mask), std::move(updateCallback));
_ripple = std::make_unique<Ui::RippleAnimation>(st.button.ripple, std::move(mask), std::move(updateCallback));
}
_ripple->add(point);
}
@ -449,18 +393,29 @@ void PeerListRow::paintRipple(Painter &p, TimeMs ms, int x, int y, int outerWidt
}
}
void PeerListRow::paintUserpic(Painter &p, TimeMs ms, int x, int y, int outerWidth) {
void PeerListRow::paintUserpic(
Painter &p,
const style::PeerListItem &st,
TimeMs ms,
int x,
int y,
int outerWidth) {
if (_disabledState == State::DisabledChecked) {
paintDisabledCheckUserpic(p, x, y, outerWidth);
paintDisabledCheckUserpic(p, st, x, y, outerWidth);
} else if (_checkbox) {
_checkbox->paint(p, ms, x, y, outerWidth);
} else {
peer()->paintUserpicLeft(p, x, y, outerWidth, st::contactsPhotoSize);
peer()->paintUserpicLeft(p, x, y, outerWidth, st.photoSize);
}
}
// Emulates Ui::RoundImageCheckbox::paint() in a checked state.
void PeerListRow::paintDisabledCheckUserpic(Painter &p, int x, int y, int outerWidth) const {
void PeerListRow::paintDisabledCheckUserpic(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int outerWidth) const {
auto userpicRadius = st::contactsPhotoCheckbox.imageSmallRadius;
auto userpicShift = st::contactsPhotoCheckbox.imageRadius - userpicRadius;
auto userpicDiameter = st::contactsPhotoCheckbox.imageRadius * 2;
@ -512,7 +467,10 @@ void PeerListRow::lazyInitialize() {
}
void PeerListRow::createCheckbox(base::lambda<void()> updateCallback) {
_checkbox = std::make_unique<Ui::RoundImageCheckbox>(st::contactsPhotoCheckbox, std::move(updateCallback), PaintUserpicCallback(_peer));
_checkbox = std::make_unique<Ui::RoundImageCheckbox>(
st::contactsPhotoCheckbox,
std::move(updateCallback),
PaintUserpicCallback(_peer));
}
void PeerListRow::setCheckedInternal(bool checked, SetStyle style) {
@ -522,9 +480,14 @@ void PeerListRow::setCheckedInternal(bool checked, SetStyle style) {
_checkbox->setChecked(checked, speed);
}
PeerListBox::Inner::Inner(QWidget *parent, not_null<PeerListController*> controller) : TWidget(parent)
PeerListContent::PeerListContent(
QWidget *parent,
not_null<PeerListController*> controller,
const style::PeerList &st)
: RpWidget(parent)
, _st(st)
, _controller(controller)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) {
, _rowHeight(_st.item.height) {
subscribe(Auth().downloaderTaskFinished(), [this] { update(); });
using UpdateFlag = Notify::PeerUpdate::Flag;
@ -543,7 +506,7 @@ PeerListBox::Inner::Inner(QWidget *parent, not_null<PeerListController*> control
});
}
void PeerListBox::Inner::appendRow(std::unique_ptr<PeerListRow> row) {
void PeerListContent::appendRow(std::unique_ptr<PeerListRow> row) {
Expects(row != nullptr);
if (_rowsById.find(row->id()) == _rowsById.cend()) {
row->setAbsoluteIndex(_rows.size());
@ -552,7 +515,7 @@ void PeerListBox::Inner::appendRow(std::unique_ptr<PeerListRow> row) {
}
}
void PeerListBox::Inner::appendSearchRow(std::unique_ptr<PeerListRow> row) {
void PeerListContent::appendSearchRow(std::unique_ptr<PeerListRow> row) {
Expects(row != nullptr);
Expects(showingSearch());
if (_rowsById.find(row->id()) == _rowsById.cend()) {
@ -564,7 +527,7 @@ void PeerListBox::Inner::appendSearchRow(std::unique_ptr<PeerListRow> row) {
}
}
void PeerListBox::Inner::appendFoundRow(not_null<PeerListRow*> row) {
void PeerListContent::appendFoundRow(not_null<PeerListRow*> row) {
Expects(showingSearch());
auto index = findRowIndex(row);
if (index.value < 0) {
@ -572,13 +535,13 @@ void PeerListBox::Inner::appendFoundRow(not_null<PeerListRow*> row) {
}
}
void PeerListBox::Inner::changeCheckState(not_null<PeerListRow*> row, bool checked, PeerListRow::SetStyle style) {
void PeerListContent::changeCheckState(not_null<PeerListRow*> row, bool checked, PeerListRow::SetStyle style) {
row->setChecked(checked, style, [this, row] {
updateRow(row);
});
}
void PeerListBox::Inner::addRowEntry(not_null<PeerListRow*> row) {
void PeerListContent::addRowEntry(not_null<PeerListRow*> row) {
_rowsById.emplace(row->id(), row);
_rowsByPeer[row->peer()].push_back(row);
if (addingToSearchIndex()) {
@ -590,18 +553,18 @@ void PeerListBox::Inner::addRowEntry(not_null<PeerListRow*> row) {
}
}
void PeerListBox::Inner::invalidatePixmapsCache() {
void PeerListContent::invalidatePixmapsCache() {
auto invalidate = [](auto &&row) { row->invalidatePixmapsCache(); };
base::for_each(_rows, invalidate);
base::for_each(_searchRows, invalidate);
}
bool PeerListBox::Inner::addingToSearchIndex() const {
bool PeerListContent::addingToSearchIndex() const {
// If we started indexing already, we continue.
return (_searchMode != PeerListSearchMode::Disabled) || !_searchIndex.empty();
}
void PeerListBox::Inner::addToSearchIndex(not_null<PeerListRow*> row) {
void PeerListContent::addToSearchIndex(not_null<PeerListRow*> row) {
if (row->isSearchResult()) {
return;
}
@ -613,7 +576,7 @@ void PeerListBox::Inner::addToSearchIndex(not_null<PeerListRow*> row) {
}
}
void PeerListBox::Inner::removeFromSearchIndex(not_null<PeerListRow*> row) {
void PeerListContent::removeFromSearchIndex(not_null<PeerListRow*> row) {
auto &nameFirstChars = row->nameFirstChars();
if (!nameFirstChars.empty()) {
for_const (auto ch, row->nameFirstChars()) {
@ -630,7 +593,7 @@ void PeerListBox::Inner::removeFromSearchIndex(not_null<PeerListRow*> row) {
}
}
void PeerListBox::Inner::prependRow(std::unique_ptr<PeerListRow> row) {
void PeerListContent::prependRow(std::unique_ptr<PeerListRow> row) {
Expects(row != nullptr);
if (_rowsById.find(row->id()) == _rowsById.cend()) {
addRowEntry(row.get());
@ -639,7 +602,7 @@ void PeerListBox::Inner::prependRow(std::unique_ptr<PeerListRow> row) {
}
}
void PeerListBox::Inner::prependRowFromSearchResult(not_null<PeerListRow*> row) {
void PeerListContent::prependRowFromSearchResult(not_null<PeerListRow*> row) {
if (!row->isSearchResult()) {
return;
}
@ -658,26 +621,26 @@ void PeerListBox::Inner::prependRowFromSearchResult(not_null<PeerListRow*> row)
}
}
void PeerListBox::Inner::refreshIndices() {
void PeerListContent::refreshIndices() {
auto index = 0;
for (auto &row : _rows) {
row->setAbsoluteIndex(index++);
}
}
void PeerListBox::Inner::removeRowAtIndex(std::vector<std::unique_ptr<PeerListRow>> &from, int index) {
void PeerListContent::removeRowAtIndex(std::vector<std::unique_ptr<PeerListRow>> &from, int index) {
from.erase(from.begin() + index);
for (auto i = index, count = int(from.size()); i != count; ++i) {
from[i]->setAbsoluteIndex(i);
}
}
PeerListRow *PeerListBox::Inner::findRow(PeerListRowId id) {
PeerListRow *PeerListContent::findRow(PeerListRowId id) {
auto it = _rowsById.find(id);
return (it == _rowsById.cend()) ? nullptr : it->second.get();
}
void PeerListBox::Inner::removeRow(not_null<PeerListRow*> row) {
void PeerListContent::removeRow(not_null<PeerListRow*> row) {
auto index = row->absoluteIndex();
auto isSearchResult = row->isSearchResult();
auto &eraseFrom = isSearchResult ? _searchRows : _rows;
@ -698,7 +661,7 @@ void PeerListBox::Inner::removeRow(not_null<PeerListRow*> row) {
restoreSelection();
}
void PeerListBox::Inner::convertRowToSearchResult(not_null<PeerListRow*> row) {
void PeerListContent::convertRowToSearchResult(not_null<PeerListRow*> row) {
if (row->isSearchResult()) {
return;
} else if (!showingSearch() || !_controller->hasComplexSearch()) {
@ -715,44 +678,44 @@ void PeerListBox::Inner::convertRowToSearchResult(not_null<PeerListRow*> row) {
removeRowAtIndex(_rows, index);
}
int PeerListBox::Inner::fullRowsCount() const {
int PeerListContent::fullRowsCount() const {
return _rows.size();
}
not_null<PeerListRow*> PeerListBox::Inner::rowAt(int index) const {
not_null<PeerListRow*> PeerListContent::rowAt(int index) const {
Expects(index >= 0 && index < _rows.size());
return _rows[index].get();
}
void PeerListBox::Inner::setDescription(object_ptr<Ui::FlatLabel> description) {
void PeerListContent::setDescription(object_ptr<Ui::FlatLabel> description) {
_description = std::move(description);
if (_description) {
_description->setParent(this);
}
}
void PeerListBox::Inner::setSearchLoading(object_ptr<Ui::FlatLabel> loading) {
void PeerListContent::setSearchLoading(object_ptr<Ui::FlatLabel> loading) {
_searchLoading = std::move(loading);
if (_searchLoading) {
_searchLoading->setParent(this);
}
}
void PeerListBox::Inner::setSearchNoResults(object_ptr<Ui::FlatLabel> noResults) {
void PeerListContent::setSearchNoResults(object_ptr<Ui::FlatLabel> noResults) {
_searchNoResults = std::move(noResults);
if (_searchNoResults) {
_searchNoResults->setParent(this);
}
}
void PeerListBox::Inner::setAboveWidget(object_ptr<TWidget> aboveWidget) {
void PeerListContent::setAboveWidget(object_ptr<TWidget> aboveWidget) {
_aboveWidget = std::move(aboveWidget);
if (_aboveWidget) {
_aboveWidget->setParent(this);
}
}
int PeerListBox::Inner::labelHeight() const {
int PeerListContent::labelHeight() const {
auto computeLabelHeight = [](auto &label) {
if (!label) {
return 0;
@ -771,8 +734,8 @@ int PeerListBox::Inner::labelHeight() const {
return computeLabelHeight(_description);
}
void PeerListBox::Inner::refreshRows() {
resizeToWidth(st::boxWideWidth);
void PeerListContent::refreshRows() {
resizeToWidth(width());
if (_visibleBottom > 0) {
checkScrollForPreload();
}
@ -780,7 +743,7 @@ void PeerListBox::Inner::refreshRows() {
update();
}
void PeerListBox::Inner::setSearchMode(PeerListSearchMode mode) {
void PeerListContent::setSearchMode(PeerListSearchMode mode) {
if (_searchMode != mode) {
if (!addingToSearchIndex()) {
for_const (auto &row, _rows) {
@ -790,7 +753,11 @@ void PeerListBox::Inner::setSearchMode(PeerListSearchMode mode) {
_searchMode = mode;
if (_controller->hasComplexSearch()) {
if (!_searchLoading) {
setSearchLoading(object_ptr<Ui::FlatLabel>(this, lang(lng_contacts_loading), Ui::FlatLabel::InitType::Simple, st::membersAbout));
setSearchLoading(object_ptr<Ui::FlatLabel>(
this,
lang(lng_contacts_loading),
Ui::FlatLabel::InitType::Simple,
st::membersAbout));
}
} else {
clearSearchRows();
@ -798,17 +765,17 @@ void PeerListBox::Inner::setSearchMode(PeerListSearchMode mode) {
}
}
void PeerListBox::Inner::clearSearchRows() {
void PeerListContent::clearSearchRows() {
while (!_searchRows.empty()) {
removeRow(_searchRows.back().get());
}
}
void PeerListBox::Inner::paintEvent(QPaintEvent *e) {
void PeerListContent::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
Painter p(this);
p.fillRect(r, st::contactsBg);
p.fillRect(r, _st.item.button.textBg);
auto rowsTopCached = rowsTop();
auto ms = getms();
@ -827,7 +794,7 @@ void PeerListBox::Inner::paintEvent(QPaintEvent *e) {
}
}
int PeerListBox::Inner::resizeGetHeight(int newWidth) {
int PeerListContent::resizeGetHeight(int newWidth) {
_aboveHeight = 0;
if (_aboveWidget) {
_aboveWidget->resizeToWidth(newWidth);
@ -839,6 +806,7 @@ int PeerListBox::Inner::resizeGetHeight(int newWidth) {
_aboveHeight = _aboveWidget->height();
}
}
auto rowsCount = shownRowsCount();
auto labelTop = rowsTop() + qMax(1, shownRowsCount()) * _rowHeight;
if (_description) {
_description->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top(), newWidth);
@ -852,20 +820,22 @@ int PeerListBox::Inner::resizeGetHeight(int newWidth) {
_searchLoading->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top(), newWidth);
_searchLoading->setVisible(showingSearch() && _filterResults.empty() && _controller->isSearchLoading());
}
return labelTop + labelHeight() + st::membersMarginBottom;
auto label = labelHeight();
return ((label > 0 || rowsCount > 0) ? (labelTop + label) : 0)
+ _st.padding.bottom();
}
void PeerListBox::Inner::enterEventHook(QEvent *e) {
void PeerListContent::enterEventHook(QEvent *e) {
setMouseTracking(true);
}
void PeerListBox::Inner::leaveEventHook(QEvent *e) {
void PeerListContent::leaveEventHook(QEvent *e) {
_mouseSelection = false;
setMouseTracking(false);
setSelected(Selected());
}
void PeerListBox::Inner::mouseMoveEvent(QMouseEvent *e) {
void PeerListContent::mouseMoveEvent(QMouseEvent *e) {
auto position = e->globalPos();
if (_mouseSelection || _lastMousePosition != position) {
_lastMousePosition = position;
@ -874,7 +844,7 @@ void PeerListBox::Inner::mouseMoveEvent(QMouseEvent *e) {
}
}
void PeerListBox::Inner::mousePressEvent(QMouseEvent *e) {
void PeerListContent::mousePressEvent(QMouseEvent *e) {
_mouseSelection = true;
_lastMousePosition = e->globalPos();
updateSelection();
@ -893,12 +863,12 @@ void PeerListBox::Inner::mousePressEvent(QMouseEvent *e) {
} else {
auto size = QSize(width(), _rowHeight);
auto point = mapFromGlobal(QCursor::pos()) - QPoint(0, getRowTop(_selected.index));
row->addRipple(size, point, std::move(updateCallback));
row->addRipple(_st.item, size, point, std::move(updateCallback));
}
}
}
void PeerListBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
void PeerListContent::mouseReleaseEvent(QMouseEvent *e) {
updateRow(_pressed.index);
updateRow(_selected.index);
@ -915,7 +885,7 @@ void PeerListBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
}
}
void PeerListBox::Inner::setPressed(Selected pressed) {
void PeerListContent::setPressed(Selected pressed) {
if (auto row = getRow(_pressed.index)) {
row->stopLastRipple();
row->stopLastActionRipple();
@ -923,7 +893,7 @@ void PeerListBox::Inner::setPressed(Selected pressed) {
_pressed = pressed;
}
void PeerListBox::Inner::paintRow(Painter &p, TimeMs ms, RowIndex index) {
void PeerListContent::paintRow(Painter &p, TimeMs ms, RowIndex index) {
auto row = getRow(index);
Assert(row != nullptr);
row->lazyInitialize();
@ -934,17 +904,26 @@ void PeerListBox::Inner::paintRow(Painter &p, TimeMs ms, RowIndex index) {
auto selected = (active.index == index);
auto actionSelected = (selected && active.action);
p.fillRect(0, 0, width(), _rowHeight, selected ? st::contactsBgOver : st::contactsBg);
auto &bg = selected
? _st.item.button.textBgOver
: _st.item.button.textBg;
p.fillRect(0, 0, width(), _rowHeight, bg);
row->paintRipple(p, ms, 0, 0, width());
row->paintUserpic(p, ms, st::contactsPadding.left(), st::contactsPadding.top(), width());
row->paintUserpic(
p,
_st.item,
ms,
_st.item.photoPosition.x(),
_st.item.photoPosition.y(),
width());
p.setPen(st::contactsNameFg);
auto actionSize = row->actionSize();
auto actionMargins = actionSize.isEmpty() ? QMargins() : row->actionMargins();
auto &name = row->name();
auto namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left();
auto namew = width() - namex - st::contactsPadding.right();
auto namex = _st.item.namePosition.x();
auto namew = width() - namex - _st.item.photoPosition.x();
if (!actionSize.isEmpty()) {
namew -= actionMargins.left() + actionSize.width() + actionMargins.right();
}
@ -952,14 +931,14 @@ void PeerListBox::Inner::paintRow(Painter &p, TimeMs ms, RowIndex index) {
if (row->needsVerifiedIcon()) {
auto icon = &st::dialogsVerifiedIcon;
namew -= icon->width();
icon->paint(p, namex + qMin(name.maxWidth(), namew), st::contactsPadding.top() + st::contactsNameTop, width());
icon->paint(p, namex + qMin(name.maxWidth(), namew), _st.item.namePosition.y(), width());
}
auto nameCheckedRatio = row->disabled() ? 0. : row->checkedRatio();
p.setPen(anim::pen(st::contactsNameFg, st::contactsNameCheckedFg, nameCheckedRatio));
name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width());
name.drawLeftElided(p, namex, _st.item.namePosition.y(), namew, width());
if (!actionSize.isEmpty()) {
auto actionLeft = width() - st::contactsPadding.right() - actionMargins.right() - actionSize.width();
auto actionLeft = width() - _st.item.photoPosition.x() - actionMargins.right() - actionSize.width();
auto actionTop = actionMargins.top();
row->paintAction(p, ms, actionLeft, actionTop, width(), actionSelected);
}
@ -975,22 +954,22 @@ void PeerListBox::Inner::paintRow(Painter &p, TimeMs ms, RowIndex index) {
if (highlightedWidth > availableWidth) {
highlightedPart = st::contactsStatusFont->elided(highlightedPart, availableWidth);
}
p.setPen(st::contactsStatusFgOnline);
p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsStatusTop, width(), highlightedPart);
p.setPen(_st.item.statusFgActive);
p.drawTextLeft(_st.item.statusPosition.x(), _st.item.statusPosition.y(), width(), highlightedPart);
} else {
grayedPart = st::contactsStatusFont->elided(grayedPart, availableWidth - highlightedWidth);
auto grayedWidth = st::contactsStatusFont->width(grayedPart);
p.setPen(st::contactsStatusFgOnline);
p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsStatusTop, width(), highlightedPart);
p.setPen(selected ? st::contactsStatusFgOver : st::contactsStatusFg);
p.drawTextLeft(namex + highlightedWidth, st::contactsPadding.top() + st::contactsStatusTop, width(), grayedPart);
p.setPen(_st.item.statusFgActive);
p.drawTextLeft(_st.item.statusPosition.x(), _st.item.statusPosition.y(), width(), highlightedPart);
p.setPen(selected ? _st.item.statusFgOver : _st.item.statusFg);
p.drawTextLeft(_st.item.statusPosition.x() + highlightedWidth, _st.item.statusPosition.y(), width(), grayedPart);
}
} else {
row->paintStatusText(p, namex, st::contactsPadding.top() + st::contactsStatusTop, statusw, width(), selected);
row->paintStatusText(p, _st.item, _st.item.statusPosition.x(), _st.item.statusPosition.y(), statusw, width(), selected);
}
}
void PeerListBox::Inner::selectSkip(int direction) {
void PeerListContent::selectSkip(int direction) {
if (_pressed.index.value >= 0) {
return;
}
@ -1049,19 +1028,19 @@ void PeerListBox::Inner::selectSkip(int direction) {
if (newSelectedIndex >= 0) {
auto top = (newSelectedIndex > 0) ? getRowTop(RowIndex(newSelectedIndex)) : 0;
auto bottom = (newSelectedIndex + 1 < rowsCount) ? getRowTop(RowIndex(newSelectedIndex + 1)) : height();
emit mustScrollTo(top, bottom);
_scrollToRequests.fire({ top, bottom });
}
update();
}
void PeerListBox::Inner::selectSkipPage(int height, int direction) {
void PeerListContent::selectSkipPage(int height, int direction) {
auto rowsToSkip = height / _rowHeight;
if (!rowsToSkip) return;
selectSkip(rowsToSkip * direction);
}
void PeerListBox::Inner::loadProfilePhotos() {
void PeerListContent::loadProfilePhotos() {
if (_visibleTop >= _visibleBottom) return;
auto yFrom = _visibleTop;
@ -1086,13 +1065,13 @@ void PeerListBox::Inner::loadProfilePhotos() {
}
}
void PeerListBox::Inner::checkScrollForPreload() {
void PeerListContent::checkScrollForPreload() {
if (_visibleBottom + PreloadHeightsCount * (_visibleBottom - _visibleTop) > height()) {
_controller->loadMoreRows();
}
}
void PeerListBox::Inner::searchQueryChanged(QString query) {
void PeerListContent::searchQueryChanged(QString query) {
auto searchWordsList = TextUtilities::PrepareSearchWords(query);
auto normalizedQuery = searchWordsList.isEmpty() ? QString() : searchWordsList.join(' ');
if (_normalizedSearchQuery != normalizedQuery) {
@ -1144,7 +1123,7 @@ void PeerListBox::Inner::searchQueryChanged(QString query) {
}
}
void PeerListBox::Inner::setSearchQuery(const QString &query, const QString &normalizedQuery) {
void PeerListContent::setSearchQuery(const QString &query, const QString &normalizedQuery) {
setSelected(Selected());
setPressed(Selected());
_searchQuery = query;
@ -1154,13 +1133,13 @@ void PeerListBox::Inner::setSearchQuery(const QString &query, const QString &nor
clearSearchRows();
}
void PeerListBox::Inner::submitted() {
void PeerListContent::submitted() {
if (auto row = getRow(_selected.index)) {
_controller->rowClicked(row);
}
}
void PeerListBox::Inner::visibleTopBottomUpdated(
void PeerListContent::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
_visibleTop = visibleTop;
@ -1169,7 +1148,7 @@ void PeerListBox::Inner::visibleTopBottomUpdated(
checkScrollForPreload();
}
void PeerListBox::Inner::setSelected(Selected selected) {
void PeerListContent::setSelected(Selected selected) {
updateRow(_selected.index);
if (_selected != selected) {
_selected = selected;
@ -1178,12 +1157,12 @@ void PeerListBox::Inner::setSelected(Selected selected) {
}
}
void PeerListBox::Inner::restoreSelection() {
void PeerListContent::restoreSelection() {
_lastMousePosition = QCursor::pos();
updateSelection();
}
void PeerListBox::Inner::updateSelection() {
void PeerListContent::updateSelection() {
if (!_mouseSelection) return;
auto point = mapFromGlobal(_lastMousePosition);
@ -1204,35 +1183,35 @@ void PeerListBox::Inner::updateSelection() {
setSelected(selected);
}
QRect PeerListBox::Inner::getActionRect(not_null<PeerListRow*> row, RowIndex index) const {
QRect PeerListContent::getActionRect(not_null<PeerListRow*> row, RowIndex index) const {
auto actionSize = row->actionSize();
if (actionSize.isEmpty()) {
return QRect();
}
auto actionMargins = row->actionMargins();
auto actionRight = st::contactsPadding.right() + actionMargins.right();
auto actionRight = _st.item.photoPosition.x() + actionMargins.right();
auto actionTop = actionMargins.top();
auto actionLeft = width() - actionRight - actionSize.width();
auto rowTop = getRowTop(index);
return myrtlrect(actionLeft, rowTop + actionTop, actionSize.width(), actionSize.height());
}
int PeerListBox::Inner::rowsTop() const {
return _aboveHeight + st::membersMarginTop;
int PeerListContent::rowsTop() const {
return _aboveHeight + _st.padding.top();
}
int PeerListBox::Inner::getRowTop(RowIndex index) const {
int PeerListContent::getRowTop(RowIndex index) const {
if (index.value >= 0) {
return rowsTop() + index.value * _rowHeight;
}
return -1;
}
void PeerListBox::Inner::updateRow(not_null<PeerListRow*> row, RowIndex hint) {
void PeerListContent::updateRow(not_null<PeerListRow*> row, RowIndex hint) {
updateRow(findRowIndex(row, hint));
}
void PeerListBox::Inner::updateRow(RowIndex index) {
void PeerListContent::updateRow(RowIndex index) {
if (index.value < 0) {
return;
}
@ -1249,12 +1228,12 @@ void PeerListBox::Inner::updateRow(RowIndex index) {
}
template <typename Callback>
bool PeerListBox::Inner::enumerateShownRows(Callback callback) {
bool PeerListContent::enumerateShownRows(Callback callback) {
return enumerateShownRows(0, shownRowsCount(), std::move(callback));
}
template <typename Callback>
bool PeerListBox::Inner::enumerateShownRows(int from, int to, Callback callback) {
bool PeerListContent::enumerateShownRows(int from, int to, Callback callback) {
Assert(0 <= from);
Assert(from <= to);
if (showingSearch()) {
@ -1275,7 +1254,7 @@ bool PeerListBox::Inner::enumerateShownRows(int from, int to, Callback callback)
return true;
}
PeerListRow *PeerListBox::Inner::getRow(RowIndex index) {
PeerListRow *PeerListContent::getRow(RowIndex index) {
if (index.value >= 0) {
if (showingSearch()) {
if (index.value < _filterResults.size()) {
@ -1288,7 +1267,7 @@ PeerListRow *PeerListBox::Inner::getRow(RowIndex index) {
return nullptr;
}
PeerListBox::Inner::RowIndex PeerListBox::Inner::findRowIndex(not_null<PeerListRow*> row, RowIndex hint) {
PeerListContent::RowIndex PeerListContent::findRowIndex(not_null<PeerListRow*> row, RowIndex hint) {
if (!showingSearch()) {
Assert(!row->isSearchResult());
return RowIndex(row->absoluteIndex());
@ -1309,7 +1288,7 @@ PeerListBox::Inner::RowIndex PeerListBox::Inner::findRowIndex(not_null<PeerListR
return result;
}
void PeerListBox::Inner::handleNameChanged(const Notify::PeerUpdate &update) {
void PeerListContent::handleNameChanged(const Notify::PeerUpdate &update) {
auto byPeer = _rowsByPeer.find(update.peer);
if (byPeer != _rowsByPeer.cend()) {
for (auto row : byPeer->second) {

View File

@ -20,10 +20,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <rpl/event_stream.h>
#include "ui/rp_widget.h"
#include "boxes/abstract_box.h"
#include "mtproto/sender.h"
#include "base/timer.h"
namespace style {
struct PeerList;
} // namespace style
namespace Ui {
class RippleAnimation;
class RoundImageCheckbox;
@ -31,6 +37,7 @@ class MultiSelect;
template <typename Widget>
class SlideWrap;
class FlatLabel;
struct ScrollToRequest;
} // namespace Ui
namespace Notify {
@ -135,10 +142,20 @@ public:
void invalidatePixmapsCache();
template <typename UpdateCallback>
void addRipple(QSize size, QPoint point, UpdateCallback updateCallback);
void addRipple(
const style::PeerListItem &st,
QSize size,
QPoint point,
UpdateCallback updateCallback);
void stopLastRipple();
void paintRipple(Painter &p, TimeMs ms, int x, int y, int outerWidth);
void paintUserpic(Painter &p, TimeMs ms, int x, int y, int outerWidth);
void paintUserpic(
Painter &p,
const style::PeerListItem &st,
TimeMs ms,
int x,
int y,
int outerWidth);
float64 checkedRatio();
void setNameFirstChars(const OrderedSet<QChar> &nameFirstChars) {
@ -149,7 +166,14 @@ public:
}
virtual void lazyInitialize();
virtual void paintStatusText(Painter &p, int x, int y, int availableWidth, int outerWidth, bool selected);
virtual void paintStatusText(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int availableWidth,
int outerWidth,
bool selected);
protected:
bool isInitialized() const {
@ -159,7 +183,12 @@ protected:
private:
void createCheckbox(base::lambda<void()> updateCallback);
void setCheckedInternal(bool checked, SetStyle style);
void paintDisabledCheckUserpic(Painter &p, int x, int y, int outerWidth) const;
void paintDisabledCheckUserpic(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int outerWidth) const;
void setStatusText(const QString &text);
PeerListRowId _id = 0;
@ -323,78 +352,14 @@ private:
};
class PeerListBox : public BoxContent, public PeerListDelegate {
class PeerListContent
: public Ui::RpWidget
, private base::Subscriber {
public:
PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(not_null<PeerListBox*>)> init);
void peerListSetTitle(base::lambda<QString()> title) override {
setTitle(std::move(title));
}
void peerListSetAdditionalTitle(base::lambda<QString()> title) override {
setAdditionalTitle(std::move(title));
}
void peerListSetDescription(object_ptr<Ui::FlatLabel> description) override;
void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) override;
void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) override;
void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) override;
void peerListSetSearchMode(PeerListSearchMode mode) override;
void peerListAppendRow(std::unique_ptr<PeerListRow> row) override;
void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) override;
void peerListAppendFoundRow(not_null<PeerListRow*> row) override;
void peerListPrependRow(std::unique_ptr<PeerListRow> row) override;
void peerListPrependRowFromSearchResult(not_null<PeerListRow*> row) override;
void peerListUpdateRow(not_null<PeerListRow*> row) override;
void peerListRemoveRow(not_null<PeerListRow*> row) override;
void peerListConvertRowToSearchResult(not_null<PeerListRow*> row) override;
void peerListSetRowChecked(not_null<PeerListRow*> row, bool checked) override;
not_null<PeerListRow*> peerListRowAt(int index) override;
bool peerListIsRowSelected(not_null<PeerData*> peer) override;
int peerListSelectedRowsCount() override;
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
void peerListRefreshRows() override;
void peerListScrollToTop() override;
int peerListFullRowsCount() override;
PeerListRow *peerListFindRow(PeerListRowId id) override;
void peerListSortRows(base::lambda<bool(PeerListRow &a, PeerListRow &b)> compare) override;
void peerListPartitionRows(base::lambda<bool(PeerListRow &a)> border) override;
protected:
void prepare() override;
void setInnerFocus() override;
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
void peerListAddSelectedRowInBunch(not_null<PeerData*> peer) override {
addSelectItem(peer, PeerListRow::SetStyle::Fast);
}
void peerListFinishSelectedRowsBunch() override;
void addSelectItem(not_null<PeerData*> peer, PeerListRow::SetStyle style);
void createMultiSelect();
int getTopScrollSkip() const;
void updateScrollSkips();
void searchQueryChanged(const QString &query);
object_ptr<Ui::SlideWrap<Ui::MultiSelect>> _select = { nullptr };
class Inner;
QPointer<Inner> _inner;
std::unique_ptr<PeerListController> _controller;
base::lambda<void(PeerListBox*)> _init;
bool _scrollBottomFixed = true;
};
// This class is hold in header because it requires Qt preprocessing.
class PeerListBox::Inner : public TWidget, private base::Subscriber {
Q_OBJECT
public:
Inner(QWidget *parent, not_null<PeerListController*> controller);
PeerListContent(
QWidget *parent,
not_null<PeerListController*> controller,
const style::PeerList &st);
void selectSkip(int direction);
void selectSkipPage(int height, int direction);
@ -436,8 +401,9 @@ public:
refreshIndices();
}
signals:
void mustScrollTo(int ymin, int ymax);
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
return _scrollToRequests.events();
}
protected:
int resizeGetHeight(int newWidth) override;
@ -528,6 +494,7 @@ private:
void clearSearchRows();
const style::PeerList &_st;
not_null<PeerListController*> _controller;
PeerListSearchMode _searchMode = PeerListSearchMode::Disabled;
@ -539,6 +506,8 @@ private:
Selected _pressed;
bool _mouseSelection = false;
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
std::vector<std::unique_ptr<PeerListRow>> _rows;
std::map<PeerListRowId, not_null<PeerListRow*>> _rowsById;
std::map<PeerData*, std::vector<not_null<PeerListRow*>>> _rowsByPeer;
@ -560,3 +529,156 @@ private:
std::vector<std::unique_ptr<PeerListRow>> _searchRows;
};
class PeerListContentDelegate : public PeerListDelegate {
public:
void setContent(PeerListContent *content) {
_content = content;
}
void peerListAppendRow(
std::unique_ptr<PeerListRow> row) override {
_content->appendRow(std::move(row));
}
void peerListAppendSearchRow(
std::unique_ptr<PeerListRow> row) override {
_content->appendSearchRow(std::move(row));
}
void peerListAppendFoundRow(
not_null<PeerListRow*> row) override {
_content->appendFoundRow(row);
}
void peerListPrependRow(
std::unique_ptr<PeerListRow> row) override {
_content->prependRow(std::move(row));
}
void peerListPrependRowFromSearchResult(
not_null<PeerListRow*> row) override {
_content->prependRowFromSearchResult(row);
}
PeerListRow *peerListFindRow(PeerListRowId id) override {
return _content->findRow(id);
}
void peerListUpdateRow(not_null<PeerListRow*> row) override {
_content->updateRow(row);
}
void peerListRemoveRow(not_null<PeerListRow*> row) override {
_content->removeRow(row);
}
void peerListConvertRowToSearchResult(
not_null<PeerListRow*> row) override {
_content->convertRowToSearchResult(row);
}
void peerListSetRowChecked(
not_null<PeerListRow*> row,
bool checked) override {
_content->changeCheckState(
row,
checked,
PeerListRow::SetStyle::Animated);
}
int peerListFullRowsCount() override {
return _content->fullRowsCount();
}
not_null<PeerListRow*> peerListRowAt(int index) override {
return _content->rowAt(index);
}
void peerListRefreshRows() override {
_content->refreshRows();
}
void peerListSetDescription(object_ptr<Ui::FlatLabel> description) override {
_content->setDescription(std::move(description));
}
void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) override {
_content->setSearchLoading(std::move(loading));
}
void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) override {
_content->setSearchNoResults(std::move(noResults));
}
void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) override {
_content->setAboveWidget(std::move(aboveWidget));
}
void peerListSetSearchMode(PeerListSearchMode mode) override {
_content->setSearchMode(mode);
}
void peerListSortRows(
base::lambda<bool(PeerListRow &a, PeerListRow &b)> compare) override {
_content->reorderRows([compare = std::move(compare)](
auto &&begin,
auto &&end) {
std::sort(begin, end, [&compare](auto &&a, auto &&b) {
return compare(*a, *b);
});
});
}
void peerListPartitionRows(
base::lambda<bool(PeerListRow &a)> border) override {
_content->reorderRows([border = std::move(border)](
auto &&begin,
auto &&end) {
std::stable_partition(begin, end, [&border](
auto &&current) {
return border(*current);
});
});
}
protected:
not_null<PeerListContent*> content() const {
return _content;
}
private:
PeerListContent *_content = nullptr;
};
class PeerListBox : public BoxContent, public PeerListContentDelegate {
public:
PeerListBox(
QWidget*,
std::unique_ptr<PeerListController> controller,
base::lambda<void(not_null<PeerListBox*>)> init);
void peerListSetTitle(base::lambda<QString()> title) override {
setTitle(std::move(title));
}
void peerListSetAdditionalTitle(base::lambda<QString()> title) override {
setAdditionalTitle(std::move(title));
}
void peerListSetSearchMode(PeerListSearchMode mode) override;
void peerListSetRowChecked(
not_null<PeerListRow*> row,
bool checked) override;
bool peerListIsRowSelected(not_null<PeerData*> peer) override;
int peerListSelectedRowsCount() override;
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
void peerListScrollToTop() override;
protected:
void prepare() override;
void setInnerFocus() override;
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
void peerListAddSelectedRowInBunch(not_null<PeerData*> peer) override {
addSelectItem(peer, PeerListRow::SetStyle::Fast);
}
void peerListFinishSelectedRowsBunch() override;
void addSelectItem(not_null<PeerData*> peer, PeerListRow::SetStyle style);
void createMultiSelect();
int getTopScrollSkip() const;
void updateScrollSkips();
void searchQueryChanged(const QString &query);
object_ptr<Ui::SlideWrap<Ui::MultiSelect>> _select = { nullptr };
std::unique_ptr<PeerListController> _controller;
base::lambda<void(PeerListBox*)> _init;
bool _scrollBottomFixed = true;
};

View File

@ -77,7 +77,14 @@ public:
return _items.front()->id;
}
void paintStatusText(Painter &p, int x, int y, int availableWidth, int outerWidth, bool selected) override;
void paintStatusText(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int availableWidth,
int outerWidth,
bool selected) override;
void addActionRipple(QPoint point, base::lambda<void()> updateCallback) override;
void stopLastActionRipple() override;
@ -111,7 +118,7 @@ BoxController::Row::Row(HistoryItem *item) : PeerListRow(item->history()->peer,
refreshStatus();
}
void BoxController::Row::paintStatusText(Painter &p, int x, int y, int availableWidth, int outerWidth, bool selected) {
void BoxController::Row::paintStatusText(Painter &p, const style::PeerListItem &st, int x, int y, int availableWidth, int outerWidth, bool selected) {
auto icon = ([this] {
switch (_type) {
case Type::In: return &st::callArrowIn;
@ -125,7 +132,7 @@ void BoxController::Row::paintStatusText(Painter &p, int x, int y, int available
x += shift;
availableWidth -= shift;
PeerListRow::paintStatusText(p, x, y, availableWidth, outerWidth, selected);
PeerListRow::paintStatusText(p, st, x, y, availableWidth, outerWidth, selected);
}
void BoxController::Row::paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) {

View File

@ -90,7 +90,7 @@ membersInnerDropdown: InnerDropdown(defaultInnerDropdown) {
scrollMargin: margins(0px, 5px, 0px, 5px);
scrollPadding: margins(0px, 3px, 0px, 3px);
}
membersInnerItem: defaultProfileMemberItem;
membersInnerItem: defaultPeerListItem;
historyFileOutImage: icon {{ "history_file_image", historyFileOutIconFg }};
historyFileOutImageSelected: icon {{ "history_file_image", historyFileOutIconFgSelected }};

View File

@ -224,10 +224,12 @@ infoSharedMediaButton: infoProfileButton;
infoSharedMediaBottomSkip: 12px;
infoMembersHeader: 56px;
infoMembersItem: ProfilePeerListItem(defaultProfileMemberItem) {
photoPosition: point(18px, 6px);
namePosition: point(79px, 11px);
statusPosition: point(79px, 31px);
infoMembersList: PeerList(defaultPeerList) {
item: PeerListItem(defaultPeerListItem) {
photoPosition: point(18px, 6px);
namePosition: point(79px, 11px);
statusPosition: point(79px, 31px);
}
}
infoMembersButtonPosition: point(12px, 9px);
infoMembersButtonIconPosition: point(6px, 6px);

View File

@ -139,6 +139,10 @@ void ContentWidget::scrollTopRestore(int scrollTop) {
_scroll->scrollToY(scrollTop);
}
void ContentWidget::scrollTo(const Ui::ScrollToRequest &request) {
_scroll->scrollTo(request);
}
bool ContentWidget::wheelEventFromFloatPlayer(QEvent *e) {
return _scroll->viewportEvent(e);
}

View File

@ -29,6 +29,7 @@ enum class SharedMediaType : char;
namespace Ui {
class ScrollArea;
struct ScrollToRequest;
} // namespace Ui
namespace Info {
@ -132,6 +133,8 @@ protected:
int scrollTopSave() const;
void scrollTopRestore(int scrollTop);
void scrollTo(const Ui::ScrollToRequest &request);
private:
RpWidget *doSetInnerWidget(
object_ptr<RpWidget> inner,

View File

@ -37,6 +37,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_info.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/scroll_area.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
@ -55,6 +56,7 @@ InnerWidget::InnerWidget(
_content->heightValue()
| rpl::start([this](int height) {
TWidget::resizeToWidth(width());
_desiredHeight.fire(countDesiredHeight());
}, lifetime());
}
@ -70,7 +72,7 @@ rpl::producer<bool> InnerWidget::canHideDetails() const {
object_ptr<Ui::RpWidget> InnerWidget::setupContent(
RpWidget *parent,
rpl::producer<Wrap> &&wrapValue) const {
rpl::producer<Wrap> &&wrapValue) {
auto result = object_ptr<Ui::VerticalLayout>(parent);
auto cover = result->add(object_ptr<Cover>(
result,
@ -97,10 +99,24 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
// }
}
if (_peer->isChat() || _peer->isMegagroup()) {
result->add(object_ptr<Members>(
_members = result->add(object_ptr<Members>(
result,
_controller,
std::move(wrapValue),
_peer));
_peer)
);
_members->scrollToRequests()
| rpl::start([this](Ui::ScrollToRequest request) {
auto min = (request.ymin < 0)
? request.ymin
: mapFromGlobal(_members->mapToGlobal({ 0, request.ymin })).y();
auto max = (request.ymin < 0)
? mapFromGlobal(_members->mapToGlobal({ 0, 0 })).y()
: (request.ymax < 0)
? request.ymax
: mapFromGlobal(_members->mapToGlobal({ 0, request.ymax })).y();
_scrollToRequests.fire({ min, max });
}, _members->lifetime());
}
return std::move(result);
}
@ -367,6 +383,12 @@ object_ptr<Ui::SlideWrap<>> InnerWidget::createSlideSkipWidget(
return Ui::CreateSlideSkipWidget(parent, st::infoProfileSkip);
}
int InnerWidget::countDesiredHeight() const {
return _content->height() + (_members
? (_members->desiredHeight() - _members->height())
: 0);
}
void InnerWidget::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {

View File

@ -31,6 +31,7 @@ namespace Ui {
class VerticalLayout;
template <typename Widget>
class SlideWrap;
struct ScrollToRequest;
} // namespace Ui
namespace Info {
@ -40,6 +41,7 @@ enum class Wrap;
namespace Profile {
class Memento;
class Members;
class InnerWidget final : public Ui::RpWidget {
public:
@ -61,6 +63,14 @@ public:
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
return _scrollToRequests.events();
}
rpl::producer<int> desiredHeightValue() const override {
return _desiredHeight.events_starting_with(countDesiredHeight());
}
protected:
int resizeGetHeight(int newWidth) override;
void visibleTopBottomUpdated(
@ -70,7 +80,7 @@ protected:
private:
object_ptr<RpWidget> setupContent(
RpWidget *parent,
rpl::producer<Wrap> &&wrapValue) const;
rpl::producer<Wrap> &&wrapValue);
object_ptr<RpWidget> setupDetails(RpWidget *parent) const;
object_ptr<RpWidget> setupSharedMedia(RpWidget *parent) const;
object_ptr<RpWidget> setupMuteToggle(RpWidget *parent) const;
@ -86,6 +96,8 @@ private:
object_ptr<Ui::SlideWrap<RpWidget>> createSlideSkipWidget(
RpWidget *parent) const;
int countDesiredHeight() const;
bool canHideDetailsEver() const;
rpl::producer<bool> canHideDetails() const;
@ -94,8 +106,12 @@ private:
int _minHeight = 0;
Members *_members = nullptr;
object_ptr<RpWidget> _content;
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
rpl::event_stream<int> _desiredHeight;
};
} // namespace Profile

View File

@ -24,11 +24,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "info/profile/info_profile_values.h"
#include "info/profile/info_profile_icon.h"
#include "info/profile/info_profile_values.h"
#include "info/profile/info_profile_members_controllers.h"
#include "info/info_memento.h"
#include "profile/profile_block_group_members.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/scroll_area.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
#include "lang/lang_keys.h"
#include "boxes/confirm_box.h"
@ -44,10 +47,12 @@ constexpr auto kEnableSearchMembersAfterCount = 50;
Members::Members(
QWidget *parent,
not_null<Window::Controller*> controller,
rpl::producer<Wrap> &&wrapValue,
not_null<PeerData*> peer)
: RpWidget(parent)
, _peer(peer)
, _controller(CreateMembersController(_peer))
, _labelWrap(this)
, _label(setupHeader())
, _addMember(this, st::infoMembersAddMember)
@ -57,13 +62,30 @@ Members::Members(
langFactory(lng_participant_filter))
, _search(this, st::infoMembersSearch)
, _cancelSearch(this, st::infoMembersCancelSearch)
, _list(setupList(this)) {
, _list(setupList(this, _controller.get())) {
setupButtons();
std::move(wrapValue)
| rpl::start([this](Wrap wrap) {
_wrap = wrap;
updateSearchOverrides();
}, lifetime());
setContent(_list.data());
_controller->setDelegate(static_cast<PeerListDelegate*>(this));
}
int Members::desiredHeight() const {
auto desired = st::infoMembersHeader;
auto count = [this] {
if (auto chat = _peer->asChat()) {
return chat->count;
} else if (auto channel = _peer->asChannel()) {
return channel->membersCount();
}
return 0;
}();
desired += qMax(count, _list->fullRowsCount())
* st::infoMembersList.item.height;
return qMax(height(), desired);
}
object_ptr<Ui::FlatLabel> Members::setupHeader() {
@ -128,12 +150,24 @@ void Members::setupButtons() {
}
object_ptr<Members::ListWidget> Members::setupList(
RpWidget *parent) const {
RpWidget *parent,
not_null<PeerListController*> controller) const {
auto result = object_ptr<ListWidget>(
parent,
_peer,
::Profile::GroupMembersWidget::TitleVisibility::Hidden,
st::infoMembersItem);
controller,
st::infoMembersList);
result->scrollToRequests()
| rpl::start([this](Ui::ScrollToRequest request) {
auto addmin = (request.ymin < 0)
? 0
: st::infoMembersHeader;
auto addmax = (request.ymax < 0)
? 0
: st::infoMembersHeader;
_scrollToRequests.fire({
request.ymin + addmin,
request.ymax + addmax });
}, result->lifetime());
result->moveToLeft(0, st::infoMembersHeader);
parent->widthValue()
| rpl::start([list = result.data()](int newWidth) {
@ -141,7 +175,7 @@ object_ptr<Members::ListWidget> Members::setupList(
}, result->lifetime());
result->heightValue()
| rpl::start([parent](int listHeight) {
auto newHeight = (listHeight > 0)
auto newHeight = (listHeight > st::membersMarginBottom)
? (st::infoMembersHeader + listHeight)
: 0;
parent->resize(parent->width(), newHeight);
@ -182,6 +216,15 @@ int Members::resizeGetHeight(int newWidth) {
st::infoMembersSearchTop,
cancelLeft - fieldLeft,
_searchField->height());
connect(_searchField, &Ui::FlatInput::cancelled, this, [this] {
cancelSearch();
});
connect(_searchField, &Ui::FlatInput::changed, this, [this] {
applySearch();
});
connect(_searchField, &Ui::FlatInput::submitted, this, [this] {
forceSearchSubmit();
});
_labelWrap->resize(
searchCurrentLeft - st::infoBlockHeaderPosition.x(),
@ -267,7 +310,12 @@ void Members::cancelSearch() {
}
void Members::applySearch() {
peerListScrollToTop();
content()->searchQueryChanged(_searchField->getLastText());
}
void Members::forceSearchSubmit() {
content()->submitted();
}
void Members::visibleTopBottomUpdated(
@ -276,6 +324,42 @@ void Members::visibleTopBottomUpdated(
setChildVisibleTopBottom(_list, visibleTop, visibleBottom);
}
void Members::peerListSetTitle(base::lambda<QString()> title) {
}
void Members::peerListSetAdditionalTitle(
base::lambda<QString()> title) {
}
bool Members::peerListIsRowSelected(not_null<PeerData*> peer) {
return false;
}
int Members::peerListSelectedRowsCount() {
return 0;
}
std::vector<not_null<PeerData*>> Members::peerListCollectSelectedRows() {
return {};
}
void Members::peerListScrollToTop() {
_scrollToRequests.fire({ -1, -1 });
}
void Members::peerListAddSelectedRowInBunch(not_null<PeerData*> peer) {
Unexpected("Item selection in Info::Profile::Members.");
}
void Members::peerListFinishSelectedRowsBunch() {
}
void Members::peerListSetDescription(
object_ptr<Ui::FlatLabel> description) {
description.destroy();
}
} // namespace Profile
} // namespace Info

View File

@ -21,16 +21,19 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once
#include "ui/rp_widget.h"
#include "boxes/peer_list_box.h"
namespace Ui {
class FlatInput;
class CrossButton;
class IconButton;
class FlatLabel;
struct ScrollToRequest;
} // namespace Ui
namespace Profile {
class GroupMembersWidget;
class ParticipantsBoxController;
} // namespace Profile
namespace Info {
@ -39,13 +42,22 @@ enum class Wrap;
namespace Profile {
class Members : public Ui::RpWidget {
class Members
: public Ui::RpWidget
, private PeerListContentDelegate {
public:
Members(
QWidget *parent,
not_null<Window::Controller*> controller,
rpl::producer<Wrap> &&wrapValue,
not_null<PeerData*> peer);
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
return _scrollToRequests.events();
}
int desiredHeight() const;
protected:
void visibleTopBottomUpdated(
int visibleTop,
@ -53,11 +65,26 @@ protected:
int resizeGetHeight(int newWidth) override;
private:
using ListWidget = ::Profile::GroupMembersWidget;
using ListWidget = PeerListContent;
// PeerListContentDelegate interface.
void peerListSetTitle(base::lambda<QString()> title) override;
void peerListSetAdditionalTitle(
base::lambda<QString()> title) override;
bool peerListIsRowSelected(not_null<PeerData*> peer) override;
int peerListSelectedRowsCount() override;
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
void peerListScrollToTop() override;
void peerListAddSelectedRowInBunch(
not_null<PeerData*> peer) override;
void peerListFinishSelectedRowsBunch() override;
void peerListSetDescription(
object_ptr<Ui::FlatLabel> description) override;
object_ptr<Ui::FlatLabel> setupHeader();
object_ptr<ListWidget> setupList(
RpWidget *parent) const;
RpWidget *parent,
not_null<PeerListController*> controller) const;
void setupButtons();
void updateSearchOverrides();
@ -67,10 +94,12 @@ private:
void toggleSearch();
void cancelSearch();
void applySearch();
void forceSearchSubmit();
void searchAnimationCallback();
Wrap _wrap;
not_null<PeerData*> _peer;
std::unique_ptr<PeerListController> _controller;
object_ptr<Ui::RpWidget> _labelWrap;
object_ptr<Ui::FlatLabel> _label;
object_ptr<Ui::IconButton> _addMember;
@ -83,6 +112,8 @@ private:
bool _searchShown = false;
base::Timer _searchTimer;
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
};
} // namespace Profile

View File

@ -0,0 +1,124 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/profile/info_profile_members_controllers.h"
#include "profile/profile_channel_controllers.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "auth_session.h"
#include "observer_peer.h"
namespace Info {
namespace Profile {
namespace {
class ChatMembersController
: public PeerListController
, private base::Subscriber {
public:
ChatMembersController(not_null<ChatData*> chat);
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
private:
void rebuildRows();
std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user);
not_null<ChatData*> _chat;
};
ChatMembersController::ChatMembersController(not_null<ChatData*> chat)
: PeerListController()
, _chat(chat) {
}
void ChatMembersController::prepare() {
setSearchNoResultsText(lang(lng_blocked_list_not_found));
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
delegate()->peerListSetTitle(langFactory(lng_channel_admins));
rebuildRows();
if (!delegate()->peerListFullRowsCount()) {
Auth().api().requestFullPeer(_chat);
}
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(
Notify::PeerUpdate::Flag::MembersChanged,
[this](const Notify::PeerUpdate &update) {
if (update.peer == _chat) {
rebuildRows();
}
}));
}
void ChatMembersController::rebuildRows() {
if (_chat->participants.empty()) {
return;
}
std::vector<not_null<UserData*>> users;
auto &participants = _chat->participants;
for (auto i = participants.cbegin(), e = participants.cend();
i != e;
++i) {
users.push_back(i.key());
}
auto now = unixtime();
base::sort(users, [now](auto a, auto b) {
return App::onlineForSort(a, now)
> App::onlineForSort(b, now);
});
base::for_each(users, [this](not_null<UserData*> user) {
if (auto row = createRow(user)) {
delegate()->peerListAppendRow(std::move(row));
}
});
delegate()->peerListRefreshRows();
}
std::unique_ptr<PeerListRow> ChatMembersController::createRow(not_null<UserData*> user) {
return std::make_unique<PeerListRow>(user);
}
void ChatMembersController::rowClicked(not_null<PeerListRow*> row) {
Ui::showPeerProfile(row->peer());
}
} // namespace
std::unique_ptr<PeerListController> CreateMembersController(
not_null<PeerData*> peer) {
if (auto chat = peer->asChat()) {
return std::make_unique<ChatMembersController>(chat);
} else if (auto channel = peer->asChannel()) {
using ChannelMembersController
= ::Profile::ParticipantsBoxController;
return std::make_unique<ChannelMembersController>(
channel,
ChannelMembersController::Role::Profile);
}
Unexpected("Peer type in CreateMembersController()");
}
} // namespace Profile
} // namespace Info

View File

@ -0,0 +1,32 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
class PeerListController;
namespace Info {
namespace Profile {
std::unique_ptr<PeerListController> CreateMembersController(
not_null<PeerData*> peer);
} // namespace Profile
} // namespace Info

View File

@ -52,6 +52,15 @@ Widget::Widget(
controller,
peer));
_inner->move(0, 0);
_inner->scrollToRequests()
| rpl::start([this](Ui::ScrollToRequest request) {
if (request.ymin < 0) {
scrollTopRestore(
qMin(scrollTopSave(), request.ymax));
} else {
scrollTo(request);
}
}, lifetime());
}
Section Widget::section() const {

View File

@ -121,7 +121,7 @@ profileInviteLinkText: FlatLabel(profileBlockTextPart) {
profileLimitReachedSkip: 6px;
profileMemberItem: ProfilePeerListItem(defaultProfileMemberItem) {
profileMemberItem: PeerListItem(defaultPeerListItem) {
left: 8px;
bottom: profileBlockMarginBottom;
button: defaultLeftOutlineButton;

View File

@ -39,7 +39,7 @@ GroupMembersWidget::GroupMembersWidget(
QWidget *parent,
PeerData *peer,
TitleVisibility titleVisibility,
const style::ProfilePeerListItem &st)
const style::PeerListItem &st)
: PeerListWidget(parent
, peer
, (titleVisibility == TitleVisibility::Visible) ? lang(lng_profile_participants_section) : QString()

View File

@ -41,7 +41,7 @@ public:
Visible,
Hidden,
};
GroupMembersWidget(QWidget *parent, PeerData *peer, TitleVisibility titleVisibility = TitleVisibility::Visible, const style::ProfilePeerListItem &st = st::profileMemberItem);
GroupMembersWidget(QWidget *parent, PeerData *peer, TitleVisibility titleVisibility = TitleVisibility::Visible, const style::PeerListItem &st = st::profileMemberItem);
int onlineCount() const {
return _onlineCount;

View File

@ -33,7 +33,7 @@ PeerListWidget::Item::Item(PeerData *peer) : peer(peer) {
PeerListWidget::Item::~Item() = default;
PeerListWidget::PeerListWidget(QWidget *parent, PeerData *peer, const QString &title, const style::ProfilePeerListItem &st, const QString &removeText)
PeerListWidget::PeerListWidget(QWidget *parent, PeerData *peer, const QString &title, const style::PeerListItem &st, const QString &removeText)
: BlockWidget(parent, peer, title)
, _st(st)
, _removeText(removeText)

View File

@ -36,7 +36,7 @@ namespace Profile {
class PeerListWidget : public BlockWidget {
public:
PeerListWidget(QWidget *parent, PeerData *peer, const QString &title, const style::ProfilePeerListItem &st = st::profileMemberItem, const QString &removeText = QString());
PeerListWidget(QWidget *parent, PeerData *peer, const QString &title, const style::PeerListItem &st = st::profileMemberItem, const QString &removeText = QString());
struct Item {
explicit Item(PeerData *peer);
@ -136,7 +136,7 @@ private:
void paintItem(Painter &p, int x, int y, Item *item, bool selected, bool selectedRemove, TimeMs ms);
const style::ProfilePeerListItem &_st;
const style::PeerListItem &_st;
base::lambda<void()> _preloadMoreCallback;
base::lambda<void(PeerData*)> _selectedCallback;

View File

@ -38,7 +38,8 @@ constexpr auto kParticipantsPerPage = 200;
} // namespace
ParticipantsBoxController::ParticipantsBoxController(not_null<ChannelData*> channel, Role role) : PeerListController(CreateSearchController(channel, role, &_additional))
ParticipantsBoxController::ParticipantsBoxController(not_null<ChannelData*> channel, Role role)
: PeerListController(CreateSearchController(channel, role, &_additional))
, _channel(channel)
, _role(role) {
if (_channel->mgInfo) {
@ -46,10 +47,17 @@ ParticipantsBoxController::ParticipantsBoxController(not_null<ChannelData*> chan
}
}
std::unique_ptr<PeerListSearchController> ParticipantsBoxController::CreateSearchController(not_null<ChannelData*> channel, Role role, not_null<Additional*> additional) {
std::unique_ptr<PeerListSearchController>
ParticipantsBoxController::CreateSearchController(
not_null<ChannelData*> channel,
Role role,
not_null<Additional*> additional) {
// In admins box complex search is used for adding new admins.
if (role != Role::Admins || channel->canAddAdmins()) {
return std::make_unique<ParticipantsBoxSearchController>(channel, role, additional);
return std::make_unique<ParticipantsBoxSearchController>(
channel,
role,
additional);
}
return nullptr;
}
@ -86,6 +94,7 @@ void ParticipantsBoxController::Start(not_null<ChannelData*> channel, Role role)
}
void ParticipantsBoxController::addNewItem() {
Expects(_role != Role::Profile);
if (_role == Role::Members) {
if (_channel->membersCount() >= Global::ChatSizeMax()) {
Ui::show(
@ -131,7 +140,10 @@ std::unique_ptr<PeerListRow> ParticipantsBoxController::createSearchRow(not_null
template <typename Callback>
void ParticipantsBoxController::HandleParticipant(const MTPChannelParticipant &participant, Role role, not_null<Additional*> additional, Callback callback) {
if ((role == Role::Members || role == Role::Admins) && participant.type() == mtpc_channelParticipantAdmin) {
if ((role == Role::Profile
|| role == Role::Members
|| role == Role::Admins)
&& participant.type() == mtpc_channelParticipantAdmin) {
auto &admin = participant.c_channelParticipantAdmin();
if (auto user = App::userLoaded(admin.vuser_id.v)) {
additional->adminRights[user] = admin.vadmin_rights;
@ -152,13 +164,20 @@ void ParticipantsBoxController::HandleParticipant(const MTPChannelParticipant &p
}
callback(user);
}
} else if ((role == Role::Members || role == Role::Admins) && participant.type() == mtpc_channelParticipantCreator) {
} else if ((role == Role::Profile
|| role == Role::Members
|| role == Role::Admins)
&& participant.type() == mtpc_channelParticipantCreator) {
auto &creator = participant.c_channelParticipantCreator();
if (auto user = App::userLoaded(creator.vuser_id.v)) {
additional->creator = user;
callback(user);
}
} else if ((role == Role::Members || role == Role::Restricted || role == Role::Kicked) && participant.type() == mtpc_channelParticipantBanned) {
} else if ((role == Role::Profile
|| role == Role::Members
|| role == Role::Restricted
|| role == Role::Kicked)
&& participant.type() == mtpc_channelParticipantBanned) {
auto &banned = participant.c_channelParticipantBanned();
if (auto user = App::userLoaded(banned.vuser_id.v)) {
additional->restrictedRights[user] = banned.vbanned_rights;
@ -172,12 +191,16 @@ void ParticipantsBoxController::HandleParticipant(const MTPChannelParticipant &p
}
callback(user);
}
} else if (role == Role::Members && participant.type() == mtpc_channelParticipant) {
} else if ((role == Role::Profile
|| role == Role::Members)
&& participant.type() == mtpc_channelParticipant) {
auto &member = participant.c_channelParticipant();
if (auto user = App::userLoaded(member.vuser_id.v)) {
callback(user);
}
} else if (role == Role::Members && participant.type() == mtpc_channelParticipantSelf) {
} else if ((role == Role::Profile
|| role == Role::Members)
&& participant.type() == mtpc_channelParticipantSelf) {
auto &member = participant.c_channelParticipantSelf();
if (auto user = App::userLoaded(member.vuser_id.v)) {
callback(user);
@ -191,6 +214,7 @@ void ParticipantsBoxController::prepare() {
auto titleKey = [this] {
switch (_role) {
case Role::Admins: return lng_channel_admins;
case Role::Profile:
case Role::Members: return lng_profile_participants_section;
case Role::Restricted: return lng_restricted_list_title;
case Role::Kicked: return lng_banned_list_title;
@ -219,7 +243,7 @@ void ParticipantsBoxController::loadMoreRows() {
}
auto filter = [this] {
if (_role == Role::Members) {
if (_role == Role::Members || _role == Role::Profile) {
return MTP_channelParticipantsRecent();
} else if (_role == Role::Admins) {
return MTP_channelParticipantsAdmins();
@ -261,7 +285,8 @@ void ParticipantsBoxController::loadMoreRows() {
}
bool ParticipantsBoxController::feedMegagroupLastParticipants() {
if (_role != Role::Members || _offset > 0) {
if ((_role != Role::Members && _role != Role::Profile)
|| _offset > 0) {
return false;
}
auto megagroup = _channel->asMegagroup();
@ -322,7 +347,7 @@ void ParticipantsBoxController::rowActionClicked(not_null<PeerListRow*> row) {
auto user = row->peer()->asUser();
Expects(user != nullptr);
if (_role == Role::Members) {
if (_role == Role::Members || _role == Role::Profile) {
kickMember(user);
} else if (_role == Role::Admins) {
showAdmin(user);
@ -617,6 +642,7 @@ bool ParticipantsBoxSearchController::loadMoreRows() {
auto filter = [this] {
switch (_role) {
case Role::Admins: // Search for members, appoint as admin on found.
case Role::Profile:
case Role::Members: return MTP_channelParticipantsSearch(MTP_string(_query));
case Role::Restricted: return MTP_channelParticipantsBanned(MTP_string(_query));
case Role::Kicked: return MTP_channelParticipantsKicked(MTP_string(_query));

View File

@ -27,9 +27,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Profile {
// Viewing admins, banned or restricted users list with search.
class ParticipantsBoxController : public PeerListController, private base::Subscriber, private MTP::Sender, public base::enable_weak_from_this {
class ParticipantsBoxController
: public PeerListController
, private base::Subscriber
, private MTP::Sender
, public base::enable_weak_from_this {
public:
enum class Role {
Profile,
Members,
Admins,
Restricted,

View File

@ -39,11 +39,11 @@ public:
event_stream(event_stream &&other);
template <typename OtherValue>
void fire_forward(OtherValue &&value);
void fire(Value &&value) {
void fire_forward(OtherValue &&value) const;
void fire(Value &&value) const {
return fire_forward(std::move(value));
}
void fire_copy(const Value &value) {
void fire_copy(const Value &value) const {
return fire_forward(value);
}
producer<Value, no_error> events() const;
@ -76,7 +76,8 @@ inline event_stream<Value>::event_stream(event_stream &&other)
template <typename Value>
template <typename OtherValue>
inline void event_stream<Value>::fire_forward(OtherValue &&value) {
inline void event_stream<Value>::fire_forward(
OtherValue &&value) const {
if (!_consumers) {
return;
}

View File

@ -685,6 +685,10 @@ void ScrollArea::leaveEventHook(QEvent *e) {
return QScrollArea::leaveEvent(e);
}
void ScrollArea::scrollTo(ScrollToRequest request) {
scrollToY(request.ymin, request.ymax);
}
void ScrollArea::scrollToY(int toTop, int toBottom) {
myEnsureResized(widget());
myEnsureResized(this);

View File

@ -34,21 +34,29 @@ enum class TouchScrollState {
class ScrollArea;
struct ScrollToRequest {
ScrollToRequest(int ymin, int ymax)
: ymin(ymin)
, ymax(ymax) {
}
int ymin = 0;
int ymax = 0;
};
class ScrollShadow : public QWidget {
Q_OBJECT
public:
ScrollShadow(ScrollArea *parent, const style::ScrollArea *st);
void paintEvent(QPaintEvent *e);
public slots:
void changeVisibility(bool shown);
private:
const style::ScrollArea *_st;
};
@ -210,6 +218,8 @@ public:
return _scrollTopUpdated.events_starting_with(scrollTop());
}
void scrollTo(ScrollToRequest request);
protected:
bool eventFilter(QObject *obj, QEvent *e) override;

View File

@ -1052,7 +1052,7 @@ MediaPlayerButton {
ripple: RippleAnimation;
}
ProfilePeerListItem {
PeerListItem {
left: pixels;
bottom: pixels;
height: pixels;
@ -1068,31 +1068,43 @@ ProfilePeerListItem {
statusFgActive: color;
}
defaultProfileMemberItem: ProfilePeerListItem {
PeerList {
padding: margins;
item: PeerListItem;
}
defaultPeerListButton: OutlineButton {
outlineWidth: 0px;
textBg: windowBg;
textBgOver: windowBgOver;
textFg: windowSubTextFg;
textFgOver: windowSubTextFgOver;
font: normalFont;
padding: margins(11px, 5px, 11px, 5px);
ripple: defaultRippleAnimation;
}
defaultPeerListItem: PeerListItem {
height: 58px;
photoPosition: point(12px, 6px);
namePosition: point(68px, 11px);
statusPosition: point(68px, 31px);
photoSize: 46px;
button: OutlineButton {
outlineWidth: 0px;
textBg: windowBg;
textBgOver: windowBgOver;
textFg: windowSubTextFg;
textFgOver: windowSubTextFgOver;
font: normalFont;
padding: margins(11px, 5px, 11px, 5px);
ripple: defaultRippleAnimation;
}
button: defaultPeerListButton;
statusFg: windowSubTextFg;
statusFgOver: windowSubTextFgOver;
statusFgActive: windowActiveTextFg;
}
defaultPeerList: PeerList {
padding: margins(0px, 0px, 0px, 0px);
item: defaultPeerListItem;
}
InfoTopBar {
height: pixels;
back: IconButton;

View File

@ -233,6 +233,8 @@
<(src_loc)/info/profile/info_profile_inner_widget.h
<(src_loc)/info/profile/info_profile_members.cpp
<(src_loc)/info/profile/info_profile_members.h
<(src_loc)/info/profile/info_profile_members_controllers.cpp
<(src_loc)/info/profile/info_profile_members_controllers.h
<(src_loc)/info/profile/info_profile_text.cpp
<(src_loc)/info/profile/info_profile_text.h
<(src_loc)/info/profile/info_profile_values.cpp