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:
@@ -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) {
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user