Support media covers for bot descriptions.

For that replace custom handling of _botAbout by a fake message.
This commit is contained in:
John Preston 2023-03-03 20:52:21 +04:00
parent 59c66d1f49
commit 221b0d19c7
10 changed files with 325 additions and 205 deletions

View file

@ -293,6 +293,9 @@ enum class MessageFlag : uint64 {
OnlyEmojiAndSpaces = (1ULL << 34),
OnlyEmojiAndSpacesSet = (1ULL << 35),
// Fake message with bot cover and information.
FakeBotAbout = (1ULL << 36),
};
inline constexpr bool is_flag_type(MessageFlag) { return true; }
using MessageFlags = base::flags<MessageFlag>;

View file

@ -32,8 +32,7 @@ using UpdateFlag = Data::PeerUpdate::Flag;
} // namespace
BotInfo::BotInfo() : text(st::msgMinWidth) {
}
BotInfo::BotInfo() = default;
UserData::UserData(not_null<Data::Session*> owner, PeerId id)
: PeerData(owner, id)
@ -188,10 +187,30 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
return;
}
QString desc = qs(d.vdescription().value_or_empty());
if (botInfo->description != desc) {
botInfo->description = desc;
botInfo->text = Ui::Text::String(st::msgMinWidth);
const auto description = qs(d.vdescription().value_or_empty());
if (botInfo->description != description) {
botInfo->description = description;
++botInfo->descriptionVersion;
}
if (const auto photo = d.vdescription_photo()) {
const auto parsed = owner().processPhoto(*photo);
if (botInfo->photo != parsed) {
botInfo->photo = parsed;
++botInfo->descriptionVersion;
}
} else if (botInfo->photo) {
botInfo->photo = nullptr;
++botInfo->descriptionVersion;
}
if (const auto document = d.vdescription_document()) {
const auto parsed = owner().processDocument(*document);
if (botInfo->document != parsed) {
botInfo->document = parsed;
++botInfo->descriptionVersion;
}
} else if (botInfo->document) {
botInfo->document = nullptr;
++botInfo->descriptionVersion;
}
auto commands = d.vcommands()

View file

@ -24,9 +24,13 @@ struct BotInfo {
bool cantJoinGroups = false;
bool supportsAttachMenu = false;
int version = 0;
QString description, inlinePlaceholder;
int descriptionVersion = 0;
QString description;
QString inlinePlaceholder;
std::vector<Data::BotCommand> commands;
Ui::Text::String text;
PhotoData *photo = nullptr;
DocumentData *document = nullptr;
QString botMenuButtonText;
QString botMenuButtonUrl;

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/crash_reports.h"
#include "core/click_handler_types.h"
#include "history/history.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/history_item.h"
#include "history/history_item_helpers.h"
#include "history/view/media/history_view_media.h"
@ -326,41 +327,100 @@ public:
};
class HistoryInner::BotAbout : public ClickHandlerHost {
class HistoryInner::BotAbout final : public ClickHandlerHost {
public:
BotAbout(not_null<HistoryInner*> parent, not_null<BotInfo*> info);
BotAbout(
not_null<History*> history,
not_null<HistoryView::ElementDelegate*> delegate);
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
[[nodiscard]] not_null<History*> history() const;
[[nodiscard]] HistoryView::Element *view() const;
[[nodiscard]] HistoryItem *item() const;
not_null<BotInfo*> info;
int width = 0;
bool refresh();
int top = 0;
int height = 0;
QRect rect;
private:
not_null<HistoryInner*> _parent;
const not_null<History*> _history;
const not_null<HistoryView::ElementDelegate*> _delegate;
AdminLog::OwnedItem _item;
int _version = 0;
};
HistoryInner::BotAbout::BotAbout(
not_null<HistoryInner*> parent,
not_null<BotInfo*> info)
: info(info)
, _parent(parent) {
not_null<History*> history,
not_null<HistoryView::ElementDelegate*> delegate)
: _history(history)
, _delegate(delegate) {
}
void HistoryInner::BotAbout::clickHandlerActiveChanged(
const ClickHandlerPtr &p,
bool active) {
_parent->update(rect);
not_null<History*> HistoryInner::BotAbout::history() const {
return _history;
}
void HistoryInner::BotAbout::clickHandlerPressedChanged(
const ClickHandlerPtr &p,
bool pressed) {
_parent->update(rect);
HistoryView::Element *HistoryInner::BotAbout::view() const {
return _item.get();
}
HistoryItem *HistoryInner::BotAbout::item() const {
if (const auto element = view()) {
return element->data();
}
return nullptr;
}
bool HistoryInner::BotAbout::refresh() {
const auto bot = _history->peer->asUser();
const auto info = bot ? bot->botInfo.get() : nullptr;
if (!info) {
if (_item) {
_item = {};
return true;
}
_version = 0;
return false;
}
const auto version = info->descriptionVersion;
if (_version == version) {
return false;
}
_version = version;
const auto flags = MessageFlag::FakeBotAbout
| MessageFlag::FakeHistoryItem
| MessageFlag::Local;
const auto postAuthor = QString();
const auto date = TimeId(0);
const auto replyTo = MsgId(0);
const auto viaBotId = UserId(0);
const auto groupedId = uint64(0);
const auto textWithEntities = TextUtilities::ParseEntities(
info->description,
Ui::ItemTextBotNoMonoOptions().flags);
const auto make = [&](auto &&a, auto &&b, auto &&...other) {
return _history->makeMessage(
_history->nextNonHistoryEntryId(),
flags,
replyTo,
viaBotId,
date,
bot->id,
postAuthor,
std::forward<decltype(a)>(a),
std::forward<decltype(b)>(b),
HistoryMessageMarkupData(),
std::forward<decltype(other)>(other)...);
};
const auto item = info->document
? make(info->document, textWithEntities)
: info->photo
? make(info->photo, textWithEntities)
: make(textWithEntities, MTP_messageMediaEmpty(), groupedId);
_item = AdminLog::OwnedItem(_delegate, item);
return true;
}
HistoryInner::HistoryInner(
@ -971,41 +1031,10 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
const auto historyDisplayedEmpty = _history->isDisplayedEmpty()
&& (!_migrated || _migrated->isDisplayedEmpty());
if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
const auto st = context.st;
const auto stm = &st->messageStyle(false, false);
if (clip.y() < _botAbout->rect.y() + _botAbout->rect.height() && clip.y() + clip.height() > _botAbout->rect.y()) {
p.setTextPalette(stm->textPalette);
using Corner = Ui::BubbleCornerRounding;
const auto rounding = Ui::BubbleRounding{
Corner::Large,
Corner::Large,
Corner::Large,
Corner::Large,
};
Ui::PaintBubble(p, Ui::SimpleBubble{
.st = st,
.geometry = _botAbout->rect,
.pattern = context.bubblesPattern,
.patternViewport = context.viewport,
.outerWidth = width(),
.selected = false,
.outbg = false,
.rounding = rounding,
});
auto top = _botAbout->rect.top() + st::msgPadding.top();
if (!_history->peer->isRepliesChat()) {
p.setFont(st::msgNameFont);
p.setPen(st->dialogsNameFg());
p.drawText(_botAbout->rect.left() + st::msgPadding.left(), top + st::msgNameFont->ascent, tr::lng_bot_description(tr::now));
top += +st::msgNameFont->height + st::botDescSkip;
}
p.setPen(stm->historyTextFg);
_botAbout->info->text.draw(p, _botAbout->rect.left() + st::msgPadding.left(), top, _botAbout->width);
p.restoreTextPalette();
if (const auto view = _botAbout ? _botAbout->view() : nullptr) {
if (clip.y() < _botAbout->top + _botAbout->height
&& clip.y() + clip.height() > _botAbout->top) {
view->draw(p, context);
}
} else if (historyDisplayedEmpty) {
paintEmpty(p, context.st, width(), height());
@ -2939,9 +2968,11 @@ void HistoryInner::recountHistoryGeometry() {
}
const auto visibleHeight = _scroll->height();
int oldHistoryPaddingTop = qMax(visibleHeight - historyHeight() - st::historyPaddingBottom, 0);
if (_botAbout && !_botAbout->info->text.isEmpty()) {
accumulate_max(oldHistoryPaddingTop, st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botAbout->height);
auto oldHistoryPaddingTop = qMax(
visibleHeight - historyHeight() - st::historyPaddingBottom,
0);
if (_botAbout) {
accumulate_max(oldHistoryPaddingTop, _botAbout->height);
}
_history->resizeToWidth(_contentWidth);
@ -2968,39 +2999,20 @@ void HistoryInner::recountHistoryGeometry() {
}
updateBotInfo(false);
if (_botAbout && !_botAbout->info->text.isEmpty()) {
int32 tw = _scroll->width() - st::msgMargin.left() - st::msgMargin.right();
if (tw > st::msgMaxWidth) tw = st::msgMaxWidth;
tw -= st::msgPadding.left() + st::msgPadding.right();
const auto descriptionWidth = _history->peer->isRepliesChat()
? 0
: st::msgNameFont->width(tr::lng_bot_description(tr::now));
int32 mw = qMax(_botAbout->info->text.maxWidth(), descriptionWidth);
if (tw > mw) tw = mw;
_botAbout->width = tw;
_botAbout->height = _botAbout->info->text.countHeight(_botAbout->width);
const auto descriptionHeight = _history->peer->isRepliesChat()
? 0
: (st::msgNameFont->height + st::botDescSkip);
int32 descH = st::msgMargin.top() + st::msgPadding.top() + descriptionHeight + _botAbout->height + st::msgPadding.bottom() + st::msgMargin.bottom();
int32 descMaxWidth = _scroll->width();
if (_isChatWide) {
descMaxWidth = qMin(descMaxWidth, int32(st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
}
int32 descAtX = (descMaxWidth - _botAbout->width) / 2 - st::msgPadding.left();
int32 descAtY = qMin(_historyPaddingTop - descH, qMax(0, (_scroll->height() - descH) / 2)) + st::msgMargin.top();
_botAbout->rect = QRect(descAtX, descAtY, _botAbout->width + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom());
if (const auto view = _botAbout ? _botAbout->view() : nullptr) {
_botAbout->height = view->resizeGetHeight(_contentWidth);
_botAbout->top = qMin(
_historyPaddingTop - _botAbout->height,
qMax(0, (_scroll->height() - _botAbout->height) / 2));
} else if (_botAbout) {
_botAbout->width = _botAbout->height = 0;
_botAbout->rect = QRect();
_botAbout->top = _botAbout->height = 0;
}
int newHistoryPaddingTop = qMax(visibleHeight - historyHeight() - st::historyPaddingBottom, 0);
if (_botAbout && !_botAbout->info->text.isEmpty()) {
accumulate_max(newHistoryPaddingTop, st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botAbout->height);
auto newHistoryPaddingTop = qMax(
visibleHeight - historyHeight() - st::historyPaddingBottom,
0);
if (_botAbout) {
accumulate_max(newHistoryPaddingTop, _botAbout->height);
}
auto historyPaddingTopDelta = (newHistoryPaddingTop - oldHistoryPaddingTop);
@ -3014,48 +3026,15 @@ void HistoryInner::recountHistoryGeometry() {
}
void HistoryInner::updateBotInfo(bool recount) {
int newh = 0;
if (_botAbout && !_botAbout->info->description.isEmpty()) {
if (_botAbout->info->text.isEmpty()) {
_botAbout->info->text.setText(
st::messageTextStyle,
_botAbout->info->description,
Ui::ItemTextBotNoMonoOptions());
if (recount) {
int32 tw = _scroll->width() - st::msgMargin.left() - st::msgMargin.right();
if (tw > st::msgMaxWidth) tw = st::msgMaxWidth;
tw -= st::msgPadding.left() + st::msgPadding.right();
const auto descriptionWidth = _history->peer->isRepliesChat()
? 0
: st::msgNameFont->width(tr::lng_bot_description(tr::now));
int32 mw = qMax(_botAbout->info->text.maxWidth(), descriptionWidth);
if (tw > mw) tw = mw;
_botAbout->width = tw;
newh = _botAbout->info->text.countHeight(_botAbout->width);
}
} else if (recount) {
newh = _botAbout->height;
}
}
if (recount && _botAbout) {
if (_botAbout->height != newh) {
_botAbout->height = newh;
if (!_botAbout) {
return;
} else if (_botAbout->refresh() && recount && _contentWidth > 0) {
const auto view = _botAbout->view();
const auto now = view ? view->resizeGetHeight(_contentWidth) : 0;
if (_botAbout->height != now) {
_botAbout->height = now;
updateSize();
}
if (_botAbout->height > 0) {
const auto descriptionHeight = _history->peer->isRepliesChat()
? 0
: (st::msgNameFont->height + st::botDescSkip);
int32 descH = st::msgMargin.top() + st::msgPadding.top() + descriptionHeight + _botAbout->height + st::msgPadding.bottom() + st::msgMargin.bottom();
int32 descAtX = (_scroll->width() - _botAbout->width) / 2 - st::msgPadding.left();
int32 descAtY = qMin(_historyPaddingTop - descH, (_scroll->height() - descH) / 2) + st::msgMargin.top();
_botAbout->rect = QRect(descAtX, descAtY, _botAbout->width + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom());
} else {
_botAbout->width = 0;
_botAbout->rect = QRect();
}
}
}
@ -3200,24 +3179,15 @@ void HistoryInner::changeItemsRevealHeight(int revealHeight) {
void HistoryInner::updateSize() {
const auto visibleHeight = _scroll->height();
const auto itemsHeight = historyHeight() - _revealHeight;
int newHistoryPaddingTop = qMax(visibleHeight - itemsHeight - st::historyPaddingBottom, 0);
if (_botAbout && !_botAbout->info->text.isEmpty()) {
accumulate_max(newHistoryPaddingTop, st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botAbout->height);
auto newHistoryPaddingTop = qMax(visibleHeight - itemsHeight - st::historyPaddingBottom, 0);
if (_botAbout) {
accumulate_max(newHistoryPaddingTop, _botAbout->height);
}
if (_botAbout && _botAbout->height > 0) {
const auto descriptionHeight = _history->peer->isRepliesChat()
? 0
: (st::msgNameFont->height + st::botDescSkip);
int32 descH = st::msgMargin.top() + st::msgPadding.top() + descriptionHeight + _botAbout->height + st::msgPadding.bottom() + st::msgMargin.bottom();
int32 descMaxWidth = _scroll->width();
if (_isChatWide) {
descMaxWidth = qMin(descMaxWidth, int32(st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
}
int32 descAtX = (descMaxWidth - _botAbout->width) / 2 - st::msgPadding.left();
int32 descAtY = qMin(newHistoryPaddingTop - descH, qMax(0, (_scroll->height() - descH) / 2)) + st::msgMargin.top();
_botAbout->rect = QRect(descAtX, descAtY, _botAbout->width + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom());
_botAbout->top = qMin(
newHistoryPaddingTop - _botAbout->height,
qMax(0, (_scroll->height() - _botAbout->height) / 2));
}
if (_historyPaddingTop != newHistoryPaddingTop) {
@ -3261,6 +3231,7 @@ void HistoryInner::leaveEventHook(QEvent *e) {
}
HistoryInner::~HistoryInner() {
_botAbout = nullptr;
for (const auto &item : _animatedStickersPlayed) {
if (const auto view = item->mainView()) {
if (const auto media = view->media()) {
@ -3632,12 +3603,15 @@ void HistoryInner::mouseActionUpdate() {
dragState = reactionState;
lnkhost = reactionView;
} else if (point.y() < _historyPaddingTop) {
if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
dragState = TextState(nullptr, _botAbout->info->text.getState(
point - _botAbout->rect.topLeft() - QPoint(st::msgPadding.left(), st::msgPadding.top() + st::botDescSkip + st::msgNameFont->height),
_botAbout->width));
if (const auto view = _botAbout ? _botAbout->view() : nullptr) {
StateRequest request;
if (base::IsAltPressed()) {
request.flags &= ~Ui::Text::StateRequest::Flag::LookupLink;
}
const auto relative = point - QPoint(0, _botAbout->top);
dragState = view->textState(relative, request);
_dragStateItem = session().data().message(dragState.itemId);
lnkhost = _botAbout.get();
lnkhost = view;
}
} else if (item) {
if (item != _mouseActionItem || (m - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) {
@ -3947,16 +3921,22 @@ void HistoryInner::setCanHaveFromUserpicsSponsored(bool value) {
int HistoryInner::itemTop(const HistoryItem *item) const {
if (!item) {
return -2;
} else if (_botAbout && item == _botAbout->item()) {
return _botAbout->top;
}
return itemTop(item->mainView());
}
int HistoryInner::itemTop(const Element *view) const {
if (!view || view->data()->mainView() != view) {
if (!view) {
return -1;
} else if (_botAbout && view == _botAbout->view()) {
return _botAbout->top;
} else if (view->data()->mainView() != view) {
return -1;
}
auto top = (view->history() == _history)
const auto top = (view->history() == _history)
? historyTop()
: (view->history() == _migrated
? migratedTop()
@ -3990,21 +3970,17 @@ auto HistoryInner::findViewForPinnedTracking(int top) const
}
void HistoryInner::notifyIsBotChanged() {
const auto newinfo = _peer->isUser()
? _peer->asUser()->botInfo.get()
: nullptr;
if ((!newinfo && !_botAbout)
|| (newinfo && _botAbout && _botAbout->info == newinfo)) {
return;
}
if (newinfo) {
_botAbout = std::make_unique<BotAbout>(this, newinfo);
if (newinfo && !newinfo->inited) {
session().api().requestFullPeer(_peer);
if (const auto user = _peer->asUser()) {
if (const auto info = user->botInfo.get()) {
if (!_botAbout) {
_botAbout = std::make_unique<BotAbout>(
_history,
_history->delegateMixin()->delegate());
}
if (!info->inited) {
session().api().requestFullPeer(_peer);
}
}
} else {
_botAbout = nullptr;
}
}

View file

@ -306,6 +306,9 @@ public:
[[nodiscard]] bool isLocal() const {
return _flags & MessageFlag::Local;
}
[[nodiscard]] bool isFakeBotAbout() const {
return _flags & MessageFlag::FakeBotAbout;
}
[[nodiscard]] bool isRegular() const;
[[nodiscard]] bool isUploading() const;
void sendFailed();

View file

@ -354,6 +354,20 @@ void DateBadge::paint(
ServiceMessagePainter::PaintDate(p, st, text, width, y, w, chatWide);
}
void FakeBotAboutTop::init() {
if (!text.isEmpty()) {
return;
}
text.setText(
st::msgNameStyle,
tr::lng_bot_description(tr::now),
Ui::NameTextOptions());
maxWidth = st::msgPadding.left()
+ text.maxWidth()
+ st::msgPadding.right();
height = st::msgNameStyle.font->height + st::botDescSkip;
}
Element::Element(
not_null<ElementDelegate*> delegate,
not_null<HistoryItem*> data,
@ -376,6 +390,9 @@ Element::Element(
if (_context == Context::History) {
history()->setHasPendingResizedItems();
}
if (data->isFakeBotAbout() && !data->history()->peer->isRepliesChat()) {
AddComponents(FakeBotAboutTop::Bit());
}
}
not_null<ElementDelegate*> Element::delegate() const {

View file

@ -230,6 +230,14 @@ struct DateBadge : public RuntimeComponent<DateBadge, Element> {
};
struct FakeBotAboutTop : public RuntimeComponent<FakeBotAboutTop, Element> {
void init();
Ui::Text::String text;
int maxWidth = 0;
int height = 0;
};
struct TopicButton {
std::unique_ptr<Ui::RippleAnimation> ripple;
ClickHandlerPtr link;

View file

@ -530,6 +530,12 @@ QSize Message::performCountOptimalSize() {
refreshInfoSkipBlock();
const auto media = this->media();
const auto botTop = item->isFakeBotAbout()
? Get<FakeBotAboutTop>()
: nullptr;
if (botTop) {
botTop->init();
}
auto maxWidth = 0;
auto minHeight = 0;
@ -560,13 +566,14 @@ QSize Message::performCountOptimalSize() {
}
// Entry page is always a bubble bottom.
const auto withVisibleText = hasVisibleText();
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
maxWidth = plainMaxWidth();
if (context() == Context::Replies && item->isDiscussionPost()) {
maxWidth = std::max(maxWidth, st::msgMaxWidth);
}
minHeight = hasVisibleText() ? text().minHeight() : 0;
minHeight = withVisibleText ? text().minHeight() : 0;
if (reactionsInBubble) {
const auto reactionsMaxWidth = st::msgPadding.left()
+ _reactions->maxWidth()
@ -604,9 +611,14 @@ QSize Message::performCountOptimalSize() {
const auto innerWidth = maxWidth
- st::msgPadding.left()
- st::msgPadding.right();
if (hasVisibleText() && maxWidth < plainMaxWidth()) {
minHeight -= text().minHeight();
minHeight += text().countHeight(innerWidth);
if (withVisibleText) {
if (botTop) {
minHeight += botTop->height;
}
if (maxWidth < plainMaxWidth()) {
minHeight -= text().minHeight();
minHeight += text().countHeight(innerWidth);
}
}
if (reactionsInBubble) {
minHeight -= _reactions->minHeight();
@ -678,6 +690,10 @@ QSize Message::performCountOptimalSize() {
accumulate_max(maxWidth, entry->maxWidth());
minHeight += entry->minHeight();
}
if (withVisibleText && botTop) {
accumulate_max(maxWidth, botTop->maxWidth);
minHeight += botTop->height;
}
}
accumulate_max(maxWidth, minWidthForMedia());
} else if (media) {
@ -1467,6 +1483,15 @@ void Message::paintText(
p.setPen(stm->historyTextFg);
p.setFont(st::msgFont);
prepareCustomEmojiPaint(p, context, text());
if (const auto botTop = Get<FakeBotAboutTop>()) {
botTop->text.drawLeftElided(
p,
trect.x(),
trect.y(),
trect.width(),
width());
trect.setY(trect.y() + botTop->height);
}
text().draw(p, {
.position = trect.topLeft(),
.availableWidth = trect.width(),
@ -2281,6 +2306,8 @@ bool Message::getStateText(
StateRequest request) const {
if (!hasVisibleText()) {
return false;
} else if (const auto botTop = Get<FakeBotAboutTop>()) {
trect.setY(trect.y() + botTop->height);
}
const auto item = data();
if (base::in_range(point.y(), trect.y(), trect.y() + trect.height())) {
@ -2886,7 +2913,7 @@ bool Message::drawBubble() const {
const auto item = data();
if (isHidden()) {
return false;
} else if (logEntryOriginal()) {
} else if (logEntryOriginal() || item->isFakeBotAbout()) {
return true;
}
const auto media = this->media();
@ -3376,8 +3403,9 @@ QRect Message::innerGeometry() const {
}
QRect Message::countGeometry() const {
const auto commentsRoot = (context() == Context::Replies)
&& data()->isDiscussionPost();
const auto item = data();
const auto centeredView = item->isFakeBotAbout()
|| (context() == Context::Replies && item->isDiscussionPost());
const auto media = this->media();
const auto mediaWidth = (media && media->isDisplayed())
? media->width()
@ -3385,7 +3413,7 @@ QRect Message::countGeometry() const {
const auto outbg = hasOutLayout();
const auto availableWidth = width()
- st::msgMargin.left()
- (commentsRoot ? st::msgMargin.left() : st::msgMargin.right());
- (centeredView ? st::msgMargin.left() : st::msgMargin.right());
auto contentLeft = (outbg && !delegate()->elementIsChatWide())
? st::msgMargin.right()
: st::msgMargin.left();
@ -3412,10 +3440,10 @@ QRect Message::countGeometry() const {
if (contentWidth < availableWidth && !delegate()->elementIsChatWide()) {
if (outbg) {
contentLeft += availableWidth - contentWidth;
} else if (commentsRoot) {
} else if (centeredView) {
contentLeft += (availableWidth - contentWidth) / 2;
}
} else if (contentWidth < availableWidth && commentsRoot) {
} else if (contentWidth < availableWidth && centeredView) {
contentLeft += std::max(
((st::msgMaxWidth + 2 * st::msgPhotoSkip) - contentWidth) / 2,
0);
@ -3433,11 +3461,13 @@ Ui::BubbleRounding Message::countMessageRounding() const {
const auto smallTop = isBubbleAttachedToPrevious();
const auto smallBottom = isBubbleAttachedToNext();
const auto media = smallBottom ? nullptr : this->media();
const auto keyboard = data()->inlineReplyKeyboard();
const auto item = data();
const auto keyboard = item->inlineReplyKeyboard();
const auto skipTail = smallBottom
|| (media && media->skipBubbleTail())
|| (keyboard != nullptr)
|| (context() == Context::Replies && data()->isDiscussionPost());
|| item->isFakeBotAbout()
|| (context() == Context::Replies && item->isDiscussionPost());
const auto right = !delegate()->elementIsChatWide() && hasOutLayout();
using Corner = Ui::BubbleCornerRounding;
return Ui::BubbleRounding{
@ -3480,16 +3510,19 @@ int Message::resizeContentGetHeight(int newWidth) {
auto newHeight = minHeight();
const auto item = data();
const auto botTop = item->isFakeBotAbout()
? Get<FakeBotAboutTop>()
: nullptr;
const auto media = this->media();
const auto mediaDisplayed = media ? media->isDisplayed() : false;
const auto bubble = drawBubble();
// This code duplicates countGeometry() but also resizes media.
const auto commentsRoot = (context() == Context::Replies)
&& data()->isDiscussionPost();
const auto centeredView = item->isFakeBotAbout()
|| (context() == Context::Replies && item->isDiscussionPost());
auto contentWidth = newWidth
- st::msgMargin.left()
- (commentsRoot ? st::msgMargin.left() : st::msgMargin.right());
- (centeredView ? st::msgMargin.left() : st::msgMargin.right());
if (hasFromPhoto()) {
if (const auto size = rightActionSize()) {
contentWidth -= size->width() + (st::msgPhotoSkip - st::historyFastShareSize);
@ -3540,7 +3573,14 @@ int Message::resizeContentGetHeight(int newWidth) {
entry->resizeGetHeight(contentWidth);
}
} else {
newHeight = hasVisibleText() ? textHeightFor(textWidth) : 0;
const auto withVisibleText = hasVisibleText();
newHeight = 0;
if (withVisibleText) {
if (botTop) {
newHeight += botTop->height;
}
newHeight += textHeightFor(textWidth);
}
if (!mediaOnBottom && (!_viewButton || !reactionsInBubble)) {
newHeight += st::msgPadding.bottom();
if (mediaDisplayed) {

View file

@ -199,6 +199,10 @@ QSize Gif::countOptimalSize() {
minHeight = adjustHeightForLessCrop(
scaled,
{ maxWidth, minHeight });
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
accumulate_max(maxWidth, botTop->maxWidth);
minHeight += botTop->height;
}
minHeight += st::mediaCaptionSkip + _caption.minHeight();
if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom();
@ -236,11 +240,14 @@ QSize Gif::countCurrentSize(int newWidth) {
if (_parent->hasBubble()) {
accumulate_max(newWidth, _parent->minWidthForMedia());
if (!_caption.isEmpty()) {
const auto maxWithCaption = qMin(
st::msgMaxWidth,
(st::msgPadding.left()
+ _caption.maxWidth()
+ st::msgPadding.right()));
auto captionMaxWidth = st::msgPadding.left()
+ _caption.maxWidth()
+ st::msgPadding.right();
const auto botTop = _parent->Get<FakeBotAboutTop>();
if (botTop) {
accumulate_max(captionMaxWidth, botTop->maxWidth);
}
const auto maxWithCaption = qMin(st::msgMaxWidth, captionMaxWidth);
newWidth = qMin(qMax(newWidth, maxWithCaption), thumbMaxWidth);
newHeight = adjustHeightForLessCrop(
scaled,
@ -248,6 +255,9 @@ QSize Gif::countCurrentSize(int newWidth) {
const auto captionw = newWidth
- st::msgPadding.left()
- st::msgPadding.right();
if (botTop) {
newHeight += botTop->height;
}
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
newHeight += st::msgPadding.bottom();
@ -349,12 +359,16 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
const auto outbg = context.outbg;
const auto inWebPage = (_parent->media() != this);
const auto isRound = _data->isVideoMessage();
const auto botTop = _parent->Get<FakeBotAboutTop>();
const auto rounding = inWebPage
? std::optional<Ui::BubbleRounding>()
: adjustedBubbleRoundingWithCaption(_caption);
if (bubble) {
if (!_caption.isEmpty()) {
if (botTop) {
painth -= botTop->height;
}
painth -= st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
painth -= st::msgPadding.bottom();
@ -674,10 +688,18 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
if (!unwrapped && !_caption.isEmpty()) {
p.setPen(stm->historyTextFg);
_parent->prepareCustomEmojiPaint(p, context, _caption);
_caption.draw(p, {
.position = QPoint(
auto top = painty + painth + st::mediaCaptionSkip;
if (botTop) {
botTop->text.drawLeftElided(
p,
st::msgPadding.left(),
painty + painth + st::mediaCaptionSkip),
top,
captionw,
_parent->width());
top += botTop->height;
}
_caption.draw(p, {
.position = QPoint(st::msgPadding.left(), top),
.availableWidth = captionw,
.palette = &stm->textPalette,
.spoiler = Ui::Text::DefaultSpoilerCache(),
@ -956,6 +978,9 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
request.forText()));
return result;
}
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
painth -= botTop->height;
}
painth -= st::mediaCaptionSkip;
}
const auto outbg = _parent->hasOutLayout();

View file

@ -187,6 +187,10 @@ QSize Photo::countOptimalSize() {
minHeight = adjustHeightForLessCrop(
dimensions,
{ maxWidth, minHeight });
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
accumulate_max(maxWidth, botTop->maxWidth);
minHeight += botTop->height;
}
minHeight += st::mediaCaptionSkip + _caption.minHeight();
if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom();
@ -214,11 +218,14 @@ QSize Photo::countCurrentSize(int newWidth) {
newWidth = qMax(pix.width(), minWidth);
auto newHeight = qMax(pix.height(), st::minPhotoSize);
if (_parent->hasBubble() && !_caption.isEmpty()) {
const auto maxWithCaption = qMin(
st::msgMaxWidth,
(st::msgPadding.left()
+ _caption.maxWidth()
+ st::msgPadding.right()));
auto captionMaxWidth = st::msgPadding.left()
+ _caption.maxWidth()
+ st::msgPadding.right();
const auto botTop = _parent->Get<FakeBotAboutTop>();
if (botTop) {
accumulate_max(captionMaxWidth, botTop->maxWidth);
}
const auto maxWithCaption = qMin(st::msgMaxWidth, captionMaxWidth);
newWidth = qMin(qMax(newWidth, maxWithCaption), thumbMaxWidth);
newHeight = adjustHeightForLessCrop(
dimensions,
@ -226,6 +233,9 @@ QSize Photo::countCurrentSize(int newWidth) {
const auto captionw = newWidth
- st::msgPadding.left()
- st::msgPadding.right();
if (botTop) {
newHeight += botTop->height;
}
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
newHeight += st::msgPadding.bottom();
@ -268,6 +278,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
}
}
const auto radial = isRadialAnimation();
const auto botTop = _parent->Get<FakeBotAboutTop>();
auto rthumb = style::rtlrect(paintx, painty, paintw, painth, width());
if (_serviceWidth > 0) {
@ -279,6 +290,9 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
if (bubble) {
if (!_caption.isEmpty()) {
painth -= st::mediaCaptionSkip + _caption.countHeight(captionw);
if (botTop) {
painth -= botTop->height;
}
if (isBubbleBottom()) {
painth -= st::msgPadding.bottom();
}
@ -348,10 +362,18 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
if (!_caption.isEmpty()) {
p.setPen(stm->historyTextFg);
_parent->prepareCustomEmojiPaint(p, context, _caption);
_caption.draw(p, {
.position = QPoint(
auto top = painty + painth + st::mediaCaptionSkip;
if (botTop) {
botTop->text.drawLeftElided(
p,
st::msgPadding.left(),
painty + painth + st::mediaCaptionSkip),
top,
captionw,
_parent->width());
top += botTop->height;
}
_caption.draw(p, {
.position = QPoint(st::msgPadding.left(), top),
.availableWidth = captionw,
.palette = &stm->textPalette,
.spoiler = Ui::Text::DefaultSpoilerCache(),
@ -592,6 +614,9 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
request.forText()));
return result;
}
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
painth -= botTop->height;
}
painth -= st::mediaCaptionSkip;
}
if (QRect(paintx, painty, paintw, painth).contains(point)) {