mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
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:
commit
3cc09578c9
22 changed files with 494 additions and 373 deletions
2
.github/workflows/snap.yml
vendored
2
.github/workflows/snap.yml
vendored
|
@ -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'
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
// LAYER 164
|
// LAYER 165
|
||||||
|
|
|
@ -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));
|
||||||
}();
|
}();
|
||||||
|
|
317
Telegram/SourceFiles/ui/effects/premium_top_bar.cpp
Normal file
317
Telegram/SourceFiles/ui/effects/premium_top_bar.cpp
Normal 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
|
93
Telegram/SourceFiles/ui/effects/premium_top_bar.h
Normal file
93
Telegram/SourceFiles/ui/effects/premium_top_bar.h
Normal 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
|
|
@ -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 \
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
2
cmake
|
@ -1 +1 @@
|
||||||
Subproject commit 218a34d9b5e3267b86a938b48bd74c045455bd3c
|
Subproject commit b1b0e95b091f298c87cb9ec4458f426574221ca4
|
Loading…
Add table
Reference in a new issue