mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Improve multi-pinned message bar.
This commit is contained in:
parent
a614ccad97
commit
aefef948cd
8 changed files with 226 additions and 45 deletions
|
@ -543,7 +543,7 @@ void PeerData::addPinnedSlice(
|
|||
return;
|
||||
}
|
||||
ensurePinnedMessagesCreated();
|
||||
_pinnedMessages->add(std::move(ids), noSkipRange, std::nullopt);
|
||||
_pinnedMessages->add(std::move(ids), noSkipRange, count);
|
||||
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
|
||||
}
|
||||
|
||||
|
|
|
@ -5225,7 +5225,8 @@ void HistoryWidget::checkPinnedBarState() {
|
|||
) | rpl::map([=](HistoryView::PinnedId messageId) {
|
||||
return HistoryView::PinnedBarId{
|
||||
FullMsgId{ peerToChannel(_peer->id), messageId.message },
|
||||
messageId.type
|
||||
messageId.index,
|
||||
messageId.count
|
||||
};
|
||||
});
|
||||
_pinnedBar = std::make_unique<Ui::PinnedBar>(
|
||||
|
|
|
@ -23,10 +23,7 @@ namespace {
|
|||
|
||||
[[nodiscard]] Ui::MessageBarContent ContentWithoutPreview(
|
||||
not_null<HistoryItem*> item) {
|
||||
const auto media = item->media();
|
||||
const auto poll = media ? media->poll() : nullptr;
|
||||
return Ui::MessageBarContent{
|
||||
.id = item->id,
|
||||
.text = { item->inReplyText() },
|
||||
};
|
||||
}
|
||||
|
@ -126,17 +123,13 @@ auto WithPinnedTitle(not_null<Main::Session*> session, PinnedBarId id) {
|
|||
if (!item) {
|
||||
return std::move(content);
|
||||
}
|
||||
const auto media = item->media();
|
||||
const auto poll = media ? media->poll() : nullptr;
|
||||
content.title = (id.type == PinnedIdType::First)
|
||||
? tr::lng_pinned_previous(tr::now) // #TODO pinned first?
|
||||
: (id.type == PinnedIdType::Middle)
|
||||
? tr::lng_pinned_previous(tr::now)
|
||||
: !poll
|
||||
content.title = (id.index + 1 >= id.count)
|
||||
? tr::lng_pinned_message(tr::now)
|
||||
: poll->quiz()
|
||||
? tr::lng_pinned_quiz(tr::now)
|
||||
: tr::lng_pinned_poll(tr::now);
|
||||
: (tr::lng_pinned_message(tr::now) // #TODO pinned
|
||||
+ " #"
|
||||
+ QString::number(id.count - id.index));
|
||||
content.count = std::max(id.count, 1);
|
||||
content.index = std::clamp(id.index, 0, content.count - 1);
|
||||
return std::move(content);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -30,13 +30,16 @@ namespace HistoryView {
|
|||
enum class PinnedIdType;
|
||||
struct PinnedBarId {
|
||||
FullMsgId message;
|
||||
PinnedIdType type = PinnedIdType();
|
||||
int index = 0;
|
||||
int count = 1;
|
||||
|
||||
bool operator<(const PinnedBarId &other) const {
|
||||
return std::tie(message, type) < std::tie(other.message, other.type);
|
||||
return std::tie(message, index, count)
|
||||
< std::tie(other.message, other.index, other.count);
|
||||
}
|
||||
bool operator==(const PinnedBarId &other) const {
|
||||
return std::tie(message, type) == std::tie(other.message, other.type);
|
||||
return std::tie(message, index, count)
|
||||
== std::tie(other.message, other.index, other.count);
|
||||
}
|
||||
bool operator!=(const PinnedBarId &other) const {
|
||||
return !(*this == other);
|
||||
|
|
|
@ -102,15 +102,18 @@ void PinnedTracker::setupViewer(not_null<Data::PinnedMessages*> data) {
|
|||
_current = PinnedId();
|
||||
return;
|
||||
}
|
||||
const auto type = (!after && (snapshot.skippedAfter == 0))
|
||||
? PinnedIdType::Last
|
||||
: (before < 2 && (snapshot.skippedBefore == 0))
|
||||
? PinnedIdType::First
|
||||
: PinnedIdType::Middle;
|
||||
const auto count = std::max(
|
||||
snapshot.fullCount.value_or(1),
|
||||
int(snapshot.ids.size()));
|
||||
const auto index = snapshot.skippedBefore.has_value()
|
||||
? (*snapshot.skippedBefore + before)
|
||||
: snapshot.skippedAfter.has_value()
|
||||
? (count - *snapshot.skippedAfter - after)
|
||||
: 1;
|
||||
if (i != begin(snapshot.ids)) {
|
||||
_current = PinnedId{ *(i - 1), type };
|
||||
_current = PinnedId{ *(i - 1), index - 1, count };
|
||||
} else if (snapshot.skippedBefore == 0) {
|
||||
_current = PinnedId{ snapshot.ids.front(), type };
|
||||
_current = PinnedId{ snapshot.ids.front(), 0, count };
|
||||
}
|
||||
}, _dataLifetime);
|
||||
}
|
||||
|
|
|
@ -16,20 +16,18 @@ enum class LoadDirection : char;
|
|||
|
||||
namespace HistoryView {
|
||||
|
||||
enum class PinnedIdType {
|
||||
First,
|
||||
Middle,
|
||||
Last,
|
||||
};
|
||||
struct PinnedId {
|
||||
MsgId message = 0;
|
||||
PinnedIdType type = PinnedIdType::Middle;
|
||||
int index = 0;
|
||||
int count = 1;
|
||||
|
||||
bool operator<(const PinnedId &other) const {
|
||||
return std::tie(message, type) < std::tie(other.message, other.type);
|
||||
return std::tie(message, index, count)
|
||||
< std::tie(other.message, other.index, other.count);
|
||||
}
|
||||
bool operator==(const PinnedId &other) const {
|
||||
return std::tie(message, type) == std::tie(other.message, other.type);
|
||||
return std::tie(message, index, count)
|
||||
== std::tie(other.message, other.index, other.count);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/chat/message_bar.h"
|
||||
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/palette.h"
|
||||
|
||||
|
@ -50,7 +51,8 @@ MessageBar::BodyAnimation MessageBar::DetectBodyAnimationType(
|
|||
? currentAnimation->bodyAnimation
|
||||
: BodyAnimation::None;
|
||||
const auto somethingChanged = (currentContent.text != nextContent.text)
|
||||
|| (currentContent.id != nextContent.id);
|
||||
|| (currentContent.index != nextContent.index)
|
||||
|| (currentContent.count != nextContent.count);
|
||||
return (now == BodyAnimation::Full
|
||||
|| currentContent.title != nextContent.title
|
||||
|| (currentContent.title.isEmpty() && somethingChanged))
|
||||
|
@ -61,6 +63,9 @@ MessageBar::BodyAnimation MessageBar::DetectBodyAnimationType(
|
|||
}
|
||||
|
||||
void MessageBar::tweenTo(MessageBarContent &&content) {
|
||||
Expects(content.count > 0);
|
||||
Expects(content.index >= 0 && content.index < content.count);
|
||||
|
||||
_widget.update();
|
||||
if (!_st.duration || anim::Disabled() || _widget.size().isEmpty()) {
|
||||
updateFromContent(std::move(content));
|
||||
|
@ -68,18 +73,22 @@ void MessageBar::tweenTo(MessageBarContent &&content) {
|
|||
}
|
||||
const auto hasImageChanged = (_content.preview.isNull()
|
||||
!= content.preview.isNull());
|
||||
const auto bodyChanged = (_content.id != content.id
|
||||
const auto bodyChanged = (_content.index != content.index
|
||||
|| _content.count != content.count
|
||||
|| _content.title != content.title
|
||||
|| _content.text != content.text
|
||||
|| _content.preview.constBits() != content.preview.constBits());
|
||||
const auto barCountChanged = (_content.count != content.count);
|
||||
const auto barFrom = _content.index;
|
||||
const auto barTo = content.index;
|
||||
auto animation = Animation();
|
||||
animation.bodyAnimation = DetectBodyAnimationType(
|
||||
_animation.get(),
|
||||
_content,
|
||||
content);
|
||||
animation.movingTo = (content.id > _content.id)
|
||||
animation.movingTo = (content.index > _content.index)
|
||||
? RectPart::Top
|
||||
: (content.id < _content.id)
|
||||
: (content.index < _content.index)
|
||||
? RectPart::Bottom
|
||||
: RectPart::None;
|
||||
animation.imageFrom = grabImagePart();
|
||||
|
@ -92,6 +101,8 @@ void MessageBar::tweenTo(MessageBarContent &&content) {
|
|||
_animation = std::move(was);
|
||||
std::swap(*_animation, animation);
|
||||
_animation->imageShown = std::move(animation.imageShown);
|
||||
_animation->barScroll = std::move(animation.barScroll);
|
||||
_animation->barTop = std::move(animation.barTop);
|
||||
} else {
|
||||
_animation = std::make_unique<Animation>(std::move(animation));
|
||||
}
|
||||
|
@ -110,6 +121,23 @@ void MessageBar::tweenTo(MessageBarContent &&content) {
|
|||
1.,
|
||||
_st.duration);
|
||||
}
|
||||
if (barCountChanged) {
|
||||
_animation->barScroll.stop();
|
||||
_animation->barTop.stop();
|
||||
} else if (barFrom != barTo) {
|
||||
const auto wasState = countBarState(barFrom);
|
||||
const auto nowState = countBarState(barTo);
|
||||
_animation->barScroll.start(
|
||||
[=] { _widget.update(); },
|
||||
wasState.scroll,
|
||||
nowState.scroll,
|
||||
_st.duration);
|
||||
_animation->barTop.start(
|
||||
[] {},
|
||||
wasState.offset,
|
||||
nowState.offset,
|
||||
_st.duration);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageBar::updateFromContent(MessageBarContent &&content) {
|
||||
|
@ -239,12 +267,7 @@ void MessageBar::paint(Painter &p) {
|
|||
? (shiftTo - shiftFull)
|
||||
: (shiftTo + shiftFull);
|
||||
|
||||
const auto bar = QRect(
|
||||
st::msgReplyBarSkip + st::msgReplyBarPos.x(),
|
||||
st::msgReplyPadding.top() + st::msgReplyBarPos.y(),
|
||||
st::msgReplyBarSize.width(),
|
||||
st::msgReplyBarSize.height());
|
||||
p.fillRect(bar, st::msgInReplyBarColor);
|
||||
paintLeftBar(p);
|
||||
|
||||
if (!_animation) {
|
||||
if (!_image.isNull()) {
|
||||
|
@ -315,4 +338,150 @@ void MessageBar::paint(Painter &p) {
|
|||
}
|
||||
}
|
||||
|
||||
auto MessageBar::countBarState(int index) const -> BarState {
|
||||
Expects(index >= 0 && index < _content.count);
|
||||
|
||||
auto result = BarState();
|
||||
const auto line = st::msgReplyBarSize.width();
|
||||
const auto height = st::msgReplyBarSize.height();
|
||||
const auto count = _content.count;
|
||||
const auto shownCount = std::min(count, 4);
|
||||
const auto dividers = (shownCount - 1) * line;
|
||||
const auto size = float64(st::msgReplyBarSize.height() - dividers)
|
||||
/ shownCount;
|
||||
const auto fullHeight = count * size + (count - 1) * line;
|
||||
const auto topByIndex = [&](int index) {
|
||||
return index * (size + line);
|
||||
};
|
||||
result.scroll = (count < 5 || index < 2)
|
||||
? 0
|
||||
: (index >= count - 2)
|
||||
? (fullHeight - height)
|
||||
: (topByIndex(index) - (height - size) / 2);
|
||||
result.size = size;
|
||||
result.skip = line;
|
||||
result.offset = topByIndex(index);
|
||||
return result;
|
||||
}
|
||||
|
||||
auto MessageBar::countBarState() const -> BarState {
|
||||
return countBarState(_content.index);
|
||||
}
|
||||
|
||||
void MessageBar::ensureGradientsCreated(int size) {
|
||||
if (!_topBarGradient.isNull()) {
|
||||
return;
|
||||
}
|
||||
const auto rows = size * style::DevicePixelRatio() - 2;
|
||||
auto bottomMask = QImage(
|
||||
QSize(1, size) * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
const auto step = ((1ULL << 24) - 1) / rows;
|
||||
const auto limit = step * rows;
|
||||
auto bits = bottomMask.bits();
|
||||
const auto perLine = bottomMask.bytesPerLine();
|
||||
for (auto counter = uint32(0); counter != limit; counter += step) {
|
||||
const auto value = (counter >> 16);
|
||||
memset(bits, int(value), perLine);
|
||||
bits += perLine;
|
||||
}
|
||||
memset(bits, 255, perLine * 2);
|
||||
auto bottom = style::colorizeImage(bottomMask, st::historyPinnedBg);
|
||||
bottom.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
auto top = bottom.mirrored();
|
||||
_bottomBarGradient = Images::PixmapFast(std::move(bottom));
|
||||
_topBarGradient = Images::PixmapFast(std::move(top));
|
||||
}
|
||||
|
||||
void MessageBar::paintLeftBar(Painter &p) {
|
||||
const auto state = countBarState();
|
||||
const auto gradientSize = int(std::ceil(state.size * 2.5));
|
||||
if (_content.count > 4) {
|
||||
ensureGradientsCreated(gradientSize);
|
||||
}
|
||||
|
||||
const auto scroll = _animation
|
||||
? _animation->barScroll.value(state.scroll)
|
||||
: state.scroll;
|
||||
const auto offset = _animation
|
||||
? _animation->barTop.value(state.offset)
|
||||
: state.offset;
|
||||
const auto line = st::msgReplyBarSize.width();
|
||||
const auto height = st::msgReplyBarSize.height();
|
||||
const auto activeFrom = offset - scroll;
|
||||
const auto activeTill = activeFrom + state.size;
|
||||
const auto single = state.size + state.skip;
|
||||
|
||||
const auto barSkip = st::msgReplyPadding.top() + st::msgReplyBarPos.y();
|
||||
const auto fullHeight = barSkip + height + barSkip;
|
||||
const auto bar = QRect(
|
||||
st::msgReplyBarSkip + st::msgReplyBarPos.x(),
|
||||
barSkip,
|
||||
line,
|
||||
state.size);
|
||||
const auto paintFromScroll = std::max(scroll - barSkip, 0.);
|
||||
const auto paintFrom = int(std::floor(paintFromScroll / single));
|
||||
const auto paintTillScroll = (scroll + height + barSkip);
|
||||
const auto paintTill = std::min(
|
||||
int(std::floor(paintTillScroll / single)) + 1,
|
||||
_content.count);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
const auto activeBrush = QBrush(st::msgInReplyBarColor);
|
||||
const auto inactiveBrush = QBrush(QColor(
|
||||
st::msgInReplyBarColor->c.red(),
|
||||
st::msgInReplyBarColor->c.green(),
|
||||
st::msgInReplyBarColor->c.blue(),
|
||||
st::msgInReplyBarColor->c.alpha() / 3));
|
||||
const auto radius = line / 2.;
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
for (auto i = paintFrom; i != paintTill; ++i) {
|
||||
const auto top = i * single - scroll;
|
||||
const auto bottom = top + state.size;
|
||||
const auto active = (top == activeFrom);
|
||||
p.setBrush(active ? activeBrush : inactiveBrush);
|
||||
p.drawRoundedRect(bar.translated(0, top), radius, radius);
|
||||
if (active
|
||||
|| bottom - line <= activeFrom
|
||||
|| top + line >= activeTill) {
|
||||
continue;
|
||||
}
|
||||
const auto partFrom = std::max(top, activeFrom);
|
||||
const auto partTill = std::min(bottom, activeTill);
|
||||
p.setBrush(activeBrush);
|
||||
p.drawRoundedRect(
|
||||
QRect(bar.x(), bar.y() + partFrom, line, partTill - partFrom),
|
||||
radius,
|
||||
radius);
|
||||
}
|
||||
if (_content.count > 4) {
|
||||
const auto firstScroll = countBarState(2).scroll;
|
||||
const auto gradientTop = (scroll >= firstScroll)
|
||||
? 0
|
||||
: anim::interpolate(-gradientSize, 0, scroll / firstScroll);
|
||||
const auto lastScroll = countBarState(_content.count - 3).scroll;
|
||||
const auto largestScroll = countBarState(_content.count - 1).scroll;
|
||||
const auto gradientBottom = (scroll <= lastScroll)
|
||||
? fullHeight
|
||||
: anim::interpolate(
|
||||
fullHeight,
|
||||
fullHeight + gradientSize,
|
||||
(scroll - lastScroll) / (largestScroll - lastScroll));
|
||||
if (gradientTop > -gradientSize) {
|
||||
p.drawPixmap(
|
||||
QRect(bar.x(), gradientTop, bar.width(), gradientSize),
|
||||
_topBarGradient);
|
||||
}
|
||||
if (gradientBottom < fullHeight + gradientSize) {
|
||||
p.drawPixmap(
|
||||
QRect(
|
||||
bar.x(),
|
||||
gradientBottom - gradientSize,
|
||||
bar.width(),
|
||||
gradientSize),
|
||||
_bottomBarGradient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -19,7 +19,8 @@ struct MessageBar;
|
|||
namespace Ui {
|
||||
|
||||
struct MessageBarContent {
|
||||
int id = 0;
|
||||
int index = 0;
|
||||
int count = 1;
|
||||
QString title;
|
||||
TextWithEntities text;
|
||||
QImage preview;
|
||||
|
@ -47,6 +48,8 @@ private:
|
|||
struct Animation {
|
||||
Ui::Animations::Simple bodyMoved;
|
||||
Ui::Animations::Simple imageShown;
|
||||
Ui::Animations::Simple barScroll;
|
||||
Ui::Animations::Simple barTop;
|
||||
QPixmap bodyOrTextFrom;
|
||||
QPixmap bodyOrTextTo;
|
||||
QPixmap imageFrom;
|
||||
|
@ -54,8 +57,15 @@ private:
|
|||
BodyAnimation bodyAnimation = BodyAnimation::None;
|
||||
RectPart movingTo = RectPart::None;
|
||||
};
|
||||
struct BarState {
|
||||
float64 scroll = 0.;
|
||||
float64 size = 0.;
|
||||
float64 skip = 0.;
|
||||
float64 offset = 0.;
|
||||
};
|
||||
void setup();
|
||||
void paint(Painter &p);
|
||||
void paintLeftBar(Painter &p);
|
||||
void tweenTo(MessageBarContent &&content);
|
||||
void updateFromContent(MessageBarContent &&content);
|
||||
[[nodiscard]] QPixmap prepareImage(const QImage &preview);
|
||||
|
@ -71,6 +81,10 @@ private:
|
|||
[[nodiscard]] QPixmap grabBodyPart();
|
||||
[[nodiscard]] QPixmap grabTextPart();
|
||||
|
||||
[[nodiscard]] BarState countBarState(int index) const;
|
||||
[[nodiscard]] BarState countBarState() const;
|
||||
void ensureGradientsCreated(int size);
|
||||
|
||||
[[nodiscard]] static BodyAnimation DetectBodyAnimationType(
|
||||
Animation *currentAnimation,
|
||||
const MessageBarContent ¤tContent,
|
||||
|
@ -81,7 +95,7 @@ private:
|
|||
MessageBarContent _content;
|
||||
rpl::lifetime _contentLifetime;
|
||||
Ui::Text::String _title, _text;
|
||||
QPixmap _image;
|
||||
QPixmap _image, _topBarGradient, _bottomBarGradient;
|
||||
std::unique_ptr<Animation> _animation;
|
||||
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue