mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 13:47:05 +02:00
Redesign webpage/giveaway/ads bottom button.
This commit is contained in:
parent
16d18b437d
commit
3a84c6afdd
12 changed files with 315 additions and 222 deletions
|
@ -1126,6 +1126,15 @@ PeerData *HistoryItem::displayFrom() const {
|
|||
return author().get();
|
||||
}
|
||||
|
||||
uint8 HistoryItem::computeColorIndex() const {
|
||||
if (const auto from = displayFrom()) {
|
||||
return from->colorIndex();
|
||||
} else if (const auto info = hiddenSenderInfo()) {
|
||||
return info->colorIndex;
|
||||
}
|
||||
Unexpected("No displayFrom and no hiddenSenderInfo.");
|
||||
}
|
||||
|
||||
std::unique_ptr<HistoryView::Element> HistoryItem::createView(
|
||||
not_null<HistoryView::ElementDelegate*> delegate,
|
||||
HistoryView::Element *replacing) {
|
||||
|
@ -3253,7 +3262,8 @@ void HistoryItem::setSponsoredFrom(const Data::SponsoredFrom &from) {
|
|||
const auto sponsored = Get<HistoryMessageSponsored>();
|
||||
sponsored->sender = std::make_unique<HiddenSenderInfo>(
|
||||
from.title,
|
||||
false);
|
||||
false,
|
||||
from.peer ? from.peer->colorIndex() : std::optional<uint8>());
|
||||
sponsored->recommended = from.isRecommended;
|
||||
sponsored->isForceUserpicDisplay = from.isForceUserpicDisplay;
|
||||
if (from.userpic.location.valid()) {
|
||||
|
|
|
@ -498,6 +498,7 @@ public:
|
|||
[[nodiscard]] bool isDiscussionPost() const;
|
||||
[[nodiscard]] HistoryItem *lookupDiscussionPostOriginal() const;
|
||||
[[nodiscard]] PeerData *displayFrom() const;
|
||||
[[nodiscard]] uint8 computeColorIndex() const;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<HistoryView::Element> createView(
|
||||
not_null<HistoryView::ElementDelegate*> delegate,
|
||||
|
|
|
@ -108,11 +108,15 @@ void HistoryMessageVia::resize(int32 availw) const {
|
|||
}
|
||||
}
|
||||
|
||||
HiddenSenderInfo::HiddenSenderInfo(const QString &name, bool external)
|
||||
HiddenSenderInfo::HiddenSenderInfo(
|
||||
const QString &name,
|
||||
bool external,
|
||||
std::optional<uint8> colorIndex)
|
||||
: name(name)
|
||||
, colorIndex(Data::DecideColorIndex(Data::FakePeerIdForJustName(name)))
|
||||
, colorIndex(colorIndex.value_or(
|
||||
Data::DecideColorIndex(Data::FakePeerIdForJustName(name))))
|
||||
, emptyUserpic(
|
||||
Ui::EmptyUserpic::UserpicColor(colorIndex),
|
||||
Ui::EmptyUserpic::UserpicColor(this->colorIndex),
|
||||
(external
|
||||
? Ui::EmptyUserpic::ExternalName()
|
||||
: name)) {
|
||||
|
@ -399,13 +403,10 @@ bool HistoryMessageReply::updateData(
|
|||
}
|
||||
|
||||
if (resolvedMessage) {
|
||||
const auto peer = resolvedMessage->history()->peer;
|
||||
_colorIndexPlusOne = (!holder->out()
|
||||
&& (peer->isMegagroup() || peer->isChat())
|
||||
&& resolvedMessage->from()->isUser())
|
||||
? (resolvedMessage->from()->colorIndex() + 1)
|
||||
: uint8();
|
||||
} else if (!resolvedStory) {
|
||||
_colorIndexPlusOne = resolvedMessage->computeColorIndex() + 1;
|
||||
} else if (resolvedStory) {
|
||||
_colorIndexPlusOne = resolvedStory->peer()->colorIndex() + 1;
|
||||
} else {
|
||||
_unavailable = 1;
|
||||
}
|
||||
|
||||
|
@ -680,14 +681,15 @@ void HistoryMessageReply::paint(
|
|||
const auto rect = QRect(x, y, w, _height);
|
||||
const auto hasQuote = !_fields.quote.empty();
|
||||
const auto selected = context.selected();
|
||||
const auto colorIndexPlusOne = context.outbg ? 0 : _colorIndexPlusOne;
|
||||
const auto cache = !inBubble
|
||||
? (hasQuote
|
||||
? st->serviceQuoteCache()
|
||||
: st->serviceReplyCache()).get()
|
||||
: _colorIndexPlusOne
|
||||
: colorIndexPlusOne
|
||||
? (hasQuote
|
||||
? st->coloredQuoteCache(selected, _colorIndexPlusOne - 1)
|
||||
: st->coloredReplyCache(selected, _colorIndexPlusOne - 1)).get()
|
||||
? st->coloredQuoteCache(selected, colorIndexPlusOne - 1)
|
||||
: st->coloredReplyCache(selected, colorIndexPlusOne - 1)).get()
|
||||
: (hasQuote ? stm->quoteCache : stm->replyCache).get();
|
||||
const auto "eSt = hasQuote
|
||||
? st::messageTextStyle.blockquote
|
||||
|
@ -762,10 +764,10 @@ void HistoryMessageReply::paint(
|
|||
w -= textLeft + st::historyReplyPadding.right();
|
||||
p.setPen(!inBubble
|
||||
? st->msgImgReplyBarColor()
|
||||
: _colorIndexPlusOne
|
||||
: colorIndexPlusOne
|
||||
? HistoryView::FromNameFg(
|
||||
context,
|
||||
_colorIndexPlusOne - 1)
|
||||
colorIndexPlusOne - 1)
|
||||
: stm->msgServiceFg);
|
||||
_name.drawLeftElided(p, x + textLeft, y + st::historyReplyPadding.top(), w, w + 2 * x + 2 * textLeft);
|
||||
if (originalVia && w > _name.maxWidth() + st::msgServiceFont->spacew) {
|
||||
|
@ -782,8 +784,8 @@ void HistoryMessageReply::paint(
|
|||
y + st::historyReplyPadding.top() + st::msgServiceNameFont->height);
|
||||
auto replyToTextPalette = &(!inBubble
|
||||
? st->imgReplyTextPalette()
|
||||
: _colorIndexPlusOne
|
||||
? st->coloredTextPalette(selected, _colorIndexPlusOne - 1)
|
||||
: colorIndexPlusOne
|
||||
? st->coloredTextPalette(selected, colorIndexPlusOne - 1)
|
||||
: stm->replyTextPalette);
|
||||
if (_fields.storyId) {
|
||||
st::dialogsMiniReplyStory.icon.icon.paint(
|
||||
|
|
|
@ -85,7 +85,10 @@ struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited, Hist
|
|||
|
||||
class HiddenSenderInfo {
|
||||
public:
|
||||
HiddenSenderInfo(const QString &name, bool external);
|
||||
HiddenSenderInfo(
|
||||
const QString &name,
|
||||
bool external,
|
||||
std::optional<uint8> colorIndex = {});
|
||||
|
||||
QString name;
|
||||
QString firstName;
|
||||
|
|
|
@ -1330,12 +1330,9 @@ void Message::paintFromName(
|
|||
const auto from = item->displayFrom();
|
||||
const auto info = from ? nullptr : item->hiddenSenderInfo();
|
||||
Assert(from || info);
|
||||
const auto service = (context.outbg || item->isPost());
|
||||
const auto st = context.st;
|
||||
const auto nameFg = !service
|
||||
const auto nameFg = !context.outbg
|
||||
? FromNameFg(context, from ? from->colorIndex() : info->colorIndex)
|
||||
: item->isSponsored()
|
||||
? st->boxTextFgGood()
|
||||
: stm->msgServiceFg;
|
||||
const auto nameText = [&] {
|
||||
if (from) {
|
||||
|
@ -3021,9 +3018,13 @@ void Message::updateViewButtonExistence() {
|
|||
return;
|
||||
}
|
||||
auto repainter = [=] { repaint(); };
|
||||
const auto index = item->computeColorIndex();
|
||||
_viewButton = sponsored
|
||||
? std::make_unique<ViewButton>(sponsored, std::move(repainter))
|
||||
: std::make_unique<ViewButton>(media, std::move(repainter));
|
||||
? std::make_unique<ViewButton>(
|
||||
sponsored,
|
||||
index,
|
||||
std::move(repainter))
|
||||
: std::make_unique<ViewButton>(media, index, std::move(repainter));
|
||||
}
|
||||
|
||||
void Message::initLogEntryOriginal() {
|
||||
|
|
|
@ -52,85 +52,31 @@ inline auto SponsoredPhrase(SponsoredType type) {
|
|||
return Ui::Text::Upper(phrase(tr::now));
|
||||
}
|
||||
|
||||
inline auto WebPageToPhrase(not_null<WebPageData*> webpage) {
|
||||
const auto type = webpage->type;
|
||||
return Ui::Text::Upper((type == WebPageType::Theme)
|
||||
? tr::lng_view_button_theme(tr::now)
|
||||
: (type == WebPageType::Story)
|
||||
? tr::lng_view_button_story(tr::now)
|
||||
: (type == WebPageType::Message)
|
||||
? tr::lng_view_button_message(tr::now)
|
||||
: (type == WebPageType::Group)
|
||||
? tr::lng_view_button_group(tr::now)
|
||||
: (type == WebPageType::WallPaper)
|
||||
? tr::lng_view_button_background(tr::now)
|
||||
: (type == WebPageType::Channel)
|
||||
? tr::lng_view_button_channel(tr::now)
|
||||
: (type == WebPageType::GroupWithRequest
|
||||
|| type == WebPageType::ChannelWithRequest)
|
||||
? tr::lng_view_button_request_join(tr::now)
|
||||
: (type == WebPageType::ChannelBoost)
|
||||
? tr::lng_view_button_boost(tr::now)
|
||||
: (type == WebPageType::VoiceChat)
|
||||
? tr::lng_view_button_voice_chat(tr::now)
|
||||
: (type == WebPageType::Livestream)
|
||||
? tr::lng_view_button_voice_chat_channel(tr::now)
|
||||
: (type == WebPageType::Bot)
|
||||
? tr::lng_view_button_bot(tr::now)
|
||||
: (type == WebPageType::User)
|
||||
? tr::lng_view_button_user(tr::now)
|
||||
: (type == WebPageType::BotApp)
|
||||
? tr::lng_view_button_bot_app(tr::now)
|
||||
: QString());
|
||||
}
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr MakeMediaButtonClickHandler(
|
||||
not_null<Data::Media*> media) {
|
||||
if (const auto giveaway = media->giveaway()) {
|
||||
const auto peer = media->parent()->history()->peer;
|
||||
const auto messageId = media->parent()->id;
|
||||
if (media->parent()->isSending() || media->parent()->hasFailed()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto info = *giveaway;
|
||||
return std::make_shared<LambdaClickHandler>([=](
|
||||
ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
const auto controller = my.sessionWindow.get();
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
ResolveGiveawayInfo(controller, peer, messageId, info);
|
||||
});
|
||||
const auto giveaway = media->giveaway();
|
||||
Assert(giveaway != nullptr);
|
||||
const auto peer = media->parent()->history()->peer;
|
||||
const auto messageId = media->parent()->id;
|
||||
if (media->parent()->isSending() || media->parent()->hasFailed()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto webpage = media->webpage();
|
||||
Assert(webpage != nullptr);
|
||||
|
||||
const auto url = webpage->url;
|
||||
const auto type = webpage->type;
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
const auto info = *giveaway;
|
||||
return std::make_shared<LambdaClickHandler>([=](
|
||||
ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
if (type == WebPageType::BotApp) {
|
||||
// Bot Web Apps always show confirmation on hidden urls.
|
||||
//
|
||||
// But from the dedicated "Open App" button we don't want
|
||||
// to request users confirmation on non-first app opening.
|
||||
UrlClickHandler::Open(url, context.other);
|
||||
} else {
|
||||
HiddenUrlClickHandler::Open(url, context.other);
|
||||
}
|
||||
const auto controller = my.sessionWindow.get();
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
ResolveGiveawayInfo(controller, peer, messageId, info);
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] QString MakeMediaButtonText(not_null<Data::Media*> media) {
|
||||
if (const auto giveaway = media->giveaway()) {
|
||||
return Ui::Text::Upper(tr::lng_prizes_how_works(tr::now));
|
||||
}
|
||||
const auto webpage = media->webpage();
|
||||
Assert(webpage != nullptr);
|
||||
return WebPageToPhrase(webpage);
|
||||
const auto giveaway = media->giveaway();
|
||||
Assert(giveaway != nullptr);
|
||||
return Ui::Text::Upper(tr::lng_prizes_how_works(tr::now));
|
||||
}
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr SponsoredLink(
|
||||
|
@ -178,8 +124,12 @@ inline auto WebPageToPhrase(not_null<WebPageData*> webpage) {
|
|||
struct ViewButton::Inner {
|
||||
Inner(
|
||||
not_null<HistoryMessageSponsored*> sponsored,
|
||||
uint8 colorIndex,
|
||||
Fn<void()> updateCallback);
|
||||
Inner(
|
||||
not_null<Data::Media*> media,
|
||||
uint8 colorIndex,
|
||||
Fn<void()> updateCallback);
|
||||
Inner(not_null<Data::Media*> media, Fn<void()> updateCallback);
|
||||
|
||||
void updateMask(int height);
|
||||
void toggleRipple(bool pressed);
|
||||
|
@ -187,59 +137,40 @@ struct ViewButton::Inner {
|
|||
const style::margins &margins;
|
||||
const ClickHandlerPtr link;
|
||||
const Fn<void()> updateCallback;
|
||||
bool belowInfo = true;
|
||||
bool externalLink = false;
|
||||
int lastWidth = 0;
|
||||
uint32 lastWidth : 24 = 0;
|
||||
uint32 colorIndex : 6 = 0;
|
||||
uint32 aboveInfo : 1 = 0;
|
||||
uint32 externalLink : 1 = 0;
|
||||
QPoint lastPoint;
|
||||
std::unique_ptr<Ui::RippleAnimation> ripple;
|
||||
Ui::Text::String text;
|
||||
};
|
||||
|
||||
bool ViewButton::MediaHasViewButton(not_null<Data::Media*> media) {
|
||||
return media->webpage()
|
||||
? MediaHasViewButton(media->webpage())
|
||||
: (media->giveaway() != nullptr);
|
||||
}
|
||||
|
||||
bool ViewButton::MediaHasViewButton(
|
||||
not_null<WebPageData*> webpage) {
|
||||
const auto type = webpage->type;
|
||||
return (type == WebPageType::Message)
|
||||
|| (type == WebPageType::Group)
|
||||
|| (type == WebPageType::Channel)
|
||||
|| (type == WebPageType::ChannelBoost)
|
||||
// || (type == WebPageType::Bot)
|
||||
|| (type == WebPageType::User)
|
||||
|| (type == WebPageType::VoiceChat)
|
||||
|| (type == WebPageType::Livestream)
|
||||
|| (type == WebPageType::BotApp)
|
||||
|| ((type == WebPageType::Theme)
|
||||
&& webpage->document
|
||||
&& webpage->document->isTheme())
|
||||
|| ((type == WebPageType::Story)
|
||||
&& (webpage->photo || webpage->document))
|
||||
|| ((type == WebPageType::WallPaper)
|
||||
&& webpage->document
|
||||
&& webpage->document->isWallPaper());
|
||||
return (media->giveaway() != nullptr);
|
||||
}
|
||||
|
||||
ViewButton::Inner::Inner(
|
||||
not_null<HistoryMessageSponsored*> sponsored,
|
||||
uint8 colorIndex,
|
||||
Fn<void()> updateCallback)
|
||||
: margins(st::historyViewButtonMargins)
|
||||
, link(SponsoredLink(sponsored))
|
||||
, updateCallback(std::move(updateCallback))
|
||||
, externalLink(sponsored->type == SponsoredType::ExternalLink)
|
||||
, colorIndex(colorIndex)
|
||||
, externalLink((sponsored->type == SponsoredType::ExternalLink) ? 1 : 0)
|
||||
, text(st::historyViewButtonTextStyle, SponsoredPhrase(sponsored->type)) {
|
||||
}
|
||||
|
||||
ViewButton::Inner::Inner(
|
||||
not_null<Data::Media*> media,
|
||||
uint8 colorIndex,
|
||||
Fn<void()> updateCallback)
|
||||
: margins(st::historyViewButtonMargins)
|
||||
, link(MakeMediaButtonClickHandler(media))
|
||||
, updateCallback(std::move(updateCallback))
|
||||
, belowInfo(false)
|
||||
, colorIndex(colorIndex)
|
||||
, aboveInfo(1)
|
||||
, text(st::historyViewButtonTextStyle, MakeMediaButtonText(media)) {
|
||||
}
|
||||
|
||||
|
@ -264,14 +195,22 @@ void ViewButton::Inner::toggleRipple(bool pressed) {
|
|||
|
||||
ViewButton::ViewButton(
|
||||
not_null<HistoryMessageSponsored*> sponsored,
|
||||
uint8 colorIndex,
|
||||
Fn<void()> updateCallback)
|
||||
: _inner(std::make_unique<Inner>(sponsored, std::move(updateCallback))) {
|
||||
: _inner(std::make_unique<Inner>(
|
||||
sponsored,
|
||||
colorIndex,
|
||||
std::move(updateCallback))) {
|
||||
}
|
||||
|
||||
ViewButton::ViewButton(
|
||||
not_null<Data::Media*> media,
|
||||
uint8 colorIndex,
|
||||
Fn<void()> updateCallback)
|
||||
: _inner(std::make_unique<Inner>(media, std::move(updateCallback))) {
|
||||
: _inner(std::make_unique<Inner>(
|
||||
media,
|
||||
colorIndex,
|
||||
std::move(updateCallback))) {
|
||||
}
|
||||
|
||||
ViewButton::~ViewButton() {
|
||||
|
@ -286,7 +225,7 @@ int ViewButton::height() const {
|
|||
}
|
||||
|
||||
bool ViewButton::belowMessageInfo() const {
|
||||
return _inner->belowInfo;
|
||||
return !_inner->aboveInfo;
|
||||
}
|
||||
|
||||
void ViewButton::draw(
|
||||
|
@ -295,45 +234,40 @@ void ViewButton::draw(
|
|||
const Ui::ChatPaintContext &context) {
|
||||
const auto stm = context.messageStyle();
|
||||
|
||||
const auto selected = context.selected();
|
||||
const auto cache = context.outbg
|
||||
? stm->replyCache.get()
|
||||
: context.st->coloredReplyCache(selected, _inner->colorIndex).get();
|
||||
const auto radius = st::historyPagePreview.radius;
|
||||
|
||||
if (_inner->ripple && !_inner->ripple->empty()) {
|
||||
const auto opacity = p.opacity();
|
||||
p.setOpacity(st::historyPollRippleOpacity);
|
||||
const auto colorOverride = &stm->msgWaveformInactive->c;
|
||||
_inner->ripple->paint(p, r.left(), r.top(), r.width(), colorOverride);
|
||||
p.setOpacity(opacity);
|
||||
_inner->ripple->paint(p, r.left(), r.top(), r.width(), &cache->bg);
|
||||
}
|
||||
|
||||
p.save();
|
||||
{
|
||||
PainterHighQualityEnabler hq(p);
|
||||
auto pen = stm->fwdTextPalette.linkFg->p;
|
||||
pen.setWidth(st::lineWidth);
|
||||
p.setPen(pen);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
const auto half = st::lineWidth / 2.;
|
||||
const auto rf = QRectF(r).marginsRemoved({ half, half, half, half });
|
||||
p.drawRoundedRect(rf, st::roundRadiusLarge, st::roundRadiusLarge);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(cache->bg);
|
||||
p.drawRoundedRect(r, radius, radius);
|
||||
|
||||
_inner->text.drawElided(
|
||||
p.setPen(cache->outline);
|
||||
_inner->text.drawElided(
|
||||
p,
|
||||
r.left(),
|
||||
r.top() + (r.height() - _inner->text.minHeight()) / 2,
|
||||
r.width(),
|
||||
1,
|
||||
style::al_top);
|
||||
|
||||
if (_inner->externalLink) {
|
||||
const auto &icon = st::msgBotKbUrlIcon;
|
||||
const auto padding = st::msgBotKbIconPadding;
|
||||
icon.paint(
|
||||
p,
|
||||
r.left(),
|
||||
r.top() + (r.height() - _inner->text.minHeight()) / 2,
|
||||
r.left() + r.width() - icon.width() - padding,
|
||||
r.top() + padding,
|
||||
r.width(),
|
||||
1,
|
||||
style::al_center);
|
||||
|
||||
if (_inner->externalLink) {
|
||||
const auto &icon = st::msgBotKbUrlIcon;
|
||||
const auto padding = st::msgBotKbIconPadding;
|
||||
icon.paint(
|
||||
p,
|
||||
r.left() + r.width() - icon.width() - padding,
|
||||
r.top() + padding,
|
||||
r.width(),
|
||||
stm->fwdTextPalette.linkFg->c);
|
||||
}
|
||||
cache->outline);
|
||||
}
|
||||
p.restore();
|
||||
if (_inner->lastWidth != r.width()) {
|
||||
_inner->lastWidth = r.width();
|
||||
resized();
|
||||
|
|
|
@ -25,14 +25,16 @@ class ViewButton {
|
|||
public:
|
||||
ViewButton(
|
||||
not_null<HistoryMessageSponsored*> sponsored,
|
||||
uint8 colorIndex,
|
||||
Fn<void()> updateCallback);
|
||||
ViewButton(
|
||||
not_null<Data::Media*> media,
|
||||
uint8 colorIndex,
|
||||
Fn<void()> updateCallback);
|
||||
ViewButton(not_null<Data::Media*> media, Fn<void()> updateCallback);
|
||||
~ViewButton();
|
||||
|
||||
[[nodiscard]] static bool MediaHasViewButton(
|
||||
not_null<Data::Media*> media);
|
||||
[[nodiscard]] static bool MediaHasViewButton(
|
||||
not_null<WebPageData*> webpage);
|
||||
|
||||
[[nodiscard]] int height() const;
|
||||
[[nodiscard]] bool belowMessageInfo() const;
|
||||
|
|
|
@ -34,6 +34,7 @@ Game::Game(
|
|||
: Media(parent)
|
||||
, _st(st::historyPagePreview)
|
||||
, _data(data)
|
||||
, _colorIndex(parent->data()->computeColorIndex())
|
||||
, _title(st::msgMinWidth - _st.padding.left() - _st.padding.right())
|
||||
, _description(st::msgMinWidth - _st.padding.left() - _st.padding.right()) {
|
||||
if (!consumed.text.isEmpty()) {
|
||||
|
@ -48,13 +49,6 @@ Game::Game(
|
|||
context);
|
||||
}
|
||||
history()->owner().registerGameView(_data, _parent);
|
||||
|
||||
const auto from = parent->data()->displayFrom();
|
||||
const auto info = from ? nullptr : parent->data()->hiddenSenderInfo();
|
||||
Assert(from || info);
|
||||
_colorIndexPlusOne = !parent->data()->isPost()
|
||||
? ((from ? from->colorIndex() : info->colorIndex) + 1)
|
||||
: 0;
|
||||
}
|
||||
|
||||
QSize Game::countOptimalSize() {
|
||||
|
@ -226,25 +220,36 @@ void Game::draw(Painter &p, const PaintContext &context) const {
|
|||
auto paintw = inner.width();
|
||||
|
||||
const auto selected = context.selected();
|
||||
const auto useColorIndex = context.outbg ? 0 : _colorIndexPlusOne;
|
||||
const auto cache = useColorIndex
|
||||
? st->coloredReplyCache(selected, useColorIndex - 1).get()
|
||||
: stm->replyCache.get();
|
||||
const auto cache = context.outbg
|
||||
? stm->replyCache.get()
|
||||
: st->coloredReplyCache(selected, _colorIndex).get();
|
||||
Ui::Text::ValidateQuotePaintCache(*cache, _st);
|
||||
Ui::Text::FillQuotePaint(p, outer, *cache, _st);
|
||||
|
||||
auto lineHeight = UnitedLineHeight();
|
||||
if (_titleLines) {
|
||||
p.setPen(cache->outline);
|
||||
p.setTextPalette(useColorIndex
|
||||
? st->coloredTextPalette(selected, useColorIndex - 1)
|
||||
: stm->semiboldPalette);
|
||||
p.setTextPalette(context.outbg
|
||||
? stm->semiboldPalette
|
||||
: st->coloredTextPalette(selected, _colorIndex));
|
||||
|
||||
auto endskip = 0;
|
||||
if (_title.hasSkipBlock()) {
|
||||
endskip = _parent->skipBlockWidth();
|
||||
}
|
||||
_title.drawLeftElided(p, inner.left(), tshift, paintw, width(), _titleLines, style::al_left, 0, -1, endskip, false, context.selection);
|
||||
_title.drawLeftElided(
|
||||
p,
|
||||
inner.left(),
|
||||
tshift,
|
||||
paintw,
|
||||
width(),
|
||||
_titleLines,
|
||||
style::al_left,
|
||||
0,
|
||||
-1,
|
||||
endskip,
|
||||
false,
|
||||
context.selection);
|
||||
tshift += _titleLines * lineHeight;
|
||||
|
||||
p.setTextPalette(stm->textPalette);
|
||||
|
|
|
@ -105,8 +105,8 @@ private:
|
|||
|
||||
int _gameTagWidth = 0;
|
||||
int _descriptionLines = 0;
|
||||
int _titleLines : 24 = 0;
|
||||
int _colorIndexPlusOne : 8 = 0;
|
||||
uint32 _titleLines : 24 = 0;
|
||||
uint32 _colorIndex : 8 = 0;
|
||||
|
||||
Ui::Text::String _title;
|
||||
Ui::Text::String _description;
|
||||
|
|
|
@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/text/format_values.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/power_saving.h"
|
||||
#include "data/data_session.h"
|
||||
|
@ -84,6 +85,38 @@ std::vector<std::unique_ptr<Data::Media>> PrepareCollageMedia(
|
|||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString PageToPhrase(not_null<WebPageData*> webpage) {
|
||||
const auto type = webpage->type;
|
||||
return Ui::Text::Upper((type == WebPageType::Theme)
|
||||
? tr::lng_view_button_theme(tr::now)
|
||||
: (type == WebPageType::Story)
|
||||
? tr::lng_view_button_story(tr::now)
|
||||
: (type == WebPageType::Message)
|
||||
? tr::lng_view_button_message(tr::now)
|
||||
: (type == WebPageType::Group)
|
||||
? tr::lng_view_button_group(tr::now)
|
||||
: (type == WebPageType::WallPaper)
|
||||
? tr::lng_view_button_background(tr::now)
|
||||
: (type == WebPageType::Channel)
|
||||
? tr::lng_view_button_channel(tr::now)
|
||||
: (type == WebPageType::GroupWithRequest
|
||||
|| type == WebPageType::ChannelWithRequest)
|
||||
? tr::lng_view_button_request_join(tr::now)
|
||||
: (type == WebPageType::ChannelBoost)
|
||||
? tr::lng_view_button_boost(tr::now)
|
||||
: (type == WebPageType::VoiceChat)
|
||||
? tr::lng_view_button_voice_chat(tr::now)
|
||||
: (type == WebPageType::Livestream)
|
||||
? tr::lng_view_button_voice_chat_channel(tr::now)
|
||||
: (type == WebPageType::Bot)
|
||||
? tr::lng_view_button_bot(tr::now)
|
||||
: (type == WebPageType::User)
|
||||
? tr::lng_view_button_user(tr::now)
|
||||
: (type == WebPageType::BotApp)
|
||||
? tr::lng_view_button_bot_app(tr::now)
|
||||
: QString());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WebPage::WebPage(
|
||||
|
@ -92,23 +125,47 @@ WebPage::WebPage(
|
|||
: Media(parent)
|
||||
, _st(st::historyPagePreview)
|
||||
, _data(data)
|
||||
, _colorIndex(parent->data()->computeColorIndex())
|
||||
, _siteName(st::msgMinWidth - _st.padding.left() - _st.padding.right())
|
||||
, _title(st::msgMinWidth - _st.padding.left() - _st.padding.right())
|
||||
, _description(st::msgMinWidth - _st.padding.left() - _st.padding.right()) {
|
||||
history()->owner().registerWebPageView(_data, _parent);
|
||||
}
|
||||
|
||||
const auto from = parent->data()->displayFrom();
|
||||
const auto info = from ? nullptr : parent->data()->hiddenSenderInfo();
|
||||
Assert(from || info);
|
||||
_colorIndexPlusOne = !parent->data()->isPost()
|
||||
? ((from ? from->colorIndex() : info->colorIndex) + 1)
|
||||
: 0;
|
||||
bool WebPage::HasButton(not_null<WebPageData*> webpage) {
|
||||
const auto type = webpage->type;
|
||||
return (type == WebPageType::Message)
|
||||
|| (type == WebPageType::Group)
|
||||
|| (type == WebPageType::Channel)
|
||||
|| (type == WebPageType::ChannelBoost)
|
||||
// || (type == WebPageType::Bot)
|
||||
|| (type == WebPageType::User)
|
||||
|| (type == WebPageType::VoiceChat)
|
||||
|| (type == WebPageType::Livestream)
|
||||
|| (type == WebPageType::BotApp)
|
||||
|| ((type == WebPageType::Theme)
|
||||
&& webpage->document
|
||||
&& webpage->document->isTheme())
|
||||
|| ((type == WebPageType::Story)
|
||||
&& (webpage->photo || webpage->document))
|
||||
|| ((type == WebPageType::WallPaper)
|
||||
&& webpage->document
|
||||
&& webpage->document->isWallPaper());
|
||||
}
|
||||
|
||||
QSize WebPage::countOptimalSize() {
|
||||
if (_data->pendingTill) {
|
||||
return { 0, 0 };
|
||||
}
|
||||
|
||||
// Detect _openButtonWidth before counting paddings.
|
||||
_openButton = QString();
|
||||
_openButtonWidth = 0;
|
||||
if (HasButton(_data)) {
|
||||
_openButton = PageToPhrase(_data);
|
||||
_openButtonWidth = st::semiboldFont->width(_openButton);
|
||||
}
|
||||
|
||||
const auto padding = inBubblePadding() + innerMargin();
|
||||
const auto versionChanged = (_dataVersion != _data->version);
|
||||
if (versionChanged) {
|
||||
|
@ -127,6 +184,13 @@ QSize WebPage::countOptimalSize() {
|
|||
|
||||
if (!_openl && !_data->url.isEmpty()) {
|
||||
const auto previewOfHiddenUrl = [&] {
|
||||
if (_data->type == WebPageType::BotApp) {
|
||||
// Bot Web Apps always show confirmation on hidden urls.
|
||||
//
|
||||
// But from the dedicated "Open App" button we don't want
|
||||
// to request users confirmation on non-first app opening.
|
||||
return false;
|
||||
}
|
||||
const auto simplify = [](const QString &url) {
|
||||
auto result = url.toLower();
|
||||
if (result.endsWith('/')) {
|
||||
|
@ -175,7 +239,7 @@ QSize WebPage::countOptimalSize() {
|
|||
? _data->author
|
||||
: _data->title);
|
||||
if (!_collage.empty()) {
|
||||
_asArticle = false;
|
||||
_asArticle = 0;
|
||||
} else if (!_data->document
|
||||
&& _data->photo
|
||||
&& _data->type != WebPageType::Photo
|
||||
|
@ -183,22 +247,22 @@ QSize WebPage::countOptimalSize() {
|
|||
&& _data->type != WebPageType::Story
|
||||
&& _data->type != WebPageType::Video) {
|
||||
if (_data->type == WebPageType::Profile) {
|
||||
_asArticle = true;
|
||||
_asArticle = 1;
|
||||
} else if (_data->siteName == u"Twitter"_q
|
||||
|| _data->siteName == u"Facebook"_q
|
||||
|| _data->type == WebPageType::ArticleWithIV) {
|
||||
_asArticle = false;
|
||||
_asArticle = 0;
|
||||
} else {
|
||||
_asArticle = true;
|
||||
_asArticle = 1;
|
||||
}
|
||||
if (_asArticle
|
||||
&& _data->description.text.isEmpty()
|
||||
&& title.isEmpty()
|
||||
&& _data->siteName.isEmpty()) {
|
||||
_asArticle = false;
|
||||
_asArticle = 0;
|
||||
}
|
||||
} else {
|
||||
_asArticle = false;
|
||||
_asArticle = 0;
|
||||
}
|
||||
|
||||
// init attach
|
||||
|
@ -211,8 +275,6 @@ QSize WebPage::countOptimalSize() {
|
|||
_data->url);
|
||||
}
|
||||
|
||||
_hasViewButton = ViewButton::MediaHasViewButton(_data);
|
||||
|
||||
// init strings
|
||||
if (_description.isEmpty() && !_data->description.text.isEmpty()) {
|
||||
auto text = _data->description;
|
||||
|
@ -306,6 +368,10 @@ QSize WebPage::countOptimalSize() {
|
|||
_duration = Ui::FormatDurationText(_data->duration);
|
||||
_durationWidth = st::msgDateFont->width(_duration);
|
||||
}
|
||||
if (_openButtonWidth) {
|
||||
const auto &margins = st::historyPageButtonPadding;
|
||||
maxWidth += margins.left() + _openButtonWidth + margins.right();
|
||||
}
|
||||
maxWidth += padding.left() + padding.right();
|
||||
minHeight += padding.top() + padding.bottom();
|
||||
|
||||
|
@ -472,13 +538,19 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
auto attachAdditionalInfoText = _attach ? _attach->additionalInfoString() : QString();
|
||||
|
||||
const auto selected = context.selected();
|
||||
const auto useColorIndex = context.outbg ? 0 : _colorIndexPlusOne;
|
||||
const auto cache = useColorIndex
|
||||
? st->coloredReplyCache(selected, useColorIndex - 1).get()
|
||||
: stm->replyCache.get();
|
||||
const auto cache = context.outbg
|
||||
? stm->replyCache.get()
|
||||
: st->coloredReplyCache(selected, _colorIndex).get();
|
||||
Ui::Text::ValidateQuotePaintCache(*cache, _st);
|
||||
Ui::Text::FillQuotePaint(p, outer, *cache, _st);
|
||||
|
||||
if (_ripple) {
|
||||
_ripple->paint(p, outer.x(), outer.y(), width(), &cache->bg);
|
||||
if (_ripple->empty()) {
|
||||
_ripple = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto lineHeight = UnitedLineHeight();
|
||||
if (asArticle()) {
|
||||
ensurePhotoMediaCreated();
|
||||
|
@ -522,9 +594,9 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
if (_siteNameLines) {
|
||||
p.setPen(cache->outline);
|
||||
p.setTextPalette(useColorIndex
|
||||
? st->coloredTextPalette(selected, useColorIndex - 1)
|
||||
: stm->semiboldPalette);
|
||||
p.setTextPalette(context.outbg
|
||||
? stm->semiboldPalette
|
||||
: st->coloredTextPalette(selected, _colorIndex));
|
||||
|
||||
auto endskip = 0;
|
||||
if (_siteName.hasSkipBlock()) {
|
||||
|
@ -620,6 +692,21 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
p.drawTextLeft(st::msgPadding.left(), outer.y() + outer.height() + st::mediaInBubbleSkip, width(), attachAdditionalInfoText);
|
||||
}
|
||||
}
|
||||
|
||||
if (_openButtonWidth) {
|
||||
p.setFont(st::semiboldFont);
|
||||
p.setPen(cache->outline);
|
||||
const auto end = inner.y() + inner.height() + _st.padding.bottom();
|
||||
const auto line = st::historyPageButtonLine;
|
||||
auto color = cache->outline;
|
||||
color.setAlphaF(color.alphaF() * 0.3);
|
||||
p.fillRect(inner.x(), end, inner.width(), line, color);
|
||||
const auto top = end + st::historyPageButtonPadding.top();
|
||||
p.drawText(
|
||||
inner.x() + (inner.width() - _openButtonWidth) / 2,
|
||||
top + st::semiboldFont->ascent,
|
||||
_openButton);
|
||||
}
|
||||
}
|
||||
|
||||
bool WebPage::asArticle() const {
|
||||
|
@ -715,9 +802,10 @@ TextState WebPage::textState(QPoint point, StateRequest request) const {
|
|||
result.link = replaceAttachLink(result.link);
|
||||
}
|
||||
}
|
||||
if (!result.link && inner.contains(point)) {
|
||||
if (!result.link && outer.contains(point)) {
|
||||
result.link = _openl;
|
||||
}
|
||||
_lastPoint = point - outer.topLeft();
|
||||
|
||||
result.symbol += symbolAdd;
|
||||
return result;
|
||||
|
@ -773,13 +861,35 @@ TextSelection WebPage::adjustSelection(TextSelection selection, TextSelectType t
|
|||
return { siteNameSelection.from, fromDescriptionSelection(descriptionSelection).to };
|
||||
}
|
||||
|
||||
void WebPage::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||
void WebPage::clickHandlerActiveChanged(
|
||||
const ClickHandlerPtr &p,
|
||||
bool active) {
|
||||
if (_attach) {
|
||||
_attach->clickHandlerActiveChanged(p, active);
|
||||
}
|
||||
}
|
||||
|
||||
void WebPage::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
|
||||
void WebPage::clickHandlerPressedChanged(
|
||||
const ClickHandlerPtr &p,
|
||||
bool pressed) {
|
||||
if (p == _openl) {
|
||||
if (pressed) {
|
||||
if (!_ripple) {
|
||||
const auto full = QRect(0, 0, width(), height());
|
||||
const auto outer = full.marginsRemoved(inBubblePadding());
|
||||
const auto owner = &parent()->history()->owner();
|
||||
_ripple = std::make_unique<Ui::RippleAnimation>(
|
||||
st::defaultRippleAnimation,
|
||||
Ui::RippleAnimation::RoundRectMask(
|
||||
outer.size(),
|
||||
_st.radius),
|
||||
[=] { owner->requestViewRepaint(parent()); });
|
||||
}
|
||||
_ripple->add(_lastPoint);
|
||||
} else if (_ripple) {
|
||||
_ripple->lastStop();
|
||||
}
|
||||
}
|
||||
if (_attach) {
|
||||
_attach->clickHandlerPressedChanged(p, pressed);
|
||||
}
|
||||
|
@ -811,6 +921,15 @@ QString WebPage::additionalInfoString() const {
|
|||
return _attach ? _attach->additionalInfoString() : QString();
|
||||
}
|
||||
|
||||
bool WebPage::toggleSelectionByHandlerClick(
|
||||
const ClickHandlerPtr &p) const {
|
||||
return _attach && _attach->toggleSelectionByHandlerClick(p);
|
||||
}
|
||||
|
||||
bool WebPage::dragItemByHandler(const ClickHandlerPtr &p) const {
|
||||
return _attach && _attach->dragItemByHandler(p);
|
||||
}
|
||||
|
||||
TextForMimeData WebPage::selectedText(TextSelection selection) const {
|
||||
auto siteNameResult = _siteName.toTextForMimeData(selection);
|
||||
auto titleResult = _title.toTextForMimeData(
|
||||
|
@ -846,7 +965,8 @@ QMargins WebPage::inBubblePadding() const {
|
|||
}
|
||||
|
||||
QMargins WebPage::innerMargin() const {
|
||||
return _st.padding;
|
||||
const auto button = _openButtonWidth ? st::historyPageButtonHeight : 0;
|
||||
return _st.padding + QMargins(0, 0, 0, button);
|
||||
}
|
||||
|
||||
bool WebPage::isLogEntryOriginal() const {
|
||||
|
|
|
@ -14,6 +14,10 @@ class Media;
|
|||
class PhotoMedia;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class RippleAnimation;
|
||||
} // namespace Ui
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
class WebPage : public Media {
|
||||
|
@ -22,6 +26,8 @@ public:
|
|||
not_null<Element*> parent,
|
||||
not_null<WebPageData*> data);
|
||||
|
||||
[[nodiscard]] static bool HasButton(not_null<WebPageData*> data);
|
||||
|
||||
void refreshParentId(not_null<HistoryItem*> realParent) override;
|
||||
|
||||
void draw(Painter &p, const PaintContext &context) const override;
|
||||
|
@ -38,21 +44,21 @@ public:
|
|||
return _title.length() + _description.length();
|
||||
}
|
||||
bool hasTextForCopy() const override {
|
||||
return false; // we do not add _title and _description in FullSelection text copy.
|
||||
// We do not add _title and _description in FullSelection text copy.
|
||||
return false;
|
||||
}
|
||||
QString additionalInfoString() const override;
|
||||
|
||||
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
|
||||
return _attach && _attach->toggleSelectionByHandlerClick(p);
|
||||
}
|
||||
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
|
||||
return _attach && _attach->dragItemByHandler(p);
|
||||
}
|
||||
bool toggleSelectionByHandlerClick(
|
||||
const ClickHandlerPtr &p) const override;
|
||||
bool dragItemByHandler(const ClickHandlerPtr &p) const override;
|
||||
|
||||
TextForMimeData selectedText(TextSelection selection) const override;
|
||||
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||
void clickHandlerActiveChanged(
|
||||
const ClickHandlerPtr &p, bool active) override;
|
||||
void clickHandlerPressedChanged(
|
||||
const ClickHandlerPtr &p, bool pressed) override;
|
||||
|
||||
bool isDisplayed() const override;
|
||||
PhotoData *getPhoto() const override {
|
||||
|
@ -123,20 +129,25 @@ private:
|
|||
ClickHandlerPtr _openl;
|
||||
std::unique_ptr<Media> _attach;
|
||||
mutable std::shared_ptr<Data::PhotoMedia> _photoMedia;
|
||||
mutable std::unique_ptr<Ui::RippleAnimation> _ripple;
|
||||
|
||||
bool _asArticle = false;
|
||||
bool _hasViewButton = false;
|
||||
int _dataVersion = -1;
|
||||
int _siteNameLines = 0;
|
||||
int _descriptionLines = 0;
|
||||
int _titleLines : 24 = 0;
|
||||
int _colorIndexPlusOne : 8 = 0;
|
||||
uint32 _titleLines : 24 = 0;
|
||||
uint32 _colorIndex : 7 = 0;
|
||||
uint32 _asArticle : 1 = 0;
|
||||
|
||||
Ui::Text::String _siteName, _title, _description;
|
||||
Ui::Text::String _siteName;
|
||||
Ui::Text::String _title;
|
||||
Ui::Text::String _description;
|
||||
|
||||
QString _openButton;
|
||||
QString _duration;
|
||||
int _openButtonWidth = 0;
|
||||
int _durationWidth = 0;
|
||||
|
||||
mutable QPoint _lastPoint;
|
||||
int _pixw = 0;
|
||||
int _pixh = 0;
|
||||
|
||||
|
|
|
@ -624,10 +624,14 @@ historyPollOutChosenSelected: icon {{ "poll_select_check", historyFileOutIconFgS
|
|||
historyPollInChosen: icon {{ "poll_select_check", historyFileInIconFg }};
|
||||
historyPollInChosenSelected: icon {{ "poll_select_check", historyFileInIconFgSelected }};
|
||||
|
||||
historyViewButtonHeight: 42px;
|
||||
historyViewButtonMargins: margins(13px, 5px, 13px, 5px);
|
||||
historyViewButtonHeight: 48px;
|
||||
historyViewButtonMargins: margins(10px, 5px, 10px, 10px);
|
||||
historyViewButtonTextStyle: semiboldTextStyle;
|
||||
|
||||
historyPageButtonLine: 1px;
|
||||
historyPageButtonHeight: 36px;
|
||||
historyPageButtonPadding: margins(13px, 8px, 13px, 8px);
|
||||
|
||||
historyCommentsButtonHeight: 40px;
|
||||
historyCommentsSkipLeft: 9px;
|
||||
historyCommentsSkipText: 10px;
|
||||
|
|
Loading…
Add table
Reference in a new issue