Show members list in PanelMode::Wide.

This commit is contained in:
John Preston 2021-04-23 15:07:12 +04:00
parent c93ddf6aac
commit eb8f709943
15 changed files with 231 additions and 121 deletions

View File

@ -729,10 +729,6 @@ termsAgePadding: margins(22px, 16px, 16px, 0px);
themesSmallSkip: 10px;
themesBackgroundSize: 120px;
themesScroll: ScrollArea(defaultScrollArea) {
bottomsh: 0px;
topsh: 0px;
}
themesMenuToggle: IconButton(defaultIconButton) {
width: 44px;
height: 44px;

View File

@ -529,7 +529,7 @@ QString PeerListRow::generateShortName() {
: peer()->shortName();
}
std::shared_ptr<Data::CloudImageView> PeerListRow::ensureUserpicView() {
std::shared_ptr<Data::CloudImageView> &PeerListRow::ensureUserpicView() {
if (!_userpic) {
_userpic = peer()->createUserpicView();
}
@ -588,11 +588,14 @@ void PeerListRow::paintStatusText(
_status.drawLeftElided(p, x, y, availableWidth, outerWidth);
}
template <typename UpdateCallback>
void PeerListRow::addRipple(const style::PeerListItem &st, QSize size, QPoint point, UpdateCallback updateCallback) {
template <typename MaskGenerator, typename UpdateCallback>
void PeerListRow::addRipple(const style::PeerListItem &st, MaskGenerator &&maskGenerator, QPoint point, UpdateCallback &&updateCallback) {
if (!_ripple) {
auto mask = Ui::RippleAnimation::rectMask(size);
_ripple = std::make_unique<Ui::RippleAnimation>(st.button.ripple, std::move(mask), std::move(updateCallback));
auto mask = maskGenerator();
if (mask.isNull()) {
return;
}
_ripple = std::make_unique<Ui::RippleAnimation>(st.button.ripple, std::move(mask), std::forward<UpdateCallback>(updateCallback));
}
_ripple->add(point);
}
@ -1241,9 +1244,16 @@ void PeerListContent::mousePressEvent(QMouseEvent *e) {
row->addActionRipple(point, std::move(updateCallback));
}
} else {
auto size = QSize(width(), _rowHeight);
auto point = mapFromGlobal(QCursor::pos()) - QPoint(0, getRowTop(_selected.index));
row->addRipple(_st.item, size, point, std::move(updateCallback));
if (_mode == Mode::Custom) {
row->addRipple(_st.item, _controller->customRowRippleMaskGenerator(), point, std::move(updateCallback));
} else {
const auto maskGenerator = [&] {
return Ui::RippleAnimation::rectMask(
QSize(width(), _rowHeight));
};
row->addRipple(_st.item, maskGenerator, point, std::move(updateCallback));
}
}
}
if (anim::Disabled()) {
@ -1779,7 +1789,11 @@ void PeerListContent::selectByMouse(QPoint globalPosition) {
auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(globalPosition));
auto selected = Selected();
auto rowsPointY = point.y() - rowsTop();
selected.index.value = (in && rowsPointY >= 0 && rowsPointY < shownRowsCount() * _rowHeight) ? (rowsPointY / _rowHeight) : -1;
selected.index.value = (in
&& rowsPointY >= 0
&& rowsPointY < shownRowsCount() * _rowHeight)
? (rowsPointY / _rowHeight)
: -1;
if (selected.index.value >= 0) {
const auto row = getRow(selected.index);
if (row->disabled()
@ -1787,7 +1801,7 @@ void PeerListContent::selectByMouse(QPoint globalPosition) {
&& !_controller->customRowSelectionPoint(
row,
point.x(),
rowsPointY))) {
rowsPointY - (selected.index.value * _rowHeight)))) {
selected = Selected();
} else if (!customMode) {
if (getActiveActionRect(row, selected.index).contains(point)) {

View File

@ -82,7 +82,7 @@ public:
return _id;
}
[[nodiscard]] std::shared_ptr<Data::CloudImageView> ensureUserpicView();
[[nodiscard]] std::shared_ptr<Data::CloudImageView> &ensureUserpicView();
[[nodiscard]] virtual QString generateName();
[[nodiscard]] virtual QString generateShortName();
@ -172,12 +172,12 @@ public:
void finishCheckedAnimation();
void invalidatePixmapsCache();
template <typename UpdateCallback>
template <typename MaskGenerator, typename UpdateCallback>
void addRipple(
const style::PeerListItem &st,
QSize size,
MaskGenerator &&maskGenerator,
QPoint point,
UpdateCallback updateCallback);
UpdateCallback &&updateCallback);
void stopLastRipple();
void paintRipple(Painter &p, int x, int y, int outerWidth);
void paintUserpic(
@ -451,21 +451,23 @@ public:
return false;
}
[[nodiscard]] virtual int customRowHeight() {
Unexpected("Unimplemented PeerListController::customRowHeight.");
Unexpected("PeerListController::customRowHeight.");
}
virtual void customRowPaint(
Painter &p,
crl::time now,
not_null<PeerListRow*> row,
bool selected) {
Unexpected("Unimplemented PeerListController::customRowPaint.");
Unexpected("PeerListController::customRowPaint.");
}
[[nodiscard]] virtual bool customRowSelectionPoint(
not_null<PeerListRow*> row,
int x,
int y) {
Unexpected(
"Unimplemented PeerListController::customRowSelectionPoint.");
Unexpected("PeerListController::customRowSelectionPoint.");
}
[[nodiscard]] virtual Fn<QImage()> customRowRippleMaskGenerator() {
Unexpected("PeerListController::customRowRippleMaskGenerator.");
}
[[nodiscard]] virtual rpl::producer<int> onlineCountValue() const;

View File

@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_utilities.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/cross_line.h"
#include "ui/round_rect.h"
#include "core/application.h" // Core::App().domain, Core::App().activeWindow.
#include "main/main_domain.h" // Core::App().domain().activate.
#include "main/main_session.h"
@ -97,6 +98,7 @@ public:
Painter &p,
QRect rect,
IconState state) = 0;
virtual void rowPaintWideBackground(Painter &p, bool selected) = 0;
};
class Row final : public PeerListRow {
@ -174,7 +176,15 @@ public:
bool selected,
bool actionSelected) override;
auto generatePaintUserpicCallback() -> PaintRoundImageCallback override;
PaintRoundImageCallback generatePaintUserpicCallback() override;
void paintComplexUserpic(
Painter &p,
int x,
int y,
int outerWidth,
int size,
PanelMode mode,
bool selected = false);
void paintStatusText(
Painter &p,
@ -243,6 +253,20 @@ private:
void ensureUserpicCache(
std::shared_ptr<Data::CloudImageView> &view,
int size);
bool paintVideo(Painter &p, int x, int y, int size, PanelMode mode);
[[nodiscard]] static std::tuple<int, int, int> UserpicInWideMode(
int x,
int y,
int size);
void paintBlobs(Painter &p, int x, int y, int size, PanelMode mode);
void paintScaledUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &userpic,
int x,
int y,
int outerWidth,
int size,
PanelMode mode);
const not_null<RowDelegate*> _delegate;
State _state = State::Inactive;
@ -305,6 +329,7 @@ public:
Painter &p,
QRect rect,
IconState state) override;
void rowPaintWideBackground(Painter &p, bool selected) override;
int customRowHeight() override;
void customRowPaint(
@ -316,6 +341,7 @@ public:
not_null<PeerListRow*> row,
int x,
int y) override;
Fn<QImage()> customRowRippleMaskGenerator() override;
private:
[[nodiscard]] std::unique_ptr<Row> createRowForMe();
@ -383,6 +409,8 @@ private:
Ui::CrossLineAnimation _inactiveCrossLine;
Ui::CrossLineAnimation _coloredCrossLine;
Ui::RoundRect _wideRoundRectSelected;
Ui::RoundRect _wideRoundRect;
rpl::lifetime _lifetime;
@ -653,78 +681,142 @@ void Row::ensureUserpicCache(
}
}
auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback {
auto userpic = ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
const auto videoSize = _videoTrackShown
? _videoTrackShown->frameSize()
: QSize();
if (!videoSize.isEmpty()) {
const auto resize = (videoSize.width() > videoSize.height())
? QSize(videoSize.width() * size / videoSize.height(), size)
: QSize(size, videoSize.height() * size / videoSize.width());
const auto request = Webrtc::FrameRequest{
.resize = resize * cIntRetinaFactor(),
.outer = QSize(size, size) * cIntRetinaFactor(),
};
const auto frame = _videoTrackShown->frame(request);
auto copy = frame; // #TODO calls optimize.
copy.detach();
Images::prepareCircle(copy);
p.drawImage(
QRect(QPoint(x, y), copy.size() / cIntRetinaFactor()),
copy);
_videoTrackShown->markFrameShown();
return;
} else if (_videoTrackShown) {
// We could skip the first notification.
_videoTrackShown->markFrameShown();
}
if (_blobsAnimation) {
const auto mutedByMe = (_state == State::MutedByMe);
const auto shift = QPointF(x + size / 2., y + size / 2.);
auto hq = PainterHighQualityEnabler(p);
p.translate(shift);
const auto brush = mutedByMe
? st::groupCallMemberMutedIcon->b
: anim::brush(
st::groupCallMemberInactiveStatus,
st::groupCallMemberActiveStatus,
_speakingAnimation.value(_speaking ? 1. : 0.));
_blobsAnimation->blobs.paint(p, brush);
p.translate(-shift);
p.setOpacity(1.);
const auto enter = _blobsAnimation->enter;
const auto &minScale = kUserpicMinScale;
const auto scaleUserpic = minScale
+ (1. - minScale) * _blobsAnimation->blobs.currentLevel();
const auto scale = scaleUserpic * enter + 1. * (1. - enter);
if (scale == 1.) {
peer()->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
} else {
ensureUserpicCache(userpic, size);
PainterHighQualityEnabler hq(p);
auto target = QRect(
x + (1 - kWideScale) / 2 * size,
y + (1 - kWideScale) / 2 * size,
kWideScale * size,
kWideScale * size);
auto shrink = anim::interpolate(
(1 - kWideScale) / 2 * size,
0,
scale);
auto margins = QMargins(shrink, shrink, shrink, shrink);
p.drawImage(
target.marginsAdded(margins),
_blobsAnimation->userpicCache);
}
} else {
peer()->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
}
bool Row::paintVideo(Painter &p, int x, int y, int size, PanelMode mode) {
if (!_videoTrackShown) {
return false;
}
const auto guard = gsl::finally([&] {
_videoTrackShown->markFrameShown();
});
const auto videoSize = _videoTrackShown->frameSize();
if (videoSize.isEmpty()) {
return false;
}
const auto resize = (videoSize.width() > videoSize.height())
? QSize(videoSize.width() * size / videoSize.height(), size)
: QSize(size, videoSize.height() * size / videoSize.width());
const auto request = Webrtc::FrameRequest{
.resize = resize * cIntRetinaFactor(),
.outer = QSize(size, size) * cIntRetinaFactor(),
};
const auto frame = _videoTrackShown->frame(request);
auto copy = frame; // #TODO calls optimize.
copy.detach();
if (mode == PanelMode::Default) {
Images::prepareCircle(copy);
} else {
Images::prepareRound(copy, ImageRoundRadius::Large);
}
p.drawImage(
QRect(QPoint(x, y), copy.size() / cIntRetinaFactor()),
copy);
return true;
}
std::tuple<int, int, int> Row::UserpicInWideMode(int x, int y, int size) {
const auto useSize = st::groupCallMembersList.item.photoSize;
const auto skip = (size - useSize) / 2;
return { x + skip, y + skip, useSize };
}
void Row::paintBlobs(Painter &p, int x, int y, int size, PanelMode mode) {
if (!_blobsAnimation) {
return;
}
if (mode == PanelMode::Wide) {
std::tie(x, y, size) = UserpicInWideMode(x, y, size);
}
const auto mutedByMe = (_state == State::MutedByMe);
const auto shift = QPointF(x + size / 2., y + size / 2.);
auto hq = PainterHighQualityEnabler(p);
p.translate(shift);
const auto brush = mutedByMe
? st::groupCallMemberMutedIcon->b
: anim::brush(
st::groupCallMemberInactiveStatus,
st::groupCallMemberActiveStatus,
_speakingAnimation.value(_speaking ? 1. : 0.));
_blobsAnimation->blobs.paint(p, brush);
p.translate(-shift);
p.setOpacity(1.);
}
void Row::paintScaledUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &userpic,
int x,
int y,
int outerWidth,
int size,
PanelMode mode) {
if (mode == PanelMode::Wide) {
std::tie(x, y, size) = UserpicInWideMode(x, y, size);
}
if (!_blobsAnimation) {
peer()->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
return;
}
const auto enter = _blobsAnimation->enter;
const auto &minScale = kUserpicMinScale;
const auto scaleUserpic = minScale
+ (1. - minScale) * _blobsAnimation->blobs.currentLevel();
const auto scale = scaleUserpic * enter + 1. * (1. - enter);
if (scale == 1.) {
peer()->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
return;
}
ensureUserpicCache(userpic, size);
PainterHighQualityEnabler hq(p);
auto target = QRect(
x + (1 - kWideScale) / 2 * size,
y + (1 - kWideScale) / 2 * size,
kWideScale * size,
kWideScale * size);
auto shrink = anim::interpolate(
(1 - kWideScale) / 2 * size,
0,
scale);
auto margins = QMargins(shrink, shrink, shrink, shrink);
p.drawImage(
target.marginsAdded(margins),
_blobsAnimation->userpicCache);
}
auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback {
return [=](Painter &p, int x, int y, int outerWidth, int size) {
paintComplexUserpic(p, x, y, outerWidth, size, PanelMode::Default);
};
}
void Row::paintComplexUserpic(
Painter &p,
int x,
int y,
int outerWidth,
int size,
PanelMode mode,
bool selected) {
if (mode == PanelMode::Wide) {
if (paintVideo(p, x, y, size, mode)) {
return;
}
_delegate->rowPaintWideBackground(p, selected);
paintRipple(p, x, y, outerWidth);
}
paintBlobs(p, x, y, size, mode);
if (mode == PanelMode::Default && paintVideo(p, x, y, size, mode)) {
return;
}
paintScaledUserpic(
p,
ensureUserpicView(),
x,
y,
outerWidth,
size,
mode);
}
int Row::statusIconWidth() const {
@ -976,7 +1068,9 @@ MembersController::MembersController(
, _menuParent(menuParent)
, _raisedHandStatusRemoveTimer([=] { scheduleRaisedHandStatusRemove(); })
, _inactiveCrossLine(st::groupCallMemberInactiveCrossLine)
, _coloredCrossLine(st::groupCallMemberColoredCrossLine) {
, _coloredCrossLine(st::groupCallMemberColoredCrossLine)
, _wideRoundRectSelected(ImageRoundRadius::Large, st::groupCallMembersBgOver)
, _wideRoundRect(ImageRoundRadius::Large, st::groupCallMembersBg) {
setupListChangeViewers();
style::PaletteChanged(
@ -1605,6 +1699,12 @@ void MembersController::rowPaintIcon(
_inactiveCrossLine.paint(p, left, top, crossProgress, iconColor);
}
void MembersController::rowPaintWideBackground(Painter &p, bool selected) {
(selected ? _wideRoundRectSelected : _wideRoundRect).paint(
p,
{ QPoint(), st::groupCallNarrowSize });
}
int MembersController::customRowHeight() {
return st::groupCallNarrowSize.height() + st::groupCallNarrowRowSkip;
}
@ -1615,6 +1715,15 @@ void MembersController::customRowPaint(
not_null<PeerListRow*> row,
bool selected) {
const auto real = static_cast<Row*>(row.get());
const auto width = st::groupCallNarrowSize.width();
real->paintComplexUserpic(
p,
0,
0,
width,
width,
PanelMode::Wide,
selected);
}
bool MembersController::customRowSelectionPoint(
@ -1624,6 +1733,14 @@ bool MembersController::customRowSelectionPoint(
return y < st::groupCallNarrowSize.height();
}
Fn<QImage()> MembersController::customRowRippleMaskGenerator() {
return [] {
return Ui::RippleAnimation::roundRectMask(
st::groupCallNarrowSize,
st::roundRadiusLarge);
};
}
auto MembersController::kickParticipantRequests() const
-> rpl::producer<not_null<PeerData*>>{
return _kickParticipantRequests.events();
@ -1938,7 +2055,7 @@ Members::Members(
not_null<GroupCall*> call)
: RpWidget(parent)
, _call(call)
, _scroll(this, st::defaultSolidScroll)
, _scroll(this)
, _listController(std::make_unique<MembersController>(call, parent)) {
setupAddMember(call);
setupList();

View File

@ -248,10 +248,6 @@ emojiSuggestionsPadding: margins(emojiColorsPadding, 0px, emojiColorsPadding, 0p
emojiSuggestionsFadeAfter: 20px;
mentionHeight: 40px;
mentionScroll: ScrollArea(defaultScrollArea) {
topsh: 0px;
bottomsh: 0px;
}
mentionPadding: margins(8px, 5px, 8px, 5px);
mentionTop: 11px;
mentionFont: linkFont;

View File

@ -140,7 +140,7 @@ FieldAutocomplete::FieldAutocomplete(
not_null<Window::SessionController*> controller)
: RpWidget(parent)
, _controller(controller)
, _scroll(this, st::mentionScroll) {
, _scroll(this) {
hide();
_scroll->setGeometry(rect());
@ -759,7 +759,7 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
auto htagwidth = width()
- st::mentionPadding.right()
- htagleft
- st::mentionScroll.width;
- st::defaultScrollArea.width;
if (!_srows->empty()) {
int32 rows = rowscount(_srows->size(), _stickersPerRow);

View File

@ -51,10 +51,6 @@ dialogsSkip: 8px;
dialogsWidthDuration: 120;
dialogsTextWidthMin: 150px;
dialogsScroll: ScrollArea(defaultScrollArea) {
topsh: 0px;
bottomsh: 0px;
}
dialogsTextPalette: TextPalette(defaultTextPalette) {
linkFg: dialogsTextFgService;

View File

@ -174,7 +174,7 @@ Widget::Widget(
object_ptr<Ui::IconButton>(this, st::dialogsCalendar))
, _cancelSearch(_searchControls, st::dialogsCancelSearch)
, _lockUnlock(_searchControls, st::dialogsLock)
, _scroll(this, st::dialogsScroll)
, _scroll(this)
, _scrollToTop(_scroll, st::dialogsToUp)
, _singleMessageSearch(&controller->session()) {
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(this, controller));

View File

@ -37,11 +37,6 @@ infoToggle: InfoToggle {
rippleAreaPadding: 8px;
}
infoScroll: ScrollArea(defaultScrollArea) {
bottomsh: 0px;
topsh: 0px;
}
infoMediaSearch: SearchFieldRow {
height: 44px;
padding: margins(8px, 6px, 8px, 6px);

View File

@ -37,7 +37,7 @@ ContentWidget::ContentWidget(
not_null<Controller*> controller)
: RpWidget(parent)
, _controller(controller)
, _scroll(this, st::infoScroll) {
, _scroll(this) {
using namespace rpl::mappers;
setAttribute(Qt::WA_OpaquePaintEvent);

View File

@ -61,9 +61,6 @@ passportPasswordForgotBottom: 36px;
passportPanelScroll: ScrollArea(defaultScrollArea) {
deltat: 6px;
deltab: 6px;
topsh: 0px;
bottomsh: 0px;
}
passportPanelAuthorize: RoundButton(passportPasswordSubmit) {

View File

@ -217,7 +217,7 @@ IntroWidget::IntroWidget(
not_null<Window::Controller*> window)
: RpWidget(parent)
, _wrap(this)
, _scroll(Ui::CreateChild<Ui::ScrollArea>(_wrap.data(), st::infoScroll))
, _scroll(Ui::CreateChild<Ui::ScrollArea>(_wrap.data()))
, _topShadow(this) {
_wrap->setAttribute(Qt::WA_OpaquePaintEvent);
_wrap->paintRequest(

View File

@ -407,9 +407,7 @@ void Autocomplete::setupContent() {
rpl::single(qsl("Search for templates"))), // #TODO hard_lang
st::autocompleteSearchPadding);
const auto input = inputWrap->entity();
const auto scroll = Ui::CreateChild<Ui::ScrollArea>(
this,
st::mentionScroll);
const auto scroll = Ui::CreateChild<Ui::ScrollArea>(this);
const auto inner = scroll->setOwnedWidget(object_ptr<Inner>(scroll));

View File

@ -60,7 +60,6 @@ historyScroll: ScrollArea(defaultScrollArea) {
deltat: 3px;
deltab: 3px;
topsh: 0px;
bottomsh: -1px;
}

View File

@ -649,7 +649,7 @@ Editor::Editor(
const Data::CloudTheme &cloud)
: _window(window)
, _cloud(cloud)
, _scroll(this, st::themesScroll)
, _scroll(this)
, _close(this, st::defaultMultiSelect.fieldCancel)
, _menuToggle(this, st::themesMenuToggle)
, _select(this, st::defaultMultiSelect, tr::lng_country_ph())