2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-30 22:16:14 +00:00

Implement expanding of list / categories.

This commit is contained in:
John Preston
2022-08-23 14:15:14 +03:00
parent c5fa4aae62
commit 20d4d00634
6 changed files with 224 additions and 47 deletions

View File

@@ -483,6 +483,21 @@ auto EmojiListWidget::premiumChosen() const
return _premiumChosen.events();
}
void EmojiListWidget::paintExpanding(
QPainter &p,
QRect clip,
RectPart origin) {
const auto shift = clip.topLeft();
const auto adjusted = clip.translated(-shift);
p.translate(shift);
p.setClipRect(adjusted);
const auto context = ExpandingContext{
.expanding = true,
};
paint(p, context, adjusted);
p.translate(-shift);
}
void EmojiListWidget::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
@@ -755,14 +770,26 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
_repaintsScheduled.clear();
const auto r = e ? e->rect() : rect();
if (r != rect()) {
p.setClipRect(r);
}
p.fillRect(r, st::emojiPanBg);
const auto clip = e ? e->rect() : rect();
p.fillRect(clip, st::emojiPanBg);
auto fromColumn = floorclamp(r.x() - _rowsLeft, _singleSize.width(), 0, _columnCount);
auto toColumn = ceilclamp(r.x() + r.width() - _rowsLeft, _singleSize.width(), 0, _columnCount);
paint(p, {}, clip);
}
void EmojiListWidget::paint(
QPainter &p,
const ExpandingContext &context,
QRect clip) {
auto fromColumn = floorclamp(
clip.x() - _rowsLeft,
_singleSize.width(),
0,
_columnCount);
auto toColumn = ceilclamp(
clip.x() + clip.width() - _rowsLeft,
_singleSize.width(),
0,
_columnCount);
if (rtl()) {
qSwap(fromColumn, toColumn);
fromColumn = _columnCount - fromColumn;
@@ -775,9 +802,9 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
? &_pressed
: &_selected);
enumerateSections([&](const SectionInfo &info) {
if (r.top() >= info.rowsBottom) {
if (clip.top() >= info.rowsBottom) {
return true;
} else if (r.top() + r.height() <= info.top) {
} else if (clip.top() + clip.height() <= info.top) {
return false;
}
const auto buttonSelected = selectedButton
@@ -785,8 +812,8 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
: false;
const auto widthForTitle = emojiRight()
- (st().headerLeft - st().margin.left())
- paintButtonGetWidth(p, info, buttonSelected, r);
if (info.section > 0 && r.top() < info.rowsTop) {
- paintButtonGetWidth(p, info, buttonSelected, clip);
if (info.section > 0 && clip.top() < info.rowsTop) {
p.setFont(st::emojiPanHeaderFont);
p.setPen(st::emojiPanHeaderFg);
auto titleText = (info.section < _staticCount)
@@ -808,14 +835,23 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
top,
width());
}
const auto textTop = top + st::emojiPanHeaderFont->ascent;
p.setFont(st::emojiPanHeaderFont);
p.setPen(st::emojiPanHeaderFg);
p.drawTextLeft(left, top, width(), titleText, titleWidth);
p.drawText(left, textTop, titleText);
}
if (r.top() + r.height() > info.rowsTop) {
if (clip.top() + clip.height() > info.rowsTop) {
ensureLoaded(info.section);
auto fromRow = floorclamp(r.y() - info.rowsTop, _singleSize.height(), 0, info.rowsCount);
auto toRow = ceilclamp(r.y() + r.height() - info.rowsTop, _singleSize.height(), 0, info.rowsCount);
auto fromRow = floorclamp(
clip.y() - info.rowsTop,
_singleSize.height(),
0,
info.rowsCount);
auto toRow = ceilclamp(
clip.y() + clip.height() - info.rowsTop,
_singleSize.height(),
0,
info.rowsCount);
for (auto i = fromRow; i < toRow; ++i) {
for (auto j = fromColumn; j < toColumn; ++j) {
const auto index = i * _columnCount + j;
@@ -1790,6 +1826,7 @@ QPoint EmojiListWidget::buttonRippleTopLeft(int section) const {
void EmojiListWidget::refreshEmoji() {
refreshRecent();
refreshCustom();
resizeToWidth(width());
}
void EmojiListWidget::showSet(uint64 setId) {

View File

@@ -112,6 +112,8 @@ public:
[[nodiscard]] auto premiumChosen() const
-> rpl::producer<not_null<DocumentData*>>;
void paintExpanding(QPainter &p, QRect clip, RectPart origin);
protected:
void visibleTopBottomUpdated(
int visibleTop,
@@ -205,6 +207,9 @@ private:
OverEmoji,
OverSet,
OverButton>;
struct ExpandingContext {
bool expanding = false;
};
template <typename Callback>
bool enumerateSections(Callback callback) const;
@@ -230,6 +235,7 @@ private:
[[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const;
void selectEmoji(EmojiPtr emoji);
void selectCustom(not_null<DocumentData*> document);
void paint(QPainter &p, const ExpandingContext &context, QRect clip);
void drawCollapsedBadge(QPainter &p, QPoint position, int count);
void drawRecent(
QPainter &p,

View File

@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lottie/lottie_single_player.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/buttons.h"
#include "ui/rect_part.h"
#include "styles/style_chat_helpers.h"
#include <QtWidgets/QApplication>
@@ -222,6 +223,27 @@ void StickersListFooter::clearHeavyData() {
});
}
void StickersListFooter::paintExpanding(
Painter &p,
QRect clip,
float64 radius,
RectPart origin) {
const auto delta = ((origin | RectPart::None) & RectPart::FullBottom)
? (height() - clip.height())
: 0;
const auto shift = QPoint(clip.x(), clip.y() - delta);
p.translate(shift);
const auto context = ExpandingContext{
.clip = clip.translated(-shift),
.progress = clip.height() / float64(height()),
.radius = int(std::ceil(radius)),
.expanding = true,
};
paint(p, context);
p.translate(-shift);
p.setClipping(false);
}
void StickersListFooter::initSearch() {
_searchField.create(
this,
@@ -536,9 +558,15 @@ void StickersListFooter::setLoading(bool loading) {
}
void StickersListFooter::paintEvent(QPaintEvent *e) {
Painter p(this);
auto p = Painter(this);
_repaintScheduled = false;
paint(p, {});
}
void StickersListFooter::paint(
Painter &p,
const ExpandingContext &context) const {
if (_searchButtonVisible) {
paintSearchIcon(p);
}
@@ -558,25 +586,37 @@ void StickersListFooter::paintEvent(QPaintEvent *e) {
if (rtl()) {
clip.moveLeft(width() - _iconsLeft - clip.width());
}
p.setClipRect(clip);
if (context.expanding) {
const auto both = clip.intersected(
context.clip.marginsRemoved(
{ context.radius, 0, context.radius, 0 }));
if (both.isEmpty()) {
return;
}
p.setClipRect(both);
} else {
p.setClipRect(clip);
}
if (!_barSelection) {
paintSelectionBg(p);
paintSelectionBg(p, context);
}
const auto now = crl::now();
const auto paused = _paused();
enumerateVisibleIcons([&](const IconInfo &info) {
paintSetIcon(p, info, now, paused);
paintSetIcon(p, context, info, now, paused);
});
if (_barSelection) {
paintSelectionBar(p);
}
paintLeftRightFading(p);
paintLeftRightFading(p, context);
}
void StickersListFooter::paintSelectionBg(Painter &p) const {
void StickersListFooter::paintSelectionBg(
QPainter &p,
const ExpandingContext &context) const {
auto selxrel = _iconsLeft + qRound(_iconState.selectionX.current());
auto selx = selxrel - qRound(_iconState.x.current());
const auto selw = qRound(_iconState.selectionWidth.current());
@@ -585,9 +625,18 @@ void StickersListFooter::paintSelectionBg(Painter &p) const {
}
const auto sely = _iconsTop;
const auto area = st().iconArea;
const auto rect = QRect(
auto rect = QRect(
QPoint(selx, sely) + _areaPosition,
QSize(selw - 2 * _areaPosition.x(), area));
if (context.expanding) {
const auto recthalf = rect.height() / 2;
const auto myhalf = height() / 2;
const auto sub = anim::interpolate(recthalf, 0, context.progress);
const auto shift = anim::interpolate(myhalf, 0, context.progress);
rect = rect.marginsRemoved(
{ sub, sub, sub, sub }
).translated(0, shift);
}
if (rect.width() == rect.height() || _subiconsWidth <= _singleWidth) {
_selectionBg.paint(p, rect);
} else if (selw == _subiconsWidth) {
@@ -606,7 +655,7 @@ void StickersListFooter::paintSelectionBg(Painter &p) const {
}
}
void StickersListFooter::paintSelectionBar(Painter &p) const {
void StickersListFooter::paintSelectionBar(QPainter &p) const {
auto selxrel = _iconsLeft + qRound(_iconState.selectionX.current());
auto selx = selxrel - qRound(_iconState.x.current());
const auto selw = qRound(_iconState.selectionWidth.current());
@@ -621,26 +670,37 @@ void StickersListFooter::paintSelectionBar(Painter &p) const {
st::stickerIconSelColor);
}
void StickersListFooter::paintLeftRightFading(Painter &p) const {
auto o_left = std::clamp(
void StickersListFooter::paintLeftRightFading(
QPainter &p,
const ExpandingContext &context) const {
const auto o_left_normal = std::clamp(
_iconState.x.current() / st().fadeLeft.width(),
0.,
1.);
const auto o_left = context.expanding
? (1. - context.progress * (1. - o_left_normal))
: o_left_normal;
const auto radiusSkip = context.expanding
? std::max(context.radius - st::roundRadiusSmall, 0)
: 0;
if (o_left > 0) {
p.setOpacity(o_left);
st().fadeLeft.fill(p, style::rtlrect(_iconsLeft, _iconsTop, st().fadeLeft.width(), st().footer, width()));
st().fadeLeft.fill(p, style::rtlrect(std::max(_iconsLeft, radiusSkip), _iconsTop, st().fadeLeft.width(), st().footer, width()));
p.setOpacity(1.);
}
auto o_right = std::clamp(
const auto o_right_normal = std::clamp(
(_iconState.max - _iconState.x.current()) / st().fadeRight.width(),
0.,
1.);
const auto o_right = context.expanding
? (1. - context.progress * (1. - o_right_normal))
: o_right_normal;
if (o_right > 0) {
p.setOpacity(o_right);
st().fadeRight.fill(
p,
style::rtlrect(
width() - _iconsRight - st().fadeRight.width(),
width() - std::max(_iconsRight, radiusSkip) - st().fadeRight.width(),
_iconsTop,
st().fadeRight.width(),
st().footer, width()));
@@ -1043,7 +1103,7 @@ bool StickersListFooter::hasOnlyFeaturedSets() const {
&& (_icons[0].setId == Data::Stickers::FeaturedSetId);
}
void StickersListFooter::paintStickerSettingsIcon(Painter &p) const {
void StickersListFooter::paintStickerSettingsIcon(QPainter &p) const {
const auto settingsLeft = width() - _iconsRight;
st::stickersSettings.paint(
p,
@@ -1053,7 +1113,7 @@ void StickersListFooter::paintStickerSettingsIcon(Painter &p) const {
width());
}
void StickersListFooter::paintSearchIcon(Painter &p) const {
void StickersListFooter::paintSearchIcon(QPainter &p) const {
const auto searchLeft = _iconsLeft - _singleWidth;
st::stickersSearch.paint(
p,
@@ -1152,10 +1212,21 @@ void StickersListFooter::updateSetIconAt(int left) {
void StickersListFooter::paintSetIcon(
Painter &p,
const ExpandingContext &context,
const IconInfo &info,
crl::time now,
bool paused) const {
const auto &icon = _icons[info.index];
if (context.expanding) {
p.save();
const auto center = QPoint(
info.adjustedLeft + _singleWidth / 2,
_iconsTop + st().footer / 2);
const auto shift = QPoint(0, anim::interpolate(height() / 2, 0, context.progress));
p.translate(shift + center);
p.scale(context.progress, context.progress);
p.translate(-center);
}
if (icon.sticker) {
icon.ensureMediaCreated();
const_cast<StickersListFooter*>(this)->validateIconAnimation(icon);
@@ -1296,6 +1367,9 @@ void StickersListFooter::paintSetIcon(
}());
}
}
if (context.expanding) {
p.restore();
}
}
LocalStickersManager::LocalStickersManager(not_null<Main::Session*> session)

View File

@@ -134,6 +134,12 @@ public:
};
[[nodiscard]] rpl::producer<SearchRequest> searchRequests() const;
void paintExpanding(
Painter &p,
QRect clip,
float64 radius,
RectPart origin);
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
@@ -182,6 +188,12 @@ private:
crl::time animationStart = 0;
Ui::Animations::Basic animation;
};
struct ExpandingContext {
QRect clip;
float64 progress = 0.;
int radius = 0;
bool expanding = false;
};
void enumerateVisibleIcons(Fn<void(const IconInfo &)> callback) const;
void enumerateIcons(Fn<bool(const IconInfo &)> callback) const;
@@ -212,16 +224,23 @@ private:
void checkDragging(ScrollState &state);
bool finishDragging(ScrollState &state);
bool finishDragging();
void paintStickerSettingsIcon(Painter &p) const;
void paintSearchIcon(Painter &p) const;
void paint(Painter &p, const ExpandingContext &context) const;
void paintStickerSettingsIcon(QPainter &p) const;
void paintSearchIcon(QPainter &p) const;
void paintSetIcon(
Painter &p,
const ExpandingContext &context,
const IconInfo &info,
crl::time now,
bool paused) const;
void paintSelectionBg(Painter &p) const;
void paintSelectionBar(Painter &p) const;
void paintLeftRightFading(Painter &p) const;
void paintSelectionBg(
QPainter &p,
const ExpandingContext &context) const;
void paintSelectionBar(QPainter &p) const;
void paintLeftRightFading(
QPainter &p,
const ExpandingContext &context) const;
void updateEmojiSectionWidth();
void updateEmojiWidthCallback();