Merge tag 'v4.10.2' into dev

# Conflicts:
#	Telegram/Resources/winrc/Telegram.rc
#	Telegram/Resources/winrc/Updater.rc
#	Telegram/SourceFiles/core/version.h
#	Telegram/lib_ui
This commit is contained in:
ZavaruKitsu 2023-09-30 15:18:12 +03:00
commit 3cc09578c9
22 changed files with 494 additions and 373 deletions

View file

@ -61,7 +61,7 @@ jobs:
sudo snap run lxd waitready sudo snap run lxd waitready
- name: Free up some disk space. - name: Free up some disk space.
uses: jlumbroso/free-disk-space@76866dbe54312617f00798d1762df7f43def6e5c uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8
- name: Telegram Desktop snap build. - name: Telegram Desktop snap build.
run: sg lxd -c 'snap run snapcraft -v' run: sg lxd -c 'snap run snapcraft -v'

View file

@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop" <Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE" ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="4.10.1.0" /> Version="4.10.2.0" />
<Properties> <Properties>
<DisplayName>Telegram Desktop</DisplayName> <DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName> <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View file

@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,10,1,0 FILEVERSION 4,10,2,0
PRODUCTVERSION 4,10,1,0 PRODUCTVERSION 4,10,2,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -62,10 +62,10 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Radolyn Labs" VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop" VALUE "FileDescription", "AyuGram Desktop"
VALUE "FileVersion", "4.10.1.0" VALUE "FileVersion", "4.10.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "AyuGram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.10.1.0" VALUE "ProductVersion", "4.10.2.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,10,1,0 FILEVERSION 4,10,2,0
PRODUCTVERSION 4,10,1,0 PRODUCTVERSION 4,10,2,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -53,10 +53,10 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Radolyn Labs" VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop Updater" VALUE "FileDescription", "AyuGram Desktop Updater"
VALUE "FileVersion", "4.10.1.0" VALUE "FileVersion", "4.10.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "AyuGram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.10.1.0" VALUE "ProductVersion", "4.10.2.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -2450,7 +2450,13 @@ void ApiWrap::refreshFileReference(
}; };
v::match(origin.data, [&](Data::FileOriginMessage data) { v::match(origin.data, [&](Data::FileOriginMessage data) {
if (const auto item = _session->data().message(data)) { if (const auto item = _session->data().message(data)) {
if (item->isScheduled()) { const auto media = item->media();
const auto storyId = media ? media->storyId() : FullStoryId();
if (storyId) {
request(MTPstories_GetStoriesByID(
_session->data().peer(storyId.peer)->input,
MTP_vector<MTPint>(1, MTP_int(storyId.story))));
} else if (item->isScheduled()) {
const auto &scheduled = _session->data().scheduledMessages(); const auto &scheduled = _session->data().scheduledMessages();
const auto realId = scheduled.lookupId(item); const auto realId = scheduled.lookupId(item);
request(MTPmessages_GetScheduledMessages( request(MTPmessages_GetScheduledMessages(

View file

@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
constexpr auto AppNameOld = "AyuGram for Windows"_cs; constexpr auto AppNameOld = "AyuGram for Windows"_cs;
constexpr auto AppName = "AyuGram Desktop"_cs; constexpr auto AppName = "AyuGram Desktop"_cs;
constexpr auto AppFile = "AyuGram"_cs; constexpr auto AppFile = "AyuGram"_cs;
constexpr auto AppVersion = 4010001; constexpr auto AppVersion = 4010002;
constexpr auto AppVersionStr = "4.10.1"; constexpr auto AppVersionStr = "4.10.2";
constexpr auto AppBetaVersion = false; constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View file

@ -1230,7 +1230,7 @@ void Stories::toggleHidden(
const auto name = peer->shortName(); const auto name = peer->shortName();
const auto guard = gsl::finally([&] { const auto guard = gsl::finally([&] {
if (show) { if (show && !justRemove) {
const auto phrase = hidden const auto phrase = hidden
? tr::lng_stories_hidden_to_contacts ? tr::lng_stories_hidden_to_contacts
: tr::lng_stories_shown_in_chats; : tr::lng_stories_shown_in_chats;

View file

@ -43,6 +43,19 @@ using UpdateFlag = StoryUpdate::Flag;
}; };
} }
[[nodiscard]] TextWithEntities StripLinks(TextWithEntities text) {
const auto link = [&](const EntityInText &entity) {
return (entity.type() == EntityType::CustomUrl)
|| (entity.type() == EntityType::Url)
|| (entity.type() == EntityType::Mention)
|| (entity.type() == EntityType::Hashtag);
};
text.entities.erase(
ranges::remove_if(text.entities, link),
text.entities.end());
return text;
}
[[nodiscard]] auto ParseLocation(const MTPMediaArea &area) [[nodiscard]] auto ParseLocation(const MTPMediaArea &area)
-> std::optional<StoryLocation> { -> std::optional<StoryLocation> {
auto result = std::optional<StoryLocation>(); auto result = std::optional<StoryLocation>();
@ -586,6 +599,11 @@ void Story::applyFields(
&owner().session(), &owner().session(),
data.ventities().value_or_empty()), data.ventities().value_or_empty()),
}; };
if (const auto user = _peer->asUser()) {
if (!user->isVerified() && !user->isPremium()) {
caption = StripLinks(std::move(caption));
}
}
auto counts = ViewsCounts(); auto counts = ViewsCounts();
auto viewsKnown = _views.known; auto viewsKnown = _views.known;
if (const auto info = data.vviews()) { if (const auto info = data.vviews()) {

View file

@ -444,10 +444,12 @@ void Row::paintUserpic(
const auto cornerBadgeShown = !_cornerBadgeUserpic const auto cornerBadgeShown = !_cornerBadgeUserpic
? _cornerBadgeShown ? _cornerBadgeShown
: !_cornerBadgeUserpic->layersManager.isDisplayedNone(); : !_cornerBadgeUserpic->layersManager.isDisplayedNone();
const auto storiesUser = peer ? peer->asUser() : nullptr; const auto storiesPeer = peer
? ((peer->isUser() || peer->isBroadcast()) ? peer : nullptr)
: nullptr;
const auto storiesFolder = peer ? nullptr : _id.folder(); const auto storiesFolder = peer ? nullptr : _id.folder();
const auto storiesHas = storiesUser const auto storiesHas = storiesPeer
? storiesUser->hasActiveStories() ? storiesPeer->hasActiveStories()
: storiesFolder : storiesFolder
? storiesFolder->storiesCount() ? storiesFolder->storiesCount()
: false; : false;
@ -467,8 +469,8 @@ void Row::paintUserpic(
const auto frameSide = (2 * framePadding + context.st->photoSize) const auto frameSide = (2 * framePadding + context.st->photoSize)
* ratio; * ratio;
const auto frameSize = QSize(frameSide, frameSide); const auto frameSize = QSize(frameSide, frameSide);
const auto storiesSource = (storiesHas && storiesUser) const auto storiesSource = (storiesHas && storiesPeer)
? storiesUser->owner().stories().source(storiesUser->id) ? storiesPeer->owner().stories().source(storiesPeer->id)
: nullptr; : nullptr;
const auto storiesCountReal = storiesSource const auto storiesCountReal = storiesSource
? int(storiesSource->ids.size()) ? int(storiesSource->ids.size())
@ -481,7 +483,7 @@ void Row::paintUserpic(
? storiesSource->unreadCount() ? storiesSource->unreadCount()
: storiesFolder : storiesFolder
? storiesFolder->storiesUnreadCount() ? storiesFolder->storiesUnreadCount()
: (storiesUser && storiesUser->hasUnreadStories()) : (storiesPeer && storiesPeer->hasUnreadStories())
? 1 ? 1
: 0; : 0;
const auto limit = Ui::kOutlineSegmentsMax; const auto limit = Ui::kOutlineSegmentsMax;

View file

@ -2336,8 +2336,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
const auto item = _dragStateItem; const auto item = _dragStateItem;
const auto itemId = item ? item->fullId() : FullMsgId(); const auto itemId = item ? item->fullId() : FullMsgId();
if (isUponSelected > 0) { if (isUponSelected > 0) {
const auto selectedText = getSelectedText();
if (!hasCopyRestrictionForSelected() if (!hasCopyRestrictionForSelected()
&& !getSelectedText().empty()) { && !selectedText.empty()) {
_menu->addAction( _menu->addAction(
(isUponSelected > 1 (isUponSelected > 1
? tr::lng_context_copy_selected_items(tr::now) ? tr::lng_context_copy_selected_items(tr::now)
@ -2345,11 +2346,12 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
[=] { copySelectedText(); }, [=] { copySelectedText(); },
&st::menuIconCopy); &st::menuIconCopy);
} }
if (!Ui::SkipTranslate(getSelectedText().rich)) { if (item && !Ui::SkipTranslate(selectedText.rich)) {
const auto peer = item->history()->peer;
_menu->addAction(tr::lng_context_translate_selected({}), [=] { _menu->addAction(tr::lng_context_translate_selected({}), [=] {
_controller->show(Box( _controller->show(Box(
Ui::TranslateBox, Ui::TranslateBox,
item->history()->peer, peer,
MsgId(), MsgId(),
getSelectedText().rich, getSelectedText().rich,
hasCopyRestrictionForSelected())); hasCopyRestrictionForSelected()));
@ -2452,7 +2454,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
[=] { copySelectedText(); }, [=] { copySelectedText(); },
&st::menuIconCopy); &st::menuIconCopy);
} }
if (!Ui::SkipTranslate(selectedText.rich)) { if (item && !Ui::SkipTranslate(selectedText.rich)) {
const auto peer = item->history()->peer; const auto peer = item->history()->peer;
_menu->addAction(tr::lng_context_translate_selected({}), [=] { _menu->addAction(tr::lng_context_translate_selected({}), [=] {
_controller->show(Box( _controller->show(Box(

View file

@ -1564,7 +1564,7 @@ peerStories#9a35e999 flags:# peer:Peer max_read_id:flags.0?int stories:Vector<St
stories.peerStories#cae68768 stories:PeerStories chats:Vector<Chat> users:Vector<User> = stories.PeerStories; stories.peerStories#cae68768 stories:PeerStories chats:Vector<Chat> users:Vector<User> = stories.PeerStories;
stories.boostsStatus#66ea1fef flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue = stories.BoostsStatus; stories.boostsStatus#e5c1aa5c flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue boost_url:string = stories.BoostsStatus;
stories.canApplyBoostOk#c3173587 = stories.CanApplyBoostResult; stories.canApplyBoostOk#c3173587 = stories.CanApplyBoostResult;
stories.canApplyBoostReplace#712c4655 current_boost:Peer chats:Vector<Chat> = stories.CanApplyBoostResult; stories.canApplyBoostReplace#712c4655 current_boost:Peer chats:Vector<Chat> = stories.CanApplyBoostResult;
@ -1573,6 +1573,8 @@ booster#e9e6380 user_id:long expires:int = Booster;
stories.boostersList#f3dd3d1d flags:# count:int boosters:Vector<Booster> next_offset:flags.0?string users:Vector<User> = stories.BoostersList; stories.boostersList#f3dd3d1d flags:# count:int boosters:Vector<Booster> next_offset:flags.0?string users:Vector<User> = stories.BoostersList;
messages.webPage#fd5e12bd webpage:WebPage chats:Vector<Chat> users:Vector<User> = messages.WebPage;
---functions--- ---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1801,7 +1803,7 @@ messages.setInlineGameScore#15ad9f64 flags:# edit_message:flags.0?true force:fla
messages.getGameHighScores#e822649d peer:InputPeer id:int user_id:InputUser = messages.HighScores; messages.getGameHighScores#e822649d peer:InputPeer id:int user_id:InputUser = messages.HighScores;
messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:InputUser = messages.HighScores; messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:InputUser = messages.HighScores;
messages.getCommonChats#e40ca104 user_id:InputUser max_id:long limit:int = messages.Chats; messages.getCommonChats#e40ca104 user_id:InputUser max_id:long limit:int = messages.Chats;
messages.getWebPage#32ca8f91 url:string hash:int = WebPage; messages.getWebPage#8d9692a3 url:string hash:int = messages.WebPage;
messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool; messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
messages.reorderPinnedDialogs#3b1adf37 flags:# force:flags.0?true folder_id:int order:Vector<InputDialogPeer> = Bool; messages.reorderPinnedDialogs#3b1adf37 flags:# force:flags.0?true folder_id:int order:Vector<InputDialogPeer> = Bool;
messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs; messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs;

View file

@ -1 +1 @@
// LAYER 164 // LAYER 165

View file

@ -33,10 +33,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_premium.h" #include "settings/settings_premium.h"
#include "ui/abstract_button.h" #include "ui/abstract_button.h"
#include "ui/basic_click_handlers.h" #include "ui/basic_click_handlers.h"
#include "ui/color_contrast.h"
#include "ui/effects/gradient.h" #include "ui/effects/gradient.h"
#include "ui/effects/premium_graphics.h" #include "ui/effects/premium_graphics.h"
#include "ui/effects/premium_stars_colored.h" #include "ui/effects/premium_stars_colored.h"
#include "ui/effects/premium_top_bar.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/text/format_values.h" #include "ui/text/format_values.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
@ -71,41 +71,6 @@ namespace {
using SectionCustomTopBarData = Info::Settings::SectionCustomTopBarData; using SectionCustomTopBarData = Info::Settings::SectionCustomTopBarData;
constexpr auto kBodyAnimationPart = 0.90;
constexpr auto kTitleAdditionalScale = 0.15;
constexpr auto kMinAcceptableContrast = 4.5; // 1.14;
[[nodiscard]] QString Svg() {
return u":/gui/icons/settings/star.svg"_q;
}
[[nodiscard]] QByteArray ColorizedSvg() {
auto f = QFile(Svg());
if (!f.open(QIODevice::ReadOnly)) {
return QByteArray();
}
auto content = qs(f.readAll());
auto stops = [] {
auto s = QString();
for (const auto &stop : Ui::Premium::ButtonGradientStops()) {
s += QString("<stop offset='%1' stop-color='%2'/>")
.arg(QString::number(stop.first), stop.second.name());
}
return s;
}();
const auto color = QString("<linearGradient id='Gradient2' "
"x1='%1' x2='%2' y1='%3' y2='%4'>%5</linearGradient>")
.arg(0)
.arg(1)
.arg(1)
.arg(0)
.arg(std::move(stops));
content.replace(u"gradientPlaceholder"_q, color);
content.replace(u"#fff"_q, u"url(#Gradient2)"_q);
f.close();
return content.toUtf8();
}
[[nodiscard]] Data::SubscriptionOptions SubscriptionOptionsForRows( [[nodiscard]] Data::SubscriptionOptions SubscriptionOptionsForRows(
Data::SubscriptionOptions result) { Data::SubscriptionOptions result) {
for (auto &option : result) { for (auto &option : result) {
@ -416,85 +381,6 @@ void SendScreenAccept(not_null<Window::SessionController*> controller) {
MTP_jsonNull()); MTP_jsonNull());
} }
class TopBarAbstract : public Ui::RpWidget {
public:
using Ui::RpWidget::RpWidget;
void setRoundEdges(bool value);
virtual void setPaused(bool paused) = 0;
virtual void setTextPosition(int x, int y) = 0;
[[nodiscard]] virtual rpl::producer<int> additionalHeight() const = 0;
protected:
void paintEdges(QPainter &p, const QBrush &brush) const;
void paintEdges(QPainter &p) const;
[[nodiscard]] QRectF starRect(
float64 topProgress,
float64 sizeProgress) const;
[[nodiscard]] bool isDark() const;
void computeIsDark();
private:
bool _roundEdges = true;
bool _isDark = false;
};
void TopBarAbstract::setRoundEdges(bool value) {
_roundEdges = value;
update();
}
void TopBarAbstract::paintEdges(QPainter &p, const QBrush &brush) const {
const auto r = rect();
if (_roundEdges) {
PainterHighQualityEnabler hq(p);
const auto radius = st::boxRadius;
p.setPen(Qt::NoPen);
p.setBrush(brush);
p.drawRoundedRect(
r + QMargins{ 0, 0, 0, radius + 1 },
radius,
radius);
} else {
p.fillRect(r, brush);
}
}
void TopBarAbstract::paintEdges(QPainter &p) const {
paintEdges(p, st::boxBg);
if (isDark()) {
paintEdges(p, st::shadowFg);
paintEdges(p, st::shadowFg);
}
}
QRectF TopBarAbstract::starRect(
float64 topProgress,
float64 sizeProgress) const {
const auto starSize = st::settingsPremiumStarSize * sizeProgress;
return QRectF(
QPointF(
(width() - starSize.width()) / 2,
st::settingsPremiumStarTopSkip * topProgress),
starSize);
};
bool TopBarAbstract::isDark() const {
return _isDark;
}
void TopBarAbstract::computeIsDark() {
const auto contrast = Ui::CountContrast(
st::boxBg->c,
st::premiumButtonFg->c);
_isDark = (contrast > kMinAcceptableContrast);
}
class EmojiStatusTopBar final { class EmojiStatusTopBar final {
public: public:
EmojiStatusTopBar( EmojiStatusTopBar(
@ -587,7 +473,7 @@ void EmojiStatusTopBar::paint(QPainter &p) {
} }
} }
class TopBarUser final : public TopBarAbstract { class TopBarUser final : public Ui::Premium::TopBarAbstract {
public: public:
TopBarUser( TopBarUser(
not_null<QWidget*> parent, not_null<QWidget*> parent,
@ -678,38 +564,8 @@ TopBarUser::TopBarUser(
HistoryView::Sticker::EmojiSize()); HistoryView::Sticker::EmojiSize());
_imageStar = QImage(); _imageStar = QImage();
} else { } else {
auto svg = QSvgRenderer(Svg());
const auto size = _starRect.size().toSize();
auto frame = QImage(
size * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
frame.setDevicePixelRatio(style::DevicePixelRatio());
auto mask = frame;
mask.fill(Qt::transparent);
{
auto p = QPainter(&mask);
auto gradient = QLinearGradient(
0,
size.height(),
size.width(),
0);
gradient.setStops(Ui::Premium::ButtonGradientStops());
p.setPen(Qt::NoPen);
p.setBrush(gradient);
p.drawRect(0, 0, size.width(), size.height());
}
frame.fill(Qt::transparent);
{
auto q = QPainter(&frame);
svg.render(&q, QRect(QPoint(), size));
q.setCompositionMode(QPainter::CompositionMode_SourceIn);
q.drawImage(0, 0, mask);
}
_imageStar = std::move(frame);
_emojiStatus = nullptr; _emojiStatus = nullptr;
_imageStar = Ui::Premium::GenerateStarForLightTopBar(_starRect);
} }
updateTitle(document, { name }, controller); updateTitle(document, { name }, controller);
@ -929,194 +785,6 @@ void TopBarUser::resizeEvent(QResizeEvent *e) {
} }
} }
class TopBar final : public TopBarAbstract {
public:
TopBar(
not_null<QWidget*> parent,
not_null<Window::SessionController*> controller,
rpl::producer<QString> title,
rpl::producer<TextWithEntities> about);
void setPaused(bool paused) override;
void setTextPosition(int x, int y) override;
rpl::producer<int> additionalHeight() const override;
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
const style::font &_titleFont;
const style::margins &_titlePadding;
object_ptr<Ui::FlatLabel> _about;
Ui::Premium::ColoredMiniStars _ministars;
QSvgRenderer _star;
struct {
float64 top = 0.;
float64 body = 0.;
float64 title = 0.;
float64 scaleTitle = 0.;
} _progress;
QRectF _starRect;
QPoint _titlePosition;
QPainterPath _titlePath;
};
TopBar::TopBar(
not_null<QWidget*> parent,
not_null<Window::SessionController*> controller,
rpl::producer<QString> title,
rpl::producer<TextWithEntities> about)
: TopBarAbstract(parent)
, _titleFont(st::boxTitle.style.font)
, _titlePadding(st::settingsPremiumTitlePadding)
, _about(this, std::move(about), st::settingsPremiumAbout)
, _ministars(this) {
std::move(
title
) | rpl::start_with_next([=](QString text) {
_titlePath = QPainterPath();
_titlePath.addText(0, _titleFont->ascent, _titleFont, text);
update();
}, lifetime());
_about->setClickHandlerFilter([=](
const ClickHandlerPtr &handler,
Qt::MouseButton button) {
ActivateClickHandler(_about, handler, {
button,
QVariant::fromValue(ClickHandlerContext{
.sessionWindow = base::make_weak(controller),
.botStartAutoSubmit = true,
})
});
return false;
});
rpl::single() | rpl::then(
style::PaletteChanged()
) | rpl::start_with_next([=] {
TopBarAbstract::computeIsDark();
if (!TopBarAbstract::isDark()) {
_star.load(Svg());
_ministars.setColorOverride(st::premiumButtonFg->c);
} else {
_star.load(ColorizedSvg());
_ministars.setColorOverride(std::nullopt);
}
auto event = QResizeEvent(size(), size());
resizeEvent(&event);
}, lifetime());
}
void TopBar::setPaused(bool paused) {
_ministars.setPaused(paused);
}
void TopBar::setTextPosition(int x, int y) {
_titlePosition = { x, y };
}
rpl::producer<int> TopBar::additionalHeight() const {
return _about->heightValue(
) | rpl::map([l = st::settingsPremiumAbout.style.lineHeight](int height) {
return std::max(height - l * 2, 0);
});
}
void TopBar::resizeEvent(QResizeEvent *e) {
const auto progress = (e->size().height() - minimumHeight())
/ float64(maximumHeight() - minimumHeight());
_progress.top = 1. -
std::clamp(
(1. - progress) / kBodyAnimationPart,
0.,
1.);
_progress.body = _progress.top;
_progress.title = 1. - progress;
_progress.scaleTitle = 1. + kTitleAdditionalScale * progress;
_ministars.setCenter(starRect(_progress.top, 1.).toRect());
_starRect = starRect(_progress.top, _progress.body);
const auto &padding = st::boxRowPadding;
const auto availableWidth = width() - padding.left() - padding.right();
const auto titleTop = _starRect.top()
+ _starRect.height()
+ _titlePadding.top();
const auto titlePathRect = _titlePath.boundingRect();
const auto aboutTop = titleTop
+ titlePathRect.height()
+ _titlePadding.bottom();
_about->resizeToWidth(availableWidth);
_about->moveToLeft(padding.left(), aboutTop);
_about->setOpacity(_progress.body);
Ui::RpWidget::resizeEvent(e);
}
void TopBar::paintEvent(QPaintEvent *e) {
auto p = QPainter(this);
p.fillRect(e->rect(), Qt::transparent);
const auto r = rect();
if (!TopBarAbstract::isDark()) {
const auto gradientPointTop = r.height() / 3. * 2.;
auto gradient = QLinearGradient(
QPointF(0, gradientPointTop),
QPointF(r.width(), r.height() - gradientPointTop));
gradient.setStops(Ui::Premium::ButtonGradientStops());
TopBarAbstract::paintEdges(p, gradient);
} else {
TopBarAbstract::paintEdges(p);
}
p.setOpacity(_progress.body);
p.translate(_starRect.center());
p.scale(_progress.body, _progress.body);
p.translate(-_starRect.center());
if (_progress.top) {
_ministars.paint(p);
}
p.resetTransform();
_star.render(&p, _starRect);
p.setPen(st::premiumButtonFg);
const auto titlePathRect = _titlePath.boundingRect();
// Title.
PainterHighQualityEnabler hq(p);
p.setOpacity(1.);
p.setFont(_titleFont);
const auto fullStarRect = starRect(1., 1.);
const auto fullTitleTop = fullStarRect.top()
+ fullStarRect.height()
+ _titlePadding.top();
p.translate(
anim::interpolate(
(width() - titlePathRect.width()) / 2,
_titlePosition.x(),
_progress.title),
anim::interpolate(fullTitleTop, _titlePosition.y(), _progress.title));
p.translate(titlePathRect.center());
p.scale(_progress.scaleTitle, _progress.scaleTitle);
p.translate(-titlePathRect.center());
p.fillPath(_titlePath, st::premiumButtonFg);
}
class Premium : public Section<Premium> { class Premium : public Section<Premium> {
public: public:
Premium( Premium(
@ -1499,7 +1167,7 @@ QPointer<Ui::RpWidget> Premium::createPinnedToTop(
return nullptr; return nullptr;
}(); }();
const auto content = [&]() -> TopBarAbstract* { const auto content = [&]() -> Ui::Premium::TopBarAbstract* {
if (peerWithPremium) { if (peerWithPremium) {
return Ui::CreateChild<TopBarUser>( return Ui::CreateChild<TopBarUser>(
parent.get(), parent.get(),
@ -1507,9 +1175,16 @@ QPointer<Ui::RpWidget> Premium::createPinnedToTop(
peerWithPremium, peerWithPremium,
_showFinished.events()); _showFinished.events());
} }
return Ui::CreateChild<TopBar>( const auto weak = base::make_weak(_controller);
const auto clickContextOther = [=] {
return QVariant::fromValue(ClickHandlerContext{
.sessionWindow = weak,
.botStartAutoSubmit = true,
});
};
return Ui::CreateChild<Ui::Premium::TopBar>(
parent.get(), parent.get(),
_controller, clickContextOther,
std::move(title), std::move(title),
std::move(about)); std::move(about));
}(); }();

View file

@ -0,0 +1,317 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/effects/premium_top_bar.h"
#include "ui/color_contrast.h"
#include "ui/painter.h"
#include "ui/effects/premium_graphics.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/fade_wrap.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
#include <QtCore/QFile>
namespace Ui::Premium {
namespace {
constexpr auto kBodyAnimationPart = 0.90;
constexpr auto kTitleAdditionalScale = 0.15;
constexpr auto kMinAcceptableContrast = 4.5; // 1.14;
} // namespace
QString Svg() {
return u":/gui/icons/settings/star.svg"_q;
}
QByteArray ColorizedSvg() {
auto f = QFile(Svg());
if (!f.open(QIODevice::ReadOnly)) {
return QByteArray();
}
auto content = QString::fromUtf8(f.readAll());
auto stops = [] {
auto s = QString();
for (const auto &stop : Ui::Premium::ButtonGradientStops()) {
s += QString("<stop offset='%1' stop-color='%2'/>")
.arg(QString::number(stop.first), stop.second.name());
}
return s;
}();
const auto color = QString("<linearGradient id='Gradient2' "
"x1='%1' x2='%2' y1='%3' y2='%4'>%5</linearGradient>")
.arg(0)
.arg(1)
.arg(1)
.arg(0)
.arg(std::move(stops));
content.replace(u"gradientPlaceholder"_q, color);
content.replace(u"#fff"_q, u"url(#Gradient2)"_q);
f.close();
return content.toUtf8();
}
QImage GenerateStarForLightTopBar(QRectF rect) {
auto svg = QSvgRenderer(Ui::Premium::Svg());
const auto size = rect.size().toSize();
auto frame = QImage(
size * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
frame.setDevicePixelRatio(style::DevicePixelRatio());
auto mask = frame;
mask.fill(Qt::transparent);
{
auto p = QPainter(&mask);
auto gradient = QLinearGradient(
0,
size.height(),
size.width(),
0);
gradient.setStops(Ui::Premium::ButtonGradientStops());
p.setPen(Qt::NoPen);
p.setBrush(gradient);
p.drawRect(0, 0, size.width(), size.height());
}
frame.fill(Qt::transparent);
{
auto q = QPainter(&frame);
svg.render(&q, QRect(QPoint(), size));
q.setCompositionMode(QPainter::CompositionMode_SourceIn);
q.drawImage(0, 0, mask);
}
return frame;
}
void TopBarAbstract::setRoundEdges(bool value) {
_roundEdges = value;
update();
}
void TopBarAbstract::paintEdges(QPainter &p, const QBrush &brush) const {
const auto r = rect();
if (_roundEdges) {
PainterHighQualityEnabler hq(p);
const auto radius = st::boxRadius;
p.setPen(Qt::NoPen);
p.setBrush(brush);
p.drawRoundedRect(
r + QMargins{ 0, 0, 0, radius + 1 },
radius,
radius);
} else {
p.fillRect(r, brush);
}
}
void TopBarAbstract::paintEdges(QPainter &p) const {
paintEdges(p, st::boxBg);
if (isDark()) {
paintEdges(p, st::shadowFg);
paintEdges(p, st::shadowFg);
}
}
QRectF TopBarAbstract::starRect(
float64 topProgress,
float64 sizeProgress) const {
const auto starSize = st::settingsPremiumStarSize * sizeProgress;
return QRectF(
QPointF(
(width() - starSize.width()) / 2,
st::settingsPremiumStarTopSkip * topProgress),
starSize);
};
bool TopBarAbstract::isDark() const {
return _isDark;
}
void TopBarAbstract::computeIsDark() {
const auto contrast = CountContrast(
st::boxBg->c,
st::premiumButtonFg->c);
_isDark = (contrast > kMinAcceptableContrast);
}
TopBar::TopBar(
not_null<QWidget*> parent,
Fn<QVariant()> clickContextOther,
rpl::producer<QString> title,
rpl::producer<TextWithEntities> about,
bool light)
: TopBarAbstract(parent)
, _light(light)
, _titleFont(st::boxTitle.style.font)
, _titlePadding(st::settingsPremiumTitlePadding)
, _about(
this,
std::move(about),
_light ? st::settingsPremiumUserAbout : st::settingsPremiumAbout)
, _ministars(this) {
std::move(
title
) | rpl::start_with_next([=](QString text) {
_titlePath = QPainterPath();
_titlePath.addText(0, _titleFont->ascent, _titleFont, text);
update();
}, lifetime());
if (clickContextOther) {
_about->setClickHandlerFilter([=](
const ClickHandlerPtr &handler,
Qt::MouseButton button) {
ActivateClickHandler(_about, handler, {
button,
clickContextOther()
});
return false;
});
}
rpl::single() | rpl::then(
style::PaletteChanged()
) | rpl::start_with_next([=] {
TopBarAbstract::computeIsDark();
if (!_light && !TopBarAbstract::isDark()) {
_star.load(Svg());
_ministars.setColorOverride(st::premiumButtonFg->c);
} else {
_star.load(ColorizedSvg());
_ministars.setColorOverride(std::nullopt);
}
auto event = QResizeEvent(size(), size());
resizeEvent(&event);
}, lifetime());
if (_light) {
const auto smallTopShadow = CreateChild<FadeShadow>(this);
smallTopShadow->setDuration(st::fadeWrapDuration);
sizeValue(
) | rpl::start_with_next([=](QSize size) {
smallTopShadow->resizeToWidth(size.width());
smallTopShadow->moveToLeft(
0,
height() - smallTopShadow->height());
const auto shown = (minimumHeight() * 2 > size.height());
smallTopShadow->toggle(shown, anim::type::normal);
}, lifetime());
}
}
TopBar::~TopBar() = default;
void TopBar::setPaused(bool paused) {
_ministars.setPaused(paused);
}
void TopBar::setTextPosition(int x, int y) {
_titlePosition = { x, y };
}
rpl::producer<int> TopBar::additionalHeight() const {
return _about->heightValue(
) | rpl::map([l = st::settingsPremiumAbout.style.lineHeight](int height) {
return std::max(height - l * 2, 0);
});
}
void TopBar::resizeEvent(QResizeEvent *e) {
const auto progress = (e->size().height() - minimumHeight())
/ float64(maximumHeight() - minimumHeight());
_progress.top = 1. -
std::clamp(
(1. - progress) / kBodyAnimationPart,
0.,
1.);
_progress.body = _progress.top;
_progress.title = 1. - progress;
_progress.scaleTitle = 1. + kTitleAdditionalScale * progress;
_ministars.setCenter(starRect(_progress.top, 1.).toRect());
_starRect = starRect(_progress.top, _progress.body);
const auto &padding = st::boxRowPadding;
const auto availableWidth = width() - padding.left() - padding.right();
const auto titleTop = _starRect.top()
+ _starRect.height()
+ _titlePadding.top();
const auto titlePathRect = _titlePath.boundingRect();
const auto aboutTop = titleTop
+ titlePathRect.height()
+ _titlePadding.bottom();
_about->resizeToWidth(availableWidth);
_about->moveToLeft(padding.left(), aboutTop);
_about->setOpacity(_progress.body);
RpWidget::resizeEvent(e);
}
void TopBar::paintEvent(QPaintEvent *e) {
auto p = QPainter(this);
p.fillRect(e->rect(), Qt::transparent);
const auto r = rect();
if (!_light && !TopBarAbstract::isDark()) {
const auto gradientPointTop = r.height() / 3. * 2.;
auto gradient = QLinearGradient(
QPointF(0, gradientPointTop),
QPointF(r.width(), r.height() - gradientPointTop));
gradient.setStops(ButtonGradientStops());
TopBarAbstract::paintEdges(p, gradient);
} else {
TopBarAbstract::paintEdges(p);
}
p.setOpacity(_progress.body);
p.translate(_starRect.center());
p.scale(_progress.body, _progress.body);
p.translate(-_starRect.center());
if (_progress.top) {
_ministars.paint(p);
}
p.resetTransform();
_star.render(&p, _starRect);
const auto color = _light
? st::settingsPremiumUserTitle.textFg
: st::premiumButtonFg;
p.setPen(color);
const auto titlePathRect = _titlePath.boundingRect();
// Title.
PainterHighQualityEnabler hq(p);
p.setOpacity(1.);
p.setFont(_titleFont);
const auto fullStarRect = starRect(1., 1.);
const auto fullTitleTop = fullStarRect.top()
+ fullStarRect.height()
+ _titlePadding.top();
p.translate(
anim::interpolate(
(width() - titlePathRect.width()) / 2,
_titlePosition.x(),
_progress.title),
anim::interpolate(fullTitleTop, _titlePosition.y(), _progress.title));
p.translate(titlePathRect.center());
p.scale(_progress.scaleTitle, _progress.scaleTitle);
p.translate(-titlePathRect.center());
p.fillPath(_titlePath, color);
}
} // namespace Ui::Premium

View file

@ -0,0 +1,93 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/object_ptr.h"
#include "ui/rp_widget.h"
#include "ui/effects/premium_stars_colored.h"
namespace Ui {
class FlatLabel;
} // namespace Ui
namespace Ui::Premium {
[[nodiscard]] QString Svg();
[[nodiscard]] QByteArray ColorizedSvg();
[[nodiscard]] QImage GenerateStarForLightTopBar(QRectF rect);
class TopBarAbstract : public RpWidget {
public:
using RpWidget::RpWidget;
void setRoundEdges(bool value);
virtual void setPaused(bool paused) = 0;
virtual void setTextPosition(int x, int y) = 0;
[[nodiscard]] virtual rpl::producer<int> additionalHeight() const = 0;
protected:
void paintEdges(QPainter &p, const QBrush &brush) const;
void paintEdges(QPainter &p) const;
[[nodiscard]] QRectF starRect(
float64 topProgress,
float64 sizeProgress) const;
[[nodiscard]] bool isDark() const;
void computeIsDark();
private:
bool _roundEdges = true;
bool _isDark = false;
};
class TopBar final : public TopBarAbstract {
public:
TopBar(
not_null<QWidget*> parent,
Fn<QVariant()> clickContextOther,
rpl::producer<QString> title,
rpl::producer<TextWithEntities> about,
bool light = false);
~TopBar();
void setPaused(bool paused) override;
void setTextPosition(int x, int y) override;
rpl::producer<int> additionalHeight() const override;
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
const bool _light = false;
const style::font &_titleFont;
const style::margins &_titlePadding;
object_ptr<FlatLabel> _about;
ColoredMiniStars _ministars;
QSvgRenderer _star;
struct {
float64 top = 0.;
float64 body = 0.;
float64 title = 0.;
float64 scaleTitle = 0.;
} _progress;
QRectF _starRect;
QPoint _titlePosition;
QPainterPath _titlePath;
};
} // namespace Ui::Premium

View file

@ -211,7 +211,7 @@ FROM builder AS libvpx
RUN git init libvpx \ RUN git init libvpx \
&& cd libvpx \ && cd libvpx \
&& git remote add origin {{ GIT }}/webmproject/libvpx.git \ && git remote add origin {{ GIT }}/webmproject/libvpx.git \
&& git fetch --depth=1 origin e1c124f8965f166d3e9ca26c9215ebc3ec3a1d72 \ && git fetch --depth=1 origin 51057f4ba894e13f9bba278905bacf6aaaecd992 \
&& git reset --hard FETCH_HEAD \ && git reset --hard FETCH_HEAD \
&& CFLAGS="$CFLAGS -fno-lto" CXXFLAGS="$CXXFLAGS -fno-lto" ./configure \ && CFLAGS="$CFLAGS -fno-lto" CXXFLAGS="$CXXFLAGS -fno-lto" ./configure \
--disable-examples \ --disable-examples \

View file

@ -837,7 +837,7 @@ stage('libvpx', """
git clone https://github.com/webmproject/libvpx.git git clone https://github.com/webmproject/libvpx.git
depends:patches/libvpx/*.patch depends:patches/libvpx/*.patch
cd libvpx cd libvpx
git checkout e1c124f89 git checkout 51057f4ba8
win: win:
for /r %%i in (..\\patches\\libvpx\\*) do git apply %%i for /r %%i in (..\\patches\\libvpx\\*) do git apply %%i

View file

@ -1,7 +1,7 @@
AppVersion 4010001 AppVersion 4010002
AppVersionStrMajor 4.10 AppVersionStrMajor 4.10
AppVersionStrSmall 4.10.1 AppVersionStrSmall 4.10.2
AppVersionStr 4.10.1 AppVersionStr 4.10.2
BetaChannel 0 BetaChannel 0
AlphaVersion 0 AlphaVersion 0
AppVersionOriginal 4.10.1 AppVersionOriginal 4.10.2

View file

@ -282,6 +282,8 @@ PRIVATE
ui/effects/premium_stars.h ui/effects/premium_stars.h
ui/effects/premium_stars_colored.cpp ui/effects/premium_stars_colored.cpp
ui/effects/premium_stars_colored.h ui/effects/premium_stars_colored.h
ui/effects/premium_top_bar.cpp
ui/effects/premium_top_bar.h
ui/effects/round_checkbox.cpp ui/effects/round_checkbox.cpp
ui/effects/round_checkbox.h ui/effects/round_checkbox.h
ui/effects/scroll_content_shadow.cpp ui/effects/scroll_content_shadow.cpp

@ -1 +1 @@
Subproject commit d67a11776ad720a718bae026b78ddd150f13fac5 Subproject commit cb8e07fa7febcff1dbdbac7bdb0ac46e4eda5c8e

View file

@ -1,3 +1,7 @@
4.10.2 (28.09.23)
- Bug fixes and other minor improvements.
4.10.1 (23.09.23) 4.10.1 (23.09.23)
- Rebuild macOS version with Xcode 14.0.1. - Rebuild macOS version with Xcode 14.0.1.

2
cmake

@ -1 +1 @@
Subproject commit 218a34d9b5e3267b86a938b48bd74c045455bd3c Subproject commit b1b0e95b091f298c87cb9ec4458f426574221ca4