2016-10-23 21:03:10 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
2018-01-03 10:23:14 +00:00
|
|
|
the official desktop application for the Telegram messaging service.
|
2016-10-23 21:03:10 +00:00
|
|
|
|
2018-01-03 10:23:14 +00:00
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
2016-10-23 21:03:10 +00:00
|
|
|
*/
|
|
|
|
#include "profile/profile_block_peer_list.h"
|
|
|
|
|
2016-12-05 11:01:08 +00:00
|
|
|
#include "ui/effects/ripple_animation.h"
|
2020-10-10 09:15:37 +00:00
|
|
|
#include "ui/text/text_options.h"
|
2022-09-16 20:23:27 +00:00
|
|
|
#include "ui/painter.h"
|
2019-01-04 11:09:48 +00:00
|
|
|
#include "data/data_peer.h"
|
2020-05-28 14:32:10 +00:00
|
|
|
#include "data/data_cloud_file.h"
|
2019-07-24 11:45:24 +00:00
|
|
|
#include "main/main_session.h"
|
2016-10-23 21:03:10 +00:00
|
|
|
#include "styles/style_profile.h"
|
|
|
|
#include "styles/style_widgets.h"
|
|
|
|
|
|
|
|
namespace Profile {
|
|
|
|
|
2020-05-28 14:32:10 +00:00
|
|
|
PeerListWidget::Item::Item(not_null<PeerData*> peer) : peer(peer) {
|
2016-12-05 11:01:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PeerListWidget::Item::~Item() = default;
|
|
|
|
|
2020-06-08 15:17:33 +00:00
|
|
|
PeerListWidget::PeerListWidget(
|
|
|
|
QWidget *parent,
|
|
|
|
PeerData *peer,
|
|
|
|
const QString &title,
|
|
|
|
const style::PeerListItem &st,
|
|
|
|
const QString &removeText)
|
2016-10-23 21:03:10 +00:00
|
|
|
: BlockWidget(parent, peer, title)
|
2016-12-09 18:56:01 +00:00
|
|
|
, _st(st)
|
2016-10-23 21:03:10 +00:00
|
|
|
, _removeText(removeText)
|
|
|
|
, _removeWidth(st::normalFont->width(_removeText)) {
|
|
|
|
setMouseTracking(true);
|
2020-07-01 08:03:34 +00:00
|
|
|
|
|
|
|
peer->session().downloaderTaskFinished(
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
update();
|
|
|
|
}, lifetime());
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int PeerListWidget::resizeGetHeight(int newWidth) {
|
|
|
|
auto newHeight = getListTop();
|
|
|
|
|
2017-09-25 16:06:53 +00:00
|
|
|
newHeight += _items.size() * _st.height;
|
2016-10-23 21:03:10 +00:00
|
|
|
|
2016-12-19 16:51:29 +00:00
|
|
|
return newHeight + _st.bottom;
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
|
2017-09-13 16:57:44 +00:00
|
|
|
void PeerListWidget::visibleTopBottomUpdated(int visibleTop, int visibleBottom) {
|
2016-10-23 21:03:10 +00:00
|
|
|
_visibleTop = visibleTop;
|
|
|
|
_visibleBottom = visibleBottom;
|
|
|
|
|
|
|
|
if (_preloadMoreCallback) {
|
|
|
|
if (_visibleTop + PreloadHeightsCount * (_visibleBottom - _visibleTop) > height()) {
|
|
|
|
_preloadMoreCallback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerListWidget::paintContents(Painter &p) {
|
2016-12-05 11:01:08 +00:00
|
|
|
auto left = getListLeft();
|
|
|
|
auto top = getListTop();
|
|
|
|
|
2021-09-03 11:09:57 +00:00
|
|
|
const auto count = int(_items.size());
|
|
|
|
auto from = floorclamp(_visibleTop - top, _st.height, 0, count);
|
|
|
|
auto to = ceilclamp(_visibleBottom - top, _st.height, 0, count);
|
2016-12-05 11:01:08 +00:00
|
|
|
for (auto i = from; i < to; ++i) {
|
2017-09-25 16:06:53 +00:00
|
|
|
auto y = top + i * _st.height;
|
2019-01-08 13:57:22 +00:00
|
|
|
auto selected = (_pressed >= 0) ? (i == _pressed) : (i == _selected);
|
2016-12-05 11:01:08 +00:00
|
|
|
auto selectedRemove = selected && _selectedRemove;
|
|
|
|
if (_pressed >= 0 && !_pressedRemove) {
|
|
|
|
selectedRemove = false;
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
2019-04-02 09:13:30 +00:00
|
|
|
paintItem(p, left, y, _items[i], selected, selectedRemove);
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-02 09:13:30 +00:00
|
|
|
void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool selected, bool selectedKick) {
|
2016-10-23 21:03:10 +00:00
|
|
|
if (_updateItemCallback) {
|
|
|
|
_updateItemCallback(item);
|
|
|
|
}
|
|
|
|
|
2016-12-05 11:01:08 +00:00
|
|
|
auto memberRowWidth = rowWidth();
|
2016-10-23 21:03:10 +00:00
|
|
|
if (selected) {
|
2019-08-26 14:17:00 +00:00
|
|
|
paintItemRect(p, x, y, memberRowWidth, _st.height);
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
2016-12-05 11:01:08 +00:00
|
|
|
if (auto &ripple = item->ripple) {
|
2019-08-26 14:17:00 +00:00
|
|
|
ripple->paint(p, x, y, width());
|
2016-12-05 11:01:08 +00:00
|
|
|
if (ripple->empty()) {
|
|
|
|
ripple.reset();
|
|
|
|
}
|
|
|
|
}
|
2017-09-25 16:06:53 +00:00
|
|
|
int skip = _st.photoPosition.x();
|
2016-10-23 21:03:10 +00:00
|
|
|
|
2020-05-28 14:32:10 +00:00
|
|
|
item->peer->paintUserpicLeft(p, item->userpic, x + _st.photoPosition.x(), y + _st.photoPosition.y(), width(), _st.photoSize);
|
2016-10-23 21:03:10 +00:00
|
|
|
|
|
|
|
if (item->name.isEmpty()) {
|
2017-12-28 13:06:06 +00:00
|
|
|
item->name.setText(
|
2022-09-30 07:25:02 +00:00
|
|
|
st::semiboldTextStyle,
|
2022-08-09 11:12:19 +00:00
|
|
|
item->peer->name(),
|
2017-12-28 13:06:06 +00:00
|
|
|
Ui::NameTextOptions());
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
2017-09-25 16:06:53 +00:00
|
|
|
int nameLeft = x + _st.namePosition.x();
|
|
|
|
int nameTop = y + _st.namePosition.y();
|
|
|
|
int nameWidth = memberRowWidth - _st.namePosition.x() - skip;
|
2016-10-23 21:03:10 +00:00
|
|
|
if (item->hasRemoveLink && selected) {
|
|
|
|
p.setFont(selectedKick ? st::normalFont->underline() : st::normalFont);
|
|
|
|
p.setPen(st::windowActiveTextFg);
|
|
|
|
p.drawTextLeft(nameLeft + nameWidth - _removeWidth, nameTop, width(), _removeText, _removeWidth);
|
|
|
|
nameWidth -= _removeWidth + skip;
|
2022-06-08 12:53:16 +00:00
|
|
|
} else if (item->rank) {
|
|
|
|
p.setFont(st::normalFont);
|
|
|
|
p.setPen(st::windowActiveTextFg);
|
|
|
|
p.drawTextLeft(nameLeft + nameWidth - item->rankWidth, nameTop, width(), *item->rank, item->rankWidth);
|
|
|
|
nameWidth -= item->rankWidth + skip;
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
p.setPen(st::profileMemberNameFg);
|
|
|
|
item->name.drawLeftElided(p, nameLeft, nameTop, nameWidth, width());
|
|
|
|
|
|
|
|
if (item->statusHasOnlineColor) {
|
2016-12-09 18:56:01 +00:00
|
|
|
p.setPen(_st.statusFgActive);
|
2016-10-23 21:03:10 +00:00
|
|
|
} else {
|
2016-12-09 18:56:01 +00:00
|
|
|
p.setPen(selected ? _st.statusFgOver : _st.statusFg);
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
p.setFont(st::normalFont);
|
2017-09-25 16:06:53 +00:00
|
|
|
p.drawTextLeft(x + _st.statusPosition.x(), y + _st.statusPosition.y(), width(), item->statusText);
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
|
2019-08-26 14:17:00 +00:00
|
|
|
void PeerListWidget::paintItemRect(Painter &p, int x, int y, int w, int h) const {
|
2019-09-13 12:22:54 +00:00
|
|
|
p.fillRect(style::rtlrect(x, y, w, h, width()), _st.button.textBgOver);
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PeerListWidget::mouseMoveEvent(QMouseEvent *e) {
|
|
|
|
_mousePosition = e->globalPos();
|
|
|
|
updateSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerListWidget::mousePressEvent(QMouseEvent *e) {
|
|
|
|
_mousePosition = e->globalPos();
|
|
|
|
updateSelection();
|
|
|
|
|
2017-01-11 05:30:51 +00:00
|
|
|
_pressButton = e->button();
|
2016-10-23 21:03:10 +00:00
|
|
|
_pressed = _selected;
|
|
|
|
_pressedRemove = _selectedRemove;
|
2016-12-05 11:01:08 +00:00
|
|
|
if (_pressed >= 0 && !_pressedRemove) {
|
2021-09-03 11:09:57 +00:00
|
|
|
const auto item = _items[_pressed];
|
2016-12-05 11:01:08 +00:00
|
|
|
if (!item->ripple) {
|
|
|
|
auto memberRowWidth = rowWidth();
|
2022-10-03 11:11:05 +00:00
|
|
|
auto mask = Ui::RippleAnimation::RectMask(QSize(memberRowWidth, _st.height));
|
2017-02-21 13:45:56 +00:00
|
|
|
item->ripple = std::make_unique<Ui::RippleAnimation>(_st.button.ripple, std::move(mask), [this, index = _pressed] {
|
2016-12-05 11:01:08 +00:00
|
|
|
repaintRow(index);
|
|
|
|
});
|
|
|
|
}
|
2019-08-26 14:17:00 +00:00
|
|
|
auto left = getListLeft();
|
2017-09-25 16:06:53 +00:00
|
|
|
auto top = getListTop() + _st.height * _pressed;
|
2016-12-05 11:01:08 +00:00
|
|
|
item->ripple->add(e->pos() - QPoint(left, top));
|
|
|
|
}
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PeerListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
2017-01-11 05:30:51 +00:00
|
|
|
mousePressReleased(e->button());
|
|
|
|
}
|
2016-10-23 21:03:10 +00:00
|
|
|
|
2017-01-11 05:30:51 +00:00
|
|
|
void PeerListWidget::mousePressReleased(Qt::MouseButton button) {
|
2016-12-05 11:01:08 +00:00
|
|
|
repaintRow(_pressed);
|
2017-02-25 16:44:02 +00:00
|
|
|
auto pressed = std::exchange(_pressed, -1);
|
2016-12-05 11:01:08 +00:00
|
|
|
auto pressedRemove = base::take(_pressedRemove);
|
|
|
|
if (pressed >= 0 && pressed < _items.size()) {
|
2021-09-03 11:09:57 +00:00
|
|
|
if (const auto &ripple = _items[pressed]->ripple) {
|
2016-12-05 11:01:08 +00:00
|
|
|
ripple->lastStop();
|
|
|
|
}
|
2017-01-11 05:30:51 +00:00
|
|
|
if (pressed == _selected && pressedRemove == _selectedRemove && button == Qt::LeftButton) {
|
2017-05-26 20:05:04 +00:00
|
|
|
InvokeQueued(this, [this, pressedRemove, peer = _items[pressed]->peer] {
|
|
|
|
if (auto &callback = (pressedRemove ? _removedCallback : _selectedCallback)) {
|
|
|
|
callback(peer);
|
|
|
|
}
|
|
|
|
});
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
setCursor(_selectedRemove ? style::cur_pointer : style::cur_default);
|
|
|
|
repaintSelectedRow();
|
|
|
|
}
|
|
|
|
|
2021-10-19 13:00:21 +00:00
|
|
|
void PeerListWidget::enterEventHook(QEnterEvent *e) {
|
2016-10-23 21:03:10 +00:00
|
|
|
_mousePosition = QCursor::pos();
|
|
|
|
updateSelection();
|
|
|
|
}
|
|
|
|
|
2017-02-11 11:24:37 +00:00
|
|
|
void PeerListWidget::leaveEventHook(QEvent *e) {
|
2016-10-23 21:03:10 +00:00
|
|
|
_mousePosition = QPoint(-1, -1);
|
|
|
|
updateSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerListWidget::updateSelection() {
|
2016-12-05 11:01:08 +00:00
|
|
|
auto selected = -1;
|
|
|
|
auto selectedKick = false;
|
2016-10-23 21:03:10 +00:00
|
|
|
|
|
|
|
auto mouse = mapFromGlobal(_mousePosition);
|
|
|
|
if (rtl()) mouse.setX(width() - mouse.x());
|
2016-12-05 11:01:08 +00:00
|
|
|
auto left = getListLeft();
|
|
|
|
auto top = getListTop();
|
|
|
|
auto memberRowWidth = rowWidth();
|
2016-10-23 21:03:10 +00:00
|
|
|
if (mouse.x() >= left && mouse.x() < left + memberRowWidth && mouse.y() >= top) {
|
2017-09-25 16:06:53 +00:00
|
|
|
selected = (mouse.y() - top) / _st.height;
|
2016-10-23 21:03:10 +00:00
|
|
|
if (selected >= _items.size()) {
|
|
|
|
selected = -1;
|
|
|
|
} else if (_items[selected]->hasRemoveLink) {
|
2017-09-25 16:06:53 +00:00
|
|
|
int skip = _st.photoPosition.x();
|
|
|
|
int nameLeft = left + _st.namePosition.x();
|
|
|
|
int nameTop = top + _selected * _st.height + _st.namePosition.y();
|
|
|
|
int nameWidth = memberRowWidth - _st.namePosition.x() - skip;
|
2016-10-23 21:03:10 +00:00
|
|
|
if (mouse.x() >= nameLeft + nameWidth - _removeWidth && mouse.x() < nameLeft + nameWidth) {
|
|
|
|
if (mouse.y() >= nameTop && mouse.y() < nameTop + st::normalFont->height) {
|
|
|
|
selectedKick = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setSelected(selected, selectedKick);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerListWidget::setSelected(int selected, bool selectedRemove) {
|
|
|
|
if (_selected == selected && _selectedRemove == selectedRemove) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
repaintSelectedRow();
|
|
|
|
if (_selectedRemove != selectedRemove) {
|
|
|
|
_selectedRemove = selectedRemove;
|
|
|
|
if (_pressed < 0) {
|
|
|
|
setCursor(_selectedRemove ? style::cur_pointer : style::cur_default);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_selected != selected) {
|
|
|
|
_selected = selected;
|
|
|
|
repaintSelectedRow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerListWidget::repaintSelectedRow() {
|
2016-12-05 11:01:08 +00:00
|
|
|
repaintRow(_selected);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerListWidget::repaintRow(int index) {
|
|
|
|
if (index >= 0) {
|
|
|
|
auto left = getListLeft();
|
2017-09-25 16:06:53 +00:00
|
|
|
rtlupdate(left, getListTop() + index * _st.height, width() - left, _st.height);
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int PeerListWidget::getListLeft() const {
|
2016-12-19 16:51:29 +00:00
|
|
|
return _st.left;
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
|
2016-12-05 11:01:08 +00:00
|
|
|
int PeerListWidget::rowWidth() const {
|
2017-09-25 16:06:53 +00:00
|
|
|
return _st.maximalWidth
|
|
|
|
? qMin(width() - getListLeft(), _st.maximalWidth)
|
|
|
|
: width() - getListLeft();
|
2016-12-05 11:01:08 +00:00
|
|
|
}
|
|
|
|
|
2016-10-23 21:03:10 +00:00
|
|
|
void PeerListWidget::preloadPhotos() {
|
|
|
|
int top = getListTop();
|
|
|
|
int preloadFor = (_visibleBottom - _visibleTop) * PreloadHeightsCount;
|
2017-09-25 16:06:53 +00:00
|
|
|
int from = floorclamp(_visibleTop - top, _st.height, 0, _items.size());
|
|
|
|
int to = ceilclamp(_visibleBottom + preloadFor - top, _st.height, 0, _items.size());
|
2016-10-23 21:03:10 +00:00
|
|
|
for (int i = from; i < to; ++i) {
|
|
|
|
_items[i]->peer->loadUserpic();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerListWidget::refreshVisibility() {
|
2021-09-03 11:09:57 +00:00
|
|
|
setVisible(!_items.empty());
|
2016-10-23 21:03:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Profile
|