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