Compare commits

...

17 Commits

Author SHA1 Message Date
John Preston
fd4a543bab Beta version 3.1.5: Fix theme change UI on Retina screens. 2021-09-28 22:27:41 +04:00
John Preston
d525e56053 Beta version 3.1.5: Fix build on Linux. 2021-09-28 22:08:28 +04:00
John Preston
dab5d1f994 Beta version 3.1.5.
- Choose one of 8 new preset themes for any individual private chat.
- Click on '...' menu > 'Change Colors' to pick a theme.
- Both chat participants will see the same theme
in that chat – on all their devices.
- Each new theme features colorful gradient message bubbles,
beautifully animated backgrounds and unique background patterns.
- All chat themes have day and night versions
and will follow your overall dark mode settings.
- Implement main window rounded corners on Windows 11.
- Fix audio capture from AirPods on macOS.
2021-09-28 22:00:51 +04:00
23rd
de3b52425c Removed unused HistoryInner::setFirstLoading. 2021-09-28 21:14:33 +04:00
John Preston
844fd58a97 Support Windows 11 rounded corners and themeable title bar. 2021-09-28 21:11:35 +04:00
John Preston
de2bad51d3 Scroll to currently selected theme. 2021-09-28 19:27:41 +04:00
John Preston
1424ea3540 Allow scrolling themes list. 2021-09-28 19:27:41 +04:00
John Preston
a8efd0ef3d Show chosen element in custom theme selector. 2021-09-28 19:27:41 +04:00
John Preston
1204e282d3 Fix attach icon in theme preview. 2021-09-28 19:27:41 +04:00
John Preston
6588242793 Prepare correct custom chat theme preview. 2021-09-28 19:27:41 +04:00
John Preston
b1ba9a42c6 Use Ui::GenerateBackgroundImage for preview in Settings. 2021-09-28 19:27:41 +04:00
John Preston
ab0d2bf9c6 Initial chat theme changing. 2021-09-28 19:27:41 +04:00
John Preston
80028e41f3 Bump OpenAL version in prepare script. 2021-09-28 19:25:39 +04:00
John Preston
2c581adc55 Add some hardening compiler / linker flags to dependencies. 2021-09-28 18:44:52 +04:00
John Preston
f0e8c1e325 Update lib_webview and docker patches revision. 2021-09-28 12:23:54 +04:00
John Preston
a2db9de4d7 Remove debug code. 2021-09-28 11:30:18 +04:00
John Preston
a228c62286 Fix "Nobody Viewed / Watched / Listened" seen state. 2021-09-27 18:51:50 +04:00
42 changed files with 1168 additions and 266 deletions

View File

@@ -1040,6 +1040,8 @@ PRIVATE
ui/chat/attach/attach_item_single_file_preview.h
ui/chat/attach/attach_item_single_media_preview.cpp
ui/chat/attach/attach_item_single_media_preview.h
ui/chat/choose_theme_controller.cpp
ui/chat/choose_theme_controller.h
ui/effects/fireworks_animation.cpp
ui/effects/fireworks_animation.h
ui/effects/round_checkbox.cpp
@@ -1368,6 +1370,7 @@ if (WIN32)
/DELAYLOAD:gdiplus.dll
/DELAYLOAD:version.dll
/DELAYLOAD:dwmapi.dll
/DELAYLOAD:uxtheme.dll
/DELAYLOAD:crypt32.dll
/DELAYLOAD:bcrypt.dll
/DELAYLOAD:imm32.dll

View File

@@ -2871,6 +2871,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_filters_remove_sure" = "This will remove the folder, your chats will not be deleted.";
"lng_filters_remove_yes" = "Remove";
"lng_chat_theme_change" = "Change colors";
"lng_chat_theme_none" = "No\nTheme";
"lng_chat_theme_apply" = "Apply Theme";
"lng_chat_theme_reset" = "Reset Theme";
"lng_chat_theme_dont" = "Do Not Set Theme";
"lng_chat_theme_title" = "Select theme";
"lng_chat_theme_cant_voice" = "Sorry, you can't change the chat theme while you're having an unsent voice message.";
"lng_photo_editor_menu_delete" = "Delete";
"lng_photo_editor_menu_flip" = "Flip";
"lng_photo_editor_menu_duplicate" = "Duplicate";

View File

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

View File

@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,1,4,0
PRODUCTVERSION 3,1,4,0
FILEVERSION 3,1,5,0
PRODUCTVERSION 3,1,5,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "3.1.4.0"
VALUE "FileVersion", "3.1.5.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "3.1.4.0"
VALUE "ProductVersion", "3.1.5.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,1,4,0
PRODUCTVERSION 3,1,4,0
FILEVERSION 3,1,5,0
PRODUCTVERSION 3,1,5,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "3.1.4.0"
VALUE "FileVersion", "3.1.5.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "3.1.4.0"
VALUE "ProductVersion", "3.1.5.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -355,6 +355,8 @@ rpl::producer<Ui::WhoReadContent> WhoRead(
} else if (UpdateUserpics(state, item, peers)) {
RegenerateParticipants(state, small, large);
pushNext();
} else if (peers.empty()) {
pushNext();
}
}, lifetime);

View File

@@ -68,6 +68,25 @@ std::map<int, const char*> BetaLogs() {
"- Reconnect without timeout when network availability changes.\n"
"- Crash fixes."
},
{
3001005,
"- Choose one of 8 new preset themes for any individual private chat.\n"
"- Click on '...' menu > 'Change Colors' to pick a theme.\n"
"- Both chat participants will see the same theme in that chat "
" on all their devices.\n"
"- Each new theme features colorful gradient message bubbles, "
"beautifully animated backgrounds and unique background patterns.\n"
"- All chat themes have day and night versions and will follow "
"your overall dark mode settings.\n"
"- Implement main window rounded corners on Windows 11.\n"
"- Fix audio capture from AirPods on macOS.\n"
}
};
};

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 3001004;
constexpr auto AppVersionStr = "3.1.4";
constexpr auto AppVersion = 3001005;
constexpr auto AppVersionStr = "3.1.5";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -387,26 +387,29 @@ rpl::producer<> CloudThemes::chatThemesUpdated() const {
}
std::optional<ChatTheme> CloudThemes::themeForEmoji(
const QString &emoji) const {
if (emoji.isEmpty()) {
const QString &emoticon) const {
const auto emoji = Ui::Emoji::Find(emoticon);
if (!emoji) {
return {};
}
const auto i = ranges::find(_chatThemes, emoji, &ChatTheme::emoji);
const auto i = ranges::find(_chatThemes, emoji, [](const ChatTheme &v) {
return Ui::Emoji::Find(v.emoticon);
});
return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt;
}
rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
const QString &emoji) {
const QString &emoticon) {
const auto testing = TestingColors();
if (emoji.isEmpty()) {
if (!Ui::Emoji::Find(emoticon)) {
return rpl::single<std::optional<ChatTheme>>(std::nullopt);
} else if (auto result = themeForEmoji(emoji)) {
} else if (auto result = themeForEmoji(emoticon)) {
if (testing) {
return rpl::single(
std::move(result)
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoji);
return themeForEmoji(emoticon);
}) | rpl::filter([](const std::optional<ChatTheme> &theme) {
return theme.has_value();
}));
@@ -419,7 +422,7 @@ rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
std::nullopt
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoji);
return themeForEmoji(emoticon);
}) | rpl::filter([](const std::optional<ChatTheme> &theme) {
return theme.has_value();
}) | rpl::take(limit));
@@ -482,12 +485,15 @@ QString CloudThemes::prepareTestingLink(const CloudTheme &theme) const {
}
std::optional<CloudTheme> CloudThemes::updateThemeFromLink(
const QString &emoji,
const QString &emoticon,
const QMap<QString, QString> &params) {
if (!TestingColors()) {
const auto emoji = Ui::Emoji::Find(emoticon);
if (!TestingColors() || !emoji) {
return std::nullopt;
}
const auto i = ranges::find(_chatThemes, emoji, &ChatTheme::emoji);
const auto i = ranges::find(_chatThemes, emoji, [](const ChatTheme &v) {
return Ui::Emoji::Find(v.emoticon);
});
if (i == end(_chatThemes)) {
return std::nullopt;
}
@@ -552,7 +558,7 @@ void CloudThemes::parseChatThemes(const QVector<MTPChatTheme> &list) {
for (const auto &theme : list) {
theme.match([&](const MTPDchatTheme &data) {
_chatThemes.push_back({
.emoji = qs(data.vemoticon()),
.emoticon = qs(data.vemoticon()),
.light = CloudTheme::Parse(_session, data.vtheme(), true),
.dark = CloudTheme::Parse(_session, data.vdark_theme(), true),
});

View File

@@ -50,7 +50,7 @@ struct CloudTheme {
};
struct ChatTheme {
QString emoji;
QString emoticon;
CloudTheme light;
CloudTheme dark;
};
@@ -71,15 +71,15 @@ public:
[[nodiscard]] const std::vector<ChatTheme> &chatThemes() const;
[[nodiscard]] rpl::producer<> chatThemesUpdated() const;
[[nodiscard]] std::optional<ChatTheme> themeForEmoji(
const QString &emoji) const;
const QString &emoticon) const;
[[nodiscard]] rpl::producer<std::optional<ChatTheme>> themeForEmojiValue(
const QString &emoji);
const QString &emoticon);
[[nodiscard]] static bool TestingColors();
static void SetTestingColors(bool testing);
[[nodiscard]] QString prepareTestingLink(const CloudTheme &theme) const;
[[nodiscard]] std::optional<CloudTheme> updateThemeFromLink(
const QString &emoji,
const QString &emoticon,
const QMap<QString, QString> &params);
void applyUpdate(const MTPTheme &theme);

View File

@@ -1004,19 +1004,24 @@ PeerId PeerData::groupCallDefaultJoinAs() const {
return 0;
}
void PeerData::setThemeEmoji(const QString &emoji) {
if (_themeEmoji == emoji) {
void PeerData::setThemeEmoji(const QString &emoticon) {
if (_themeEmoticon == emoticon) {
return;
}
_themeEmoji = emoji;
if (!emoji.isEmpty() && !owner().cloudThemes().themeForEmoji(emoji)) {
if (Ui::Emoji::Find(_themeEmoticon) == Ui::Emoji::Find(emoticon)) {
_themeEmoticon = emoticon;
return;
}
_themeEmoticon = emoticon;
if (!emoticon.isEmpty()
&& !owner().cloudThemes().themeForEmoji(emoticon)) {
owner().cloudThemes().refreshChatThemes();
}
session().changes().peerUpdated(this, UpdateFlag::ChatThemeEmoji);
}
const QString &PeerData::themeEmoji() const {
return _themeEmoji;
return _themeEmoticon;
}
void PeerData::setIsBlocked(bool is) {

View File

@@ -459,7 +459,7 @@ public:
[[nodiscard]] Data::GroupCall *groupCall() const;
[[nodiscard]] PeerId groupCallDefaultJoinAs() const;
void setThemeEmoji(const QString &emoji);
void setThemeEmoji(const QString &emoticon);
[[nodiscard]] const QString &themeEmoji() const;
const PeerId id;
@@ -506,7 +506,7 @@ private:
LoadedStatus _loadedStatus = LoadedStatus::Not;
QString _about;
QString _themeEmoji;
QString _themeEmoticon;
};

View File

@@ -614,8 +614,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
const auto historyDisplayedEmpty = _history->isDisplayedEmpty()
&& (!_migrated || _migrated->isDisplayedEmpty());
bool noHistoryDisplayed = _firstLoading || historyDisplayedEmpty;
if (!_firstLoading && _botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
bool noHistoryDisplayed = historyDisplayedEmpty;
if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
const auto st = context.st;
const auto stm = &st->messageStyle(false, false);
if (clip.y() < _botAbout->rect.y() + _botAbout->rect.height() && clip.y() + clip.height() > _botAbout->rect.y()) {
@@ -2328,11 +2328,6 @@ bool HistoryInner::wasSelectedText() const {
return _wasSelectedText;
}
void HistoryInner::setFirstLoading(bool loading) {
_firstLoading = loading;
update();
}
void HistoryInner::visibleAreaUpdated(int top, int bottom) {
auto scrolledUp = (top < _visibleAreaTop);
_visibleAreaTop = top;

View File

@@ -121,7 +121,6 @@ public:
void updateBotInfo(bool recount = true);
bool wasSelectedText() const;
void setFirstLoading(bool loading);
// updates history->scrollTopItem/scrollTopOffset
void visibleAreaUpdated(int top, int bottom);
@@ -378,8 +377,6 @@ private:
mutable int _curBlock = 0;
mutable int _curItem = 0;
bool _firstLoading = false;
style::cursor _cursor = style::cur_default;
SelectedItems _selected;
std::optional<Ui::ReportReason> _chooseForReportReason;

View File

@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/special_buttons.h"
#include "ui/emoji_config.h"
#include "ui/chat/attach/attach_prepare.h"
#include "ui/chat/choose_theme_controller.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/inner_dropdown.h"
#include "ui/widgets/dropdown_menu.h"
@@ -1282,11 +1283,39 @@ void HistoryWidget::insertHashtagOrBotCommand(
}
}
InlineBotQuery HistoryWidget::parseInlineBotQuery() const {
return (isChoosingTheme() || _editMsgId)
? InlineBotQuery()
: ParseInlineBotQuery(&session(), _field);
}
AutocompleteQuery HistoryWidget::parseMentionHashtagBotCommandQuery() const {
const auto result = (isChoosingTheme()
|| (_inlineBot && !_inlineLookingUpBot))
? AutocompleteQuery()
: ParseMentionHashtagBotCommandQuery(_field);
if (result.query.isEmpty()) {
return result;
} else if (result.query[0] == '#'
&& cRecentWriteHashtags().isEmpty()
&& cRecentSearchHashtags().isEmpty()) {
session().local().readRecentHashtagsAndBots();
} else if (result.query[0] == '@'
&& cRecentInlineBots().isEmpty()) {
session().local().readRecentHashtagsAndBots();
} else if (result.query[0] == '/'
&& ((_peer->isUser() && !_peer->asUser()->isBot()) || _editMsgId)) {
return AutocompleteQuery();
}
return result;
}
void HistoryWidget::updateInlineBotQuery() {
if (!_history) {
return;
}
const auto query = ParseInlineBotQuery(&session(), _field);
const auto query = parseInlineBotQuery();
if (_inlineBotUsername != query.username) {
_inlineBotUsername = query.username;
if (_inlineBotResolveRequestId) {
@@ -1369,10 +1398,11 @@ void HistoryWidget::orderWidgets() {
if (_groupCallBar) {
_groupCallBar->raise();
}
_topShadow->raise();
if (_fieldAutocomplete) {
_fieldAutocomplete->raise();
if (_chooseTheme) {
_chooseTheme->raise();
}
_topShadow->raise();
_fieldAutocomplete->raise();
if (_membersDropdown) {
_membersDropdown->raise();
}
@@ -1410,6 +1440,37 @@ bool HistoryWidget::updateStickersByEmoji() {
return (emoji != nullptr);
}
void HistoryWidget::toggleChooseChatTheme(not_null<PeerData*> peer) {
const auto update = [=] {
updateInlineBotQuery();
updateControlsGeometry();
updateControlsVisibility();
};
if (peer.get() != _peer) {
return;
} else if (_chooseTheme) {
if (isChoosingTheme()) {
const auto was = base::take(_chooseTheme);
if (Ui::InFocusChain(this)) {
setInnerFocus();
}
update();
}
return;
} else if (_voiceRecordBar->isActive()) {
Ui::ShowMultilineToast({
.text = { tr::lng_chat_theme_cant_voice(tr::now) },
});
return;
}
_chooseTheme = std::make_unique<Ui::ChooseThemeController>(
this,
controller(),
peer);
_chooseTheme->shouldBeShownValue(
) | rpl::start_with_next(update, _chooseTheme->lifetime());
}
void HistoryWidget::fieldChanged() {
const auto updateTyping = (_textUpdateEvents & TextUpdateEvent::SendTyping);
@@ -1556,7 +1617,9 @@ void HistoryWidget::setInnerFocus() {
if (_scroll->isHidden()) {
setFocus();
} else if (_list) {
if (_nonEmptySelection
if (_chooseTheme && _chooseTheme->shouldBeShown()) {
_chooseTheme->setFocus();
} else if (_nonEmptySelection
|| (_list && _list->wasSelectedText())
|| isRecording()
|| isBotStart()
@@ -1925,6 +1988,7 @@ void HistoryWidget::showHistory(
_pinnedTracker = nullptr;
_groupCallBar = nullptr;
_groupCallTracker = nullptr;
_chooseTheme = nullptr;
_membersDropdown.destroy();
_scrollToAnimation.stop();
@@ -2322,52 +2386,42 @@ void HistoryWidget::updateControlsVisibility() {
if (_contactStatus) {
_contactStatus->show();
}
if (!editingMessage() && (isBlocked() || isJoinChannel() || isMuteUnmute() || isBotStart() || isReportMessages())) {
if (isReportMessages()) {
_unblock->hide();
_joinChannel->hide();
_muteUnmute->hide();
_botStart->hide();
if (_reportMessages->isHidden()) {
_reportMessages->clearState();
_reportMessages->show();
}
if (isChoosingTheme()
|| (!editingMessage()
&& (isBlocked()
|| isJoinChannel()
|| isMuteUnmute()
|| isBotStart()
|| isReportMessages()))) {
const auto toggle = [&](Ui::FlatButton *shown) {
const auto toggleOne = [&](not_null<Ui::FlatButton*> button) {
if (button.get() != shown) {
button->hide();
} else if (button->isHidden()) {
button->clearState();
button->show();
}
};
toggleOne(_reportMessages);
toggleOne(_joinChannel);
toggleOne(_muteUnmute);
toggleOne(_botStart);
toggleOne(_unblock);
};
if (isChoosingTheme()) {
_chooseTheme->show();
setInnerFocus();
toggle(nullptr);
} else if (isReportMessages()) {
toggle(_reportMessages);
} else if (isBlocked()) {
_reportMessages->hide();
_joinChannel->hide();
_muteUnmute->hide();
_botStart->hide();
if (_unblock->isHidden()) {
_unblock->clearState();
_unblock->show();
}
toggle(_unblock);
} else if (isJoinChannel()) {
_reportMessages->hide();
_unblock->hide();
_muteUnmute->hide();
_botStart->hide();
if (_joinChannel->isHidden()) {
_joinChannel->clearState();
_joinChannel->show();
}
toggle(_joinChannel);
} else if (isMuteUnmute()) {
_reportMessages->hide();
_unblock->hide();
_joinChannel->hide();
_botStart->hide();
if (_muteUnmute->isHidden()) {
_muteUnmute->clearState();
_muteUnmute->show();
}
toggle(_muteUnmute);
} else if (isBotStart()) {
_reportMessages->hide();
_unblock->hide();
_joinChannel->hide();
_muteUnmute->hide();
if (_botStart->isHidden()) {
_botStart->clearState();
_botStart->show();
}
toggle(_botStart);
}
_kbShown = false;
_fieldAutocomplete->hide();
@@ -3289,6 +3343,9 @@ void HistoryWidget::hideChildWidgets() {
if (_voiceRecordBar) {
_voiceRecordBar->hideFast();
}
if (_chooseTheme) {
_chooseTheme->hide();
}
hideChildren();
}
@@ -3908,7 +3965,7 @@ void HistoryWidget::inlineBotResolveDone(
}();
session().data().processChats(data.vchats());
const auto query = ParseInlineBotQuery(&session(), _field);
const auto query = parseInlineBotQuery();
if (_inlineBotUsername == query.username) {
applyInlineBotQuery(
query.lookingUpBot ? resolvedBot : query.bot,
@@ -3953,6 +4010,10 @@ bool HistoryWidget::isJoinChannel() const {
return _peer && _peer->isChannel() && !_peer->asChannel()->amIn();
}
bool HistoryWidget::isChoosingTheme() const {
return _chooseTheme && _chooseTheme->shouldBeShown();
}
bool HistoryWidget::isMuteUnmute() const {
return _peer
&& ((_peer->isBroadcast() && !_peer->asChannel()->canPublish())
@@ -4337,24 +4398,7 @@ void HistoryWidget::checkFieldAutocomplete() {
return;
}
const auto isInlineBot = _inlineBot && !_inlineLookingUpBot;
const auto autocomplete = isInlineBot
? AutocompleteQuery()
: ParseMentionHashtagBotCommandQuery(_field);
if (!autocomplete.query.isEmpty()) {
if (autocomplete.query[0] == '#'
&& cRecentWriteHashtags().isEmpty()
&& cRecentSearchHashtags().isEmpty()) {
session().local().readRecentHashtagsAndBots();
} else if (autocomplete.query[0] == '@'
&& cRecentInlineBots().isEmpty()) {
session().local().readRecentHashtagsAndBots();
} else if (autocomplete.query[0] == '/'
&& ((_peer->isUser() && !_peer->asUser()->isBot())
|| _editMsgId)) {
return;
}
}
const auto autocomplete = parseMentionHashtagBotCommandQuery();
_fieldAutocomplete->showFiltered(
_peer,
autocomplete.query,
@@ -4922,7 +4966,14 @@ void HistoryWidget::updateHistoryGeometry(
if (_contactStatus) {
newScrollHeight -= _contactStatus->height();
}
if (!editingMessage() && (isBlocked() || isBotStart() || isJoinChannel() || isMuteUnmute() || isReportMessages())) {
if (isChoosingTheme()) {
newScrollHeight -= _chooseTheme->height();
} else if (!editingMessage()
&& (isBlocked()
|| isBotStart()
|| isJoinChannel()
|| isMuteUnmute()
|| isReportMessages())) {
newScrollHeight -= _unblock->height();
} else {
if (editingMessage() || _canSendMessages) {
@@ -6136,16 +6187,17 @@ void HistoryWidget::editMessage(FullMsgId itemId) {
}
void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
if (_voiceRecordBar->isActive()) {
controller()->show(
Box<InformBox>(tr::lng_edit_caption_voice(tr::now)));
return;
}
if (const auto media = item->media()) {
if (media->allowsEditCaption()) {
controller()->show(Box<EditCaptionBox>(controller(), item));
return;
}
} else if (_chooseTheme) {
toggleChooseChatTheme(_peer);
} else if (_voiceRecordBar->isActive()) {
controller()->show(
Box<InformBox>(tr::lng_edit_caption_voice(tr::now)));
return;
}
if (isRecording()) {

View File

@@ -25,6 +25,8 @@ struct FileLoadResult;
struct SendingAlbum;
enum class SendMediaType;
class MessageLinksParser;
struct InlineBotQuery;
struct AutocompleteQuery;
namespace MTP {
class Error;
@@ -77,6 +79,7 @@ enum class ReportReason;
namespace Toast {
class Instance;
} // namespace Toast
class ChooseThemeController;
} // namespace Ui
namespace Window {
@@ -237,6 +240,8 @@ public:
void clearDelayedShowAt();
void saveFieldToHistoryLocalDraft();
void toggleChooseChatTheme(not_null<PeerData*> peer);
void applyCloudDraft(History *history);
void updateHistoryDownPosition();
@@ -454,6 +459,10 @@ private:
std::optional<QString> writeRestriction() const;
void orderWidgets();
[[nodiscard]] InlineBotQuery parseInlineBotQuery() const;
[[nodiscard]] auto parseMentionHashtagBotCommandQuery() const
-> AutocompleteQuery;
void clearInlineBot();
void inlineBotChanged();
@@ -585,19 +594,21 @@ private:
void inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result);
void inlineBotResolveFail(const MTP::Error &error, const QString &username);
bool isRecording() const;
[[nodiscard]] bool isRecording() const;
bool isBotStart() const;
bool isBlocked() const;
bool isJoinChannel() const;
bool isMuteUnmute() const;
bool isReportMessages() const;
[[nodiscard]] bool isBotStart() const;
[[nodiscard]] bool isBlocked() const;
[[nodiscard]] bool isJoinChannel() const;
[[nodiscard]] bool isMuteUnmute() const;
[[nodiscard]] bool isReportMessages() const;
bool updateCmdStartShown();
void updateSendButtonType();
bool showRecordButton() const;
bool showInlineBotCancel() const;
[[nodiscard]] bool showRecordButton() const;
[[nodiscard]] bool showInlineBotCancel() const;
void refreshSilentToggle();
[[nodiscard]] bool isChoosingTheme() const;
void setupScheduledToggle();
void refreshScheduledToggle();
@@ -689,7 +700,7 @@ private:
bool _unreadMentionsIsShown = false;
object_ptr<Ui::HistoryDownButton> _unreadMentions;
object_ptr<FieldAutocomplete> _fieldAutocomplete;
const object_ptr<FieldAutocomplete> _fieldAutocomplete;
object_ptr<Support::Autocomplete> _supportAutocomplete;
std::unique_ptr<MessageLinksParser> _fieldLinksParser;
@@ -726,6 +737,8 @@ private:
object_ptr<Ui::ScrollArea> _kbScroll;
const not_null<BotKeyboard*> _keyboard;
std::unique_ptr<Ui::ChooseThemeController> _chooseTheme;
object_ptr<Ui::InnerDropdown> _membersDropdown = { nullptr };
base::Timer _membersDropdownShowTimer;

View File

@@ -1353,6 +1353,10 @@ void MainWidget::clearChooseReportMessages() {
_history->setChooseReportMessagesDetails({}, nullptr);
}
void MainWidget::toggleChooseChatTheme(not_null<PeerData*> peer) {
_history->toggleChooseChatTheme(peer);
}
void MainWidget::ui_showPeerHistory(
PeerId peerId,
const SectionShow &params,

View File

@@ -213,6 +213,8 @@ public:
Fn<void(MessageIdsList)> done);
void clearChooseReportMessages();
void toggleChooseChatTheme(not_null<PeerData*> peer);
void ui_showPeerHistory(
PeerId peer,
const SectionShow &params,

View File

@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "base/crc32hash.h"
#include "base/platform/win/base_windows_wrl.h"
#include "base/platform/base_platform_info.h"
#include "core/application.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
@@ -34,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <Shobjidl.h>
#include <shellapi.h>
#include <WtsApi32.h>
#include <dwmapi.h>
#include <windows.ui.viewmanagement.h>
#include <UIViewSettingsInterop.h>
@@ -401,11 +403,9 @@ void MainWindow::initHook() {
}
void MainWindow::validateWindowTheme(bool native, bool night) {
if (!Dlls::SetWindowTheme) {
return;
} else if (!IsWindows8OrGreater()) {
if (!IsWindows8OrGreater()) {
const auto empty = native ? nullptr : L" ";
Dlls::SetWindowTheme(ps_hWnd, empty, empty);
SetWindowTheme(ps_hWnd, empty, empty);
QApplication::setStyle(QStyleFactory::create(u"Windows"_q));
#if 0
} else if (!Platform::IsDarkModeSupported()/*
@@ -416,7 +416,7 @@ void MainWindow::validateWindowTheme(bool native, bool night) {
return;
#endif
} else if (!native) {
Dlls::SetWindowTheme(ps_hWnd, nullptr, nullptr);
SetWindowTheme(ps_hWnd, nullptr, nullptr);
return;
}
@@ -435,13 +435,13 @@ void MainWindow::validateWindowTheme(bool native, bool night) {
sizeof(darkValue)
};
Dlls::SetWindowCompositionAttribute(ps_hWnd, &data);
} else if (kSystemVersion.microVersion() >= 17763 && Dlls::DwmSetWindowAttribute) {
static const auto DWMWA_USE_IMMERSIVE_DARK_MODE = (kSystemVersion.microVersion() >= 18985)
} else if (kSystemVersion.microVersion() >= 17763) {
static const auto kDWMWA_USE_IMMERSIVE_DARK_MODE = (kSystemVersion.microVersion() >= 18985)
? DWORD(20)
: DWORD(19);
Dlls::DwmSetWindowAttribute(
DwmSetWindowAttribute(
ps_hWnd,
DWMWA_USE_IMMERSIVE_DARK_MODE,
kDWMWA_USE_IMMERSIVE_DARK_MODE,
&darkValue,
sizeof(darkValue));
}
@@ -457,7 +457,7 @@ void MainWindow::validateWindowTheme(bool native, bool night) {
//const auto updateWindowTheme = [&] {
// const auto set = [&](LPCWSTR name) {
// return Dlls::SetWindowTheme(ps_hWnd, name, nullptr);
// return SetWindowTheme(ps_hWnd, name, nullptr);
// };
// if (!night || FAILED(set(L"DarkMode_Explorer"))) {
// set(L"Explorer");

View File

@@ -36,13 +36,12 @@ SafeIniter::SafeIniter() {
LOAD_SYMBOL(LibShell32, SHChangeNotify);
LOAD_SYMBOL(LibShell32, SetCurrentProcessExplicitAppUserModelID);
const auto LibUxTheme = LoadLibrary(L"uxtheme.dll");
LOAD_SYMBOL(LibUxTheme, SetWindowTheme);
//if (IsWindows10OrGreater()) {
// static const auto kSystemVersion = QOperatingSystemVersion::current();
// static const auto kMinor = kSystemVersion.minorVersion();
// static const auto kBuild = kSystemVersion.microVersion();
// if (kMinor > 0 || (kMinor == 0 && kBuild >= 17763)) {
// const auto LibUxTheme = LoadLibrary(L"uxtheme.dll");
// if (kBuild < 18362) {
// LOAD_SYMBOL(LibUxTheme, AllowDarkModeForApp, 135);
// } else {
@@ -62,9 +61,6 @@ SafeIniter::SafeIniter() {
LOAD_SYMBOL(LibPropSys, PropVariantToString);
LOAD_SYMBOL(LibPropSys, PSStringFromPropertyKey);
const auto LibDwmApi = LoadLibrary(L"dwmapi.dll");
LOAD_SYMBOL(LibDwmApi, DwmSetWindowAttribute);
const auto LibPsApi = LoadLibrary(L"psapi.dll");
LOAD_SYMBOL(LibPsApi, GetProcessMemoryInfo);

View File

@@ -24,12 +24,6 @@ namespace Dlls {
void CheckLoadedModules();
// UXTHEME.DLL
inline HRESULT(__stdcall *SetWindowTheme)(
HWND hWnd,
LPCWSTR pszSubAppName,
LPCWSTR pszSubIdList);
//inline void(__stdcall *RefreshImmersiveColorPolicyState)();
//
//inline BOOL(__stdcall *AllowDarkModeForApp)(BOOL allow);
@@ -94,14 +88,6 @@ inline HRESULT(__stdcall *PSStringFromPropertyKey)(
_Out_writes_(cch) LPWSTR psz,
_In_ UINT cch);
// DWMAPI.DLL
inline HRESULT(__stdcall *DwmSetWindowAttribute)(
HWND hwnd,
DWORD dwAttribute,
_In_reads_bytes_(cbAttribute) LPCVOID pvAttribute,
DWORD cbAttribute);
// PSAPI.DLL
inline BOOL(__stdcall *GetProcessMemoryInfo)(

View File

@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/chat/attach/attach_extensions.h"
#include "ui/chat/chat_theme.h"
#include "ui/layers/generic_box.h"
#include "ui/effects/radial_animation.h"
#include "ui/style/style_palette_colorizer.h"
@@ -556,61 +557,61 @@ void BackgroundRow::updateImage() {
const auto size = st::settingsBackgroundThumb;
const auto fullsize = size * cIntRetinaFactor();
// We use Format_RGB32 so that DestinationIn shows black, not transparent.
// Then we'll convert to Format_ARGB32_Premultiplied for round corners.
auto back = QImage(fullsize, fullsize, QImage::Format_RGB32);
back.setDevicePixelRatio(cRetinaFactor());
{
Painter p(&back);
PainterHighQualityEnabler hq(p);
const auto background = Window::Theme::Background();
if (const auto color = background->colorForFill()) {
p.fillRect(0, 0, size, size, *color);
} else {
const auto gradient = background->gradientForFill();
const auto patternOpacity = background->paper().patternOpacity();
if (!gradient.isNull()) {
auto hq = PainterHighQualityEnabler(p);
p.drawImage(QRect(0, 0, size, size), gradient);
if (patternOpacity >= 0.) {
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
} else {
p.setCompositionMode(
QPainter::CompositionMode_DestinationIn);
}
const auto &background = *Window::Theme::Background();
const auto &paper = background.paper();
const auto &prepared = background.prepared();
const auto preparePattern = [&] {
const auto paintPattern = [&](QPainter &p, bool inverted) {
if (prepared.isNull()) {
return;
}
const auto &prepared = background->prepared();
if (!prepared.isNull()) {
const auto pattern = background->paper().isPattern();
const auto w = prepared.width();
const auto h = prepared.height();
const auto use = [&] {
if (!pattern) {
return std::min(w, h);
}
const auto scaledw = w * st::windowMinHeight / h;
const auto result = (w * size) / scaledw;
return std::min({ result, w, h });
}();
p.drawImage(
QRect(0, 0, size, size),
prepared,
QRect((w - use) / 2, (h - use) / 2, use, use));
}
if (!gradient.isNull()
&& !prepared.isNull()
&& patternOpacity < 0.
&& patternOpacity > -1.) {
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
p.setOpacity(1. + patternOpacity);
p.fillRect(QRect(0, 0, size, size), Qt::black);
const auto w = prepared.width();
const auto h = prepared.height();
const auto s = [&] {
const auto scaledw = w * st::windowMinHeight / h;
const auto result = (w * size) / scaledw;
return std::min({ result, w, h });
}();
auto small = prepared.copy((w - s) / 2, (h - s) / 2, s, s);
if (inverted) {
small = Ui::InvertPatternImage(std::move(small));
}
p.drawImage(QRect(0, 0, size, size), small);
};
return Ui::GenerateBackgroundImage(
{ fullsize, fullsize },
paper.backgroundColors(),
paper.gradientRotation(),
paper.patternOpacity(),
paintPattern);
};
const auto prepareNormal = [&] {
auto result = QImage(
QSize{ fullsize, fullsize },
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
if (const auto color = background.colorForFill()) {
result.fill(*color);
return result;
} else if (prepared.isNull()) {
result.fill(Qt::transparent);
return result;
}
}
back = std::move(back).convertToFormat(
QImage::Format_ARGB32_Premultiplied);
Painter p(&result);
PainterHighQualityEnabler hq(p);
const auto w = prepared.width();
const auto h = prepared.height();
const auto s = std::min(w, h);
p.drawImage(
QRect(0, 0, size, size),
prepared,
QRect((w - s) / 2, (h - s) / 2, s, s));
p.end();
return result;
};
auto back = (paper.isPattern() || !background.gradientForFill().isNull())
? preparePattern()
: prepareNormal();
Images::prepareRound(back, ImageRoundRadius::Small);
_background = Ui::PixmapFromImage(std::move(back));
_background.setDevicePixelRatio(cRetinaFactor());

View File

@@ -329,10 +329,6 @@ void UpdateImageDetails(PreparedFile &file, int previewWidth) {
: image->data;
Assert(!preview.isNull());
file.shownDimensions = PrepareShownDimensions(preview);
const auto scaledWidth = style::ConvertScale(preview.width());
constexpr auto kIntMin = std::numeric_limits<int>::min();
static_assert(kIntMin == -2147483648);
scaledWidth;
const auto toWidth = std::min(
previewWidth,
style::ConvertScale(preview.width())

View File

@@ -124,9 +124,8 @@ constexpr auto kMinAcceptableContrast = 1.14;// 4.5;
QImage::Format_ARGB32_Premultiplied),
.gradient = gradient,
.area = request.area,
.waitingForNegativePattern = (request.background.isPattern
&& request.background.prepared.isNull()
&& request.background.patternOpacity < 0.)
.waitingForNegativePattern
= request.background.waitingForNegativePattern()
};
} else {
const auto rects = ComputeChatBackgroundRects(
@@ -427,6 +426,8 @@ void ChatTheme::updateBackgroundImageFrom(ChatThemeBackground &&background) {
_cacheBackgroundTimer->cancel();
}
cacheBackgroundNow();
} else {
_repaintBackgroundRequests.fire({});
}
}
@@ -511,6 +512,11 @@ const BackgroundState &ChatTheme::backgroundState(QSize area) {
return _backgroundState;
}
void ChatTheme::clearBackgroundState() {
_backgroundState = BackgroundState();
_backgroundFade.stop();
}
bool ChatTheme::readyForBackgroundRotation() const {
Expects(_cacheBackgroundTimer.has_value());

View File

@@ -32,6 +32,10 @@ struct ChatThemeBackground {
int gradientRotation = 0;
bool isPattern = false;
bool tile = false;
[[nodiscard]] bool waitingForNegativePattern() const {
return isPattern && prepared.isNull() && (patternOpacity < 0.);
}
};
bool operator==(const ChatThemeBackground &a, const ChatThemeBackground &b);
@@ -137,6 +141,7 @@ public:
QRect viewport,
QRect clip);
[[nodiscard]] const BackgroundState &backgroundState(QSize area);
void clearBackgroundState();
[[nodiscard]] rpl::producer<> repaintBackgroundRequests() const;
void rotateComplexGradientBackground();
@@ -157,7 +162,6 @@ private:
void cacheBubblesNow();
void cacheBubblesAsync(
const CacheBackgroundRequest &request);
void setCachedBubbles(CacheBackgroundResult &&cached);
[[nodiscard]] CacheBackgroundRequest cacheBubblesRequest(
QSize area) const;

View File

@@ -0,0 +1,637 @@
/*
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/chat/choose_theme_controller.h"
#include "ui/rp_widget.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/chat/chat_theme.h"
#include "ui/chat/message_bubble.h"
#include "ui/wrap/vertical_layout.h"
#include "main/main_session.h"
#include "window/window_session_controller.h"
#include "window/themes/window_theme.h"
#include "data/data_session.h"
#include "data/data_peer.h"
#include "data/data_cloud_themes.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "styles/style_widgets.h"
#include "styles/style_layers.h" // boxTitle.
#include "styles/style_settings.h"
#include "styles/style_window.h"
#include <QtWidgets/QApplication>
namespace Ui {
namespace {
constexpr auto kDisableElement = "disable"_cs;
[[nodiscard]] QImage GeneratePreview(not_null<Ui::ChatTheme*> theme) {
const auto &background = theme->background();
const auto &colors = background.colors;
const auto size = st::settingsThemePreviewSize;
auto prepared = background.prepared;
const auto paintPattern = [&](QPainter &p, bool inverted) {
if (prepared.isNull()) {
return;
}
const auto w = prepared.width();
const auto h = prepared.height();
const auto scaled = size.scaled(
st::windowMinWidth / 2,
st::windowMinHeight / 2,
Qt::KeepAspectRatio);
const auto use = (scaled.width() > w || scaled.height() > h)
? scaled.scaled({ w, h }, Qt::KeepAspectRatio)
: scaled;
const auto good = QSize(
std::max(use.width(), 1),
std::max(use.height(), 1));
auto small = prepared.copy(QRect(
QPoint(
(w - good.width()) / 2,
(h - good.height()) / 2),
good));
if (inverted) {
small = Ui::InvertPatternImage(std::move(small));
}
p.drawImage(
QRect(QPoint(), size * style::DevicePixelRatio()),
small);
};
const auto fullsize = size * style::DevicePixelRatio();
auto result = background.waitingForNegativePattern()
? QImage(
fullsize,
QImage::Format_ARGB32_Premultiplied)
: Ui::GenerateBackgroundImage(
fullsize,
colors.empty() ? std::vector{ 1, QColor(0, 0, 0) } : colors,
background.gradientRotation,
background.patternOpacity,
paintPattern);
if (background.waitingForNegativePattern()) {
result.fill(Qt::black);
}
result.setDevicePixelRatio(style::DevicePixelRatio());
{
auto p = QPainter(&result);
const auto sent = QRect(
QPoint(
(size.width()
- st::settingsThemeBubbleSize.width()
- st::settingsThemeBubblePosition.x()),
st::settingsThemeBubblePosition.y()),
st::settingsThemeBubbleSize);
const auto received = QRect(
st::settingsThemeBubblePosition.x(),
sent.y() + sent.height() + st::settingsThemeBubbleSkip,
sent.width(),
sent.height());
const auto radius = st::settingsThemeBubbleRadius;
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
if (const auto pattern = theme->bubblesBackgroundPattern()) {
auto bubble = pattern->pixmap.toImage().scaled(
sent.size() * style::DevicePixelRatio(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation
).convertToFormat(QImage::Format_ARGB32_Premultiplied);
const auto corners = Images::CornersMask(radius);
Images::prepareRound(bubble, corners);
p.drawImage(sent, bubble);
} else {
p.setBrush(theme->palette()->msgOutBg()->c);
p.drawRoundedRect(sent, radius, radius);
}
p.setBrush(theme->palette()->msgInBg()->c);
p.drawRoundedRect(received, radius, radius);
}
Images::prepareRound(result, ImageRoundRadius::Large);
return result;
}
[[nodiscard]] QImage GenerateEmptyPreview() {
auto result = QImage(
st::settingsThemePreviewSize * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
result.fill(st::settingsThemeNotSupportedBg->c);
result.setDevicePixelRatio(style::DevicePixelRatio());
{
auto p = QPainter(&result);
p.setPen(st::menuIconFg);
p.setFont(st::semiboldFont);
const auto top = st::normalFont->height / 2;
const auto width = st::settingsThemePreviewSize.width();
const auto height = st::settingsThemePreviewSize.height() - top;
p.drawText(
QRect(0, top, width, height),
tr::lng_chat_theme_none(tr::now),
style::al_top);
}
Images::prepareRound(result, ImageRoundRadius::Large);
return result;
}
} // namespace
struct ChooseThemeController::Entry {
uint64 id = 0;
std::shared_ptr<Ui::ChatTheme> theme;
std::shared_ptr<Data::DocumentMedia> media;
QImage preview;
EmojiPtr emoji = nullptr;
QRect geometry;
bool chosen = false;
};
ChooseThemeController::ChooseThemeController(
not_null<RpWidget*> parent,
not_null<Window::SessionController*> window,
not_null<PeerData*> peer)
: _controller(window)
, _peer(peer)
, _wrap(std::make_unique<VerticalLayout>(parent))
, _topShadow(std::make_unique<PlainShadow>(parent))
, _content(_wrap->add(object_ptr<RpWidget>(_wrap.get())))
, _inner(CreateChild<RpWidget>(_content.get()))
, _dark(Window::Theme::IsThemeDarkValue()) {
init(parent->sizeValue());
}
ChooseThemeController::~ChooseThemeController() {
_controller->clearPeerThemeOverride(_peer);
}
void ChooseThemeController::init(rpl::producer<QSize> outer) {
using namespace rpl::mappers;
const auto themes = &_controller->session().data().cloudThemes();
const auto &list = themes->chatThemes();
if (!list.empty()) {
fill(list);
} else {
themes->refreshChatThemes();
themes->chatThemesUpdated(
) | rpl::take(1) | rpl::start_with_next([=] {
fill(themes->chatThemes());
}, lifetime());
}
const auto skip = st::normalFont->spacew * 4;
const auto titleWrap = _wrap->insert(
0,
object_ptr<FixedHeightWidget>(
_wrap.get(),
skip + st::boxTitle.style.font->height + skip));
auto title = CreateChild<FlatLabel>(
titleWrap,
tr::lng_chat_theme_title(),
st::boxTitle);
_wrap->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
QPainter(_wrap.get()).fillRect(clip, st::windowBg);
}, lifetime());
initButtons();
initList();
_inner->positionValue(
) | rpl::start_with_next([=](QPoint position) {
title->move(std::max(position.x(), skip) + skip, skip);
}, title->lifetime());
std::move(
outer
) | rpl::start_with_next([=](QSize outer) {
_wrap->resizeToWidth(outer.width());
_wrap->move(0, outer.height() - _wrap->height());
const auto line = st::lineWidth;
_topShadow->setGeometry(0, _wrap->y() - line, outer.width(), line);
}, lifetime());
rpl::combine(
_shouldBeShown.value(),
_forceHidden.value(),
_1 && !_2
) | rpl::start_with_next([=](bool shown) {
_wrap->setVisible(shown);
_topShadow->setVisible(shown);
}, lifetime());
}
void ChooseThemeController::initButtons() {
const auto controls = _wrap->add(object_ptr<RpWidget>(_wrap.get()));
const auto cancel = CreateChild<RoundButton>(
controls,
tr::lng_cancel(),
st::defaultLightButton);
const auto apply = CreateChild<RoundButton>(
controls,
tr::lng_chat_theme_apply(),
st::defaultActiveButton);
const auto skip = st::normalFont->spacew * 2;
controls->resize(
skip + cancel->width() + skip + apply->width() + skip,
apply->height() + skip * 2);
rpl::combine(
controls->widthValue(),
cancel->widthValue(),
apply->widthValue()
) | rpl::start_with_next([=](
int outer,
int cancelWidth,
int applyWidth) {
const auto inner = skip + cancelWidth + skip + applyWidth + skip;
const auto left = (outer - inner) / 2;
cancel->moveToLeft(left, 0);
apply->moveToRight(left, 0);
}, controls->lifetime());
const auto changed = [=] {
if (_chosen.isEmpty()) {
return false;
}
const auto now = Ui::Emoji::Find(_peer->themeEmoji());
if (_chosen == kDisableElement.utf16()) {
return !now;
}
for (const auto &entry : _entries) {
if (entry.id && entry.emoji->text() == _chosen) {
return (now != entry.emoji);
}
}
return false;
};
cancel->setClickedCallback([=] { close(); });
apply->setClickedCallback([=] {
if (const auto chosen = findChosen()) {
if (Ui::Emoji::Find(_peer->themeEmoji()) != chosen->emoji) {
const auto now = chosen->id ? _chosen : QString();
_peer->setThemeEmoji(now);
if (chosen->theme) {
// Remember while changes propagate through event loop.
_controller->pushLastUsedChatTheme(chosen->theme);
}
const auto api = &_peer->session().api();
api->request(MTPmessages_SetChatTheme(
_peer->input,
MTP_string(now)
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result);
}).send();
}
}
_controller->toggleChooseChatTheme(_peer);
});
}
void ChooseThemeController::paintEntry(QPainter &p, const Entry &entry) {
const auto geometry = entry.geometry;
p.drawImage(geometry, entry.preview);
const auto size = Ui::Emoji::GetSizeLarge();
const auto factor = style::DevicePixelRatio();
const auto emojiLeft = geometry.x()
+ (geometry.width() - (size / factor)) / 2;
const auto emojiTop = geometry.y()
+ geometry.height()
- (size / factor)
- (st::normalFont->spacew * 2);
Ui::Emoji::Draw(p, entry.emoji, size, emojiLeft, emojiTop);
if (entry.chosen) {
auto hq = PainterHighQualityEnabler(p);
auto pen = st::activeLineFg->p;
const auto width = st::defaultFlatInput.borderWidth;
pen.setWidth(width);
p.setPen(pen);
const auto add = st::lineWidth + width;
p.drawRoundedRect(
entry.geometry.marginsAdded({ add, add, add, add }),
st::roundRadiusLarge + add,
st::roundRadiusLarge + add);
}
}
void ChooseThemeController::initList() {
_content->resize(
_content->width(),
8 * st::normalFont->spacew + st::settingsThemePreviewSize.height());
_inner->setMouseTracking(true);
_inner->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
auto p = QPainter(_inner.get());
for (const auto &entry : _entries) {
if (entry.preview.isNull() || !clip.intersects(entry.geometry)) {
continue;
}
paintEntry(p, entry);
}
}, lifetime());
const auto byPoint = [=](QPoint position) -> Entry* {
for (auto &entry : _entries) {
if (entry.geometry.contains(position)) {
return &entry;
}
}
return nullptr;
};
const auto chosenText = [=](const Entry *entry) {
if (!entry) {
return QString();
} else if (entry->id) {
return entry->emoji->text();
} else {
return kDisableElement.utf16();
}
};
_inner->events(
) | rpl::start_with_next([=](not_null<QEvent*> event) {
const auto type = event->type();
if (type == QEvent::MouseMove) {
const auto mouse = static_cast<QMouseEvent*>(event.get());
const auto skip = _inner->width() - _content->width();
if (skip <= 0) {
_dragStartPosition = _pressPosition = std::nullopt;
} else if (_pressPosition.has_value()
&& ((mouse->globalPos() - *_pressPosition).manhattanLength()
>= QApplication::startDragDistance())) {
_dragStartPosition = base::take(_pressPosition);
_dragStartInnerLeft = _inner->x();
}
if (_dragStartPosition.has_value()) {
const auto shift = mouse->globalPos().x()
- _dragStartPosition->x();
updateInnerLeft(_dragStartInnerLeft + shift);
} else {
_inner->setCursor(byPoint(mouse->pos())
? style::cur_pointer
: style::cur_default);
}
} else if (type == QEvent::MouseButtonPress) {
const auto mouse = static_cast<QMouseEvent*>(event.get());
if (mouse->button() == Qt::LeftButton) {
_pressPosition = mouse->globalPos();
}
_pressed = chosenText(byPoint(mouse->pos()));
} else if (type == QEvent::MouseButtonRelease) {
_pressPosition = _dragStartPosition = std::nullopt;
const auto mouse = static_cast<QMouseEvent*>(event.get());
const auto entry = byPoint(mouse->pos());
const auto chosen = chosenText(entry);
if (entry && chosen == _pressed && chosen != _chosen) {
clearCurrentBackgroundState();
if (const auto was = findChosen()) {
was->chosen = false;
}
_chosen = chosen;
entry->chosen = true;
if (entry->theme || !entry->id) {
_controller->overridePeerTheme(_peer, entry->theme);
}
_inner->update();
}
_pressed = QString();
} else if (type == QEvent::Wheel) {
const auto wheel = static_cast<QWheelEvent*>(event.get());
const auto was = _inner->x();
updateInnerLeft((wheel->angleDelta().x() != 0)
? (was + (wheel->pixelDelta().x()
? wheel->pixelDelta().x()
: wheel->angleDelta().x()))
: (wheel->angleDelta().y() != 0)
? (was + (wheel->pixelDelta().y()
? wheel->pixelDelta().y()
: wheel->angleDelta().y()))
: was);
}
}, lifetime());
_content->events(
) | rpl::start_with_next([=](not_null<QEvent*> event) {
const auto type = event->type();
if (type == QEvent::KeyPress) {
const auto key = static_cast<QKeyEvent*>(event.get());
if (key->key() == Qt::Key_Escape) {
close();
}
}
}, lifetime());
rpl::combine(
_content->widthValue(),
_inner->widthValue()
) | rpl::start_with_next([=](int content, int inner) {
if (!content || !inner) {
return;
} else if (!_entries.empty() && !_initialInnerLeftApplied) {
applyInitialInnerLeft();
} else {
updateInnerLeft(_inner->x());
}
}, lifetime());
}
void ChooseThemeController::applyInitialInnerLeft() {
if (const auto chosen = findChosen()) {
updateInnerLeft(
_content->width() / 2 - chosen->geometry.center().x());
}
_initialInnerLeftApplied = true;
}
void ChooseThemeController::updateInnerLeft(int now) {
const auto skip = _content->width() - _inner->width();
const auto clamped = (skip >= 0)
? (skip / 2)
: std::clamp(now, skip, 0);
_inner->move(clamped, 0);
}
void ChooseThemeController::close() {
if (const auto chosen = findChosen()) {
if (Ui::Emoji::Find(_peer->themeEmoji()) != chosen->emoji) {
clearCurrentBackgroundState();
}
}
_controller->toggleChooseChatTheme(_peer);
}
void ChooseThemeController::clearCurrentBackgroundState() {
if (const auto entry = findChosen()) {
if (entry->theme) {
entry->theme->clearBackgroundState();
}
}
}
auto ChooseThemeController::findChosen() -> Entry* {
if (_chosen.isEmpty()) {
return nullptr;
}
for (auto &entry : _entries) {
if (!entry.id && _chosen == kDisableElement.utf16()) {
return &entry;
} else if (_chosen == entry.emoji->text()) {
return &entry;
}
}
return nullptr;
}
auto ChooseThemeController::findChosen() const -> const Entry* {
return const_cast<ChooseThemeController*>(this)->findChosen();
}
void ChooseThemeController::fill(
const std::vector<Data::ChatTheme> &themes) {
if (themes.empty()) {
return;
}
const auto count = int(themes.size()) + 1;
const auto single = st::settingsThemePreviewSize;
const auto skip = st::normalFont->spacew * 2;
const auto full = single.width() * count + skip * (count + 3);
_inner->resize(full, skip + single.height() + skip);
const auto initial = Ui::Emoji::Find(_peer->themeEmoji());
_dark.value(
) | rpl::start_with_next([=](bool dark) {
clearCurrentBackgroundState();
if (_chosen.isEmpty() && initial) {
_chosen = initial->text();
}
_cachingLifetime.destroy();
const auto old = base::take(_entries);
auto x = skip * 2;
_entries.push_back({
.preview = GenerateEmptyPreview(),
.emoji = Ui::Emoji::Find(QString::fromUtf8("\xe2\x9d\x8c")),
.geometry = QRect(QPoint(x, skip), single),
.chosen = (_chosen == kDisableElement.utf16()),
});
Assert(_entries.front().emoji != nullptr);
style::PaletteChanged(
) | rpl::start_with_next([=] {
_entries.front().preview = GenerateEmptyPreview();
}, _cachingLifetime);
x += single.width() + skip;
for (const auto &theme : themes) {
const auto emoji = Ui::Emoji::Find(theme.emoticon);
if (!emoji) {
continue;
}
const auto &used = dark ? theme.dark : theme.light;
const auto id = used.id;
const auto isChosen = (_chosen == emoji->text());
_entries.push_back({
.id = id,
.emoji = emoji,
.geometry = QRect(QPoint(x, skip), single),
.chosen = isChosen,
});
_controller->cachedChatThemeValue(
used
) | rpl::filter([=](const std::shared_ptr<ChatTheme> &data) {
return data && (data->key() == id);
}) | rpl::take(
1
) | rpl::start_with_next([=](std::shared_ptr<ChatTheme> &&data) {
const auto id = data->key();
const auto i = ranges::find(_entries, id, &Entry::id);
if (i == end(_entries)) {
return;
}
const auto theme = data.get();
i->theme = std::move(data);
i->preview = GeneratePreview(theme);
if (_chosen == i->emoji->text()) {
_controller->overridePeerTheme(_peer, i->theme);
}
_inner->update();
if (!theme->background().isPattern
|| !theme->background().prepared.isNull()) {
return;
}
// Subscribe to pattern loading if needed.
theme->repaintBackgroundRequests(
) | rpl::filter([=] {
const auto i = ranges::find(
_entries,
id,
&Entry::id);
return (i == end(_entries))
|| !i->theme->background().prepared.isNull();
}) | rpl::take(1) | rpl::start_with_next([=] {
const auto i = ranges::find(
_entries,
id,
&Entry::id);
if (i == end(_entries)) {
return;
}
i->preview = GeneratePreview(theme);
_inner->update();
}, _cachingLifetime);
}, _cachingLifetime);
x += single.width() + skip;
}
if (!_initialInnerLeftApplied && _content->width() > 0) {
applyInitialInnerLeft();
}
}, lifetime());
_shouldBeShown = true;
}
bool ChooseThemeController::shouldBeShown() const {
return _shouldBeShown.current();
}
rpl::producer<bool> ChooseThemeController::shouldBeShownValue() const {
return _shouldBeShown.value();
}
int ChooseThemeController::height() const {
return shouldBeShown() ? _wrap->height() : 0;
}
void ChooseThemeController::hide() {
_forceHidden = true;
}
void ChooseThemeController::show() {
_forceHidden = false;
}
void ChooseThemeController::raise() {
_wrap->raise();
_topShadow->raise();
}
void ChooseThemeController::setFocus() {
_content->setFocus();
}
rpl::lifetime &ChooseThemeController::lifetime() {
return _wrap->lifetime();
}
} // namespace Ui

View File

@@ -0,0 +1,84 @@
/*
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
class PeerData;
namespace Window {
class SessionController;
} // namespace Window
namespace Data {
struct ChatTheme;
} // namespace Data
namespace Ui {
class RpWidget;
class PlainShadow;
class VerticalLayout;
class ChooseThemeController final {
public:
ChooseThemeController(
not_null<RpWidget*> parent,
not_null<Window::SessionController*> window,
not_null<PeerData*> peer);
~ChooseThemeController();
[[nodiscard]] bool shouldBeShown() const;
[[nodiscard]] rpl::producer<bool> shouldBeShownValue() const;
[[nodiscard]] int height() const;
void hide();
void show();
void raise();
void setFocus();
[[nodiscard]] rpl::lifetime &lifetime();
private:
struct Entry;
void init(rpl::producer<QSize> outer);
void initButtons();
void initList();
void fill(const std::vector<Data::ChatTheme> &themes);
void close();
void clearCurrentBackgroundState();
void paintEntry(QPainter &p, const Entry &entry);
void applyInitialInnerLeft();
void updateInnerLeft(int now);
[[nodiscard]] Entry *findChosen();
[[nodiscard]] const Entry *findChosen() const;
const not_null<Window::SessionController*> _controller;
const not_null<PeerData*> _peer;
const std::unique_ptr<VerticalLayout> _wrap;
const std::unique_ptr<PlainShadow> _topShadow;
const not_null<RpWidget*> _content;
const not_null<RpWidget*> _inner;
std::vector<Entry> _entries;
QString _pressed;
QString _chosen;
std::optional<QPoint> _pressPosition;
std::optional<QPoint> _dragStartPosition;
int _dragStartInnerLeft = 0;
bool _initialInnerLeftApplied = false;
rpl::variable<bool> _shouldBeShown = false;
rpl::variable<bool> _forceHidden = false;
rpl::variable<bool> _dark = false;
rpl::lifetime _cachingLifetime;
};
} // namespace Ui

View File

@@ -214,7 +214,8 @@ Action::Action(
content
) | rpl::start_with_next([=](WhoReadContent &&content) {
checkAppeared();
const auto changed = (_content.participants != content.participants);
const auto changed = (_content.participants != content.participants)
|| (_content.unknown != content.unknown);
_content = content;
if (changed) {
PostponeCall(this, [=] { populateSubmenu(); });

View File

@@ -25,8 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Window {
namespace {
constexpr auto kDarkValueThreshold = 0.5;
[[nodiscard]] rpl::producer<QString> PeerThemeEmojiValue(
not_null<PeerData*> peer) {
return peer->session().changes().peerFlagsValue(
@@ -51,17 +49,9 @@ constexpr auto kDarkValueThreshold = 0.5;
[[nodiscard]] auto MaybeCloudThemeValueFromPeer(
not_null<PeerData*> peer)
-> rpl::producer<std::optional<Data::CloudTheme>> {
auto isThemeDarkValue = rpl::single(
rpl::empty_value()
) | rpl::then(
style::PaletteChanged()
) | rpl::map([] {
return (st::dialogsBg->c.valueF() < kDarkValueThreshold);
}) | rpl::distinct_until_changed();
return rpl::combine(
MaybeChatThemeDataValueFromPeer(peer),
std::move(isThemeDarkValue)
Theme::IsThemeDarkValue() | rpl::distinct_until_changed()
) | rpl::map([](std::optional<Data::ChatTheme> theme, bool night) {
return !theme
? std::nullopt
@@ -304,7 +294,7 @@ auto ChatThemeValueFromPeer(
not_null<SessionController*> controller,
not_null<PeerData*> peer)
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
return MaybeCloudThemeValueFromPeer(
auto cloud = MaybeCloudThemeValueFromPeer(
peer
) | rpl::map([=](std::optional<Data::CloudTheme> theme)
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
@@ -314,6 +304,17 @@ auto ChatThemeValueFromPeer(
return controller->cachedChatThemeValue(*theme);
}) | rpl::flatten_latest(
) | rpl::distinct_until_changed();
return rpl::combine(
std::move(cloud),
controller->peerThemeOverrideValue()
) | rpl::map([=](
std::shared_ptr<Ui::ChatTheme> &&cloud,
PeerThemeOverride &&overriden) {
return (overriden.peer == peer.get())
? std::move(overriden.theme)
: std::move(cloud);
});
}
} // namespace Window

View File

@@ -46,6 +46,7 @@ namespace {
constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024;
constexpr auto kBackgroundSizeLimit = 25 * 1024 * 1024;
constexpr auto kNightThemeFile = ":/gui/night.tdesktop-theme"_cs;
constexpr auto kDarkValueThreshold = 0.5;
struct Applying {
Saved data;
@@ -1450,6 +1451,16 @@ bool LoadFromContent(
out);
}
rpl::producer<bool> IsThemeDarkValue() {
return rpl::single(
rpl::empty_value()
) | rpl::then(
style::PaletteChanged()
) | rpl::map([] {
return (st::dialogsBg->c.valueF() < kDarkValueThreshold);
});
}
QString EditingPalettePath() {
return cWorkingDir() + "tdata/editing-theme.tdesktop-palette";
}

View File

@@ -97,6 +97,8 @@ void ResetToSomeDefault();
[[nodiscard]] bool IsNonDefaultBackground();
void Revert();
[[nodiscard]] rpl::producer<bool> IsThemeDarkValue();
[[nodiscard]] QString EditingPalettePath();
// NB! This method looks to Core::App().settings() to get colorizer by 'file'.

View File

@@ -505,7 +505,13 @@ void Generator::paintComposeArea() {
_p->fillRect(_composeArea, st::historyReplyBg[_palette]);
auto controlsTop = _composeArea.y() + _composeArea.height() - st::historySendSize.height();
st::historyAttach.icon[_palette].paint(*_p, _composeArea.x() + st::historyAttach.iconPosition.x(), controlsTop + st::historyAttach.iconPosition.y(), _rect.width());
const auto attachIconLeft = (st::historyAttach.iconPosition.x() < 0)
? ((st::historyAttach.width - st::historyAttach.icon.width()) / 2)
: st::historyAttach.iconPosition.x();
const auto attachIconTop = (st::historyAttach.iconPosition.y() < 0)
? ((st::historyAttach.height - st::historyAttach.icon.height()) / 2)
: st::historyAttach.iconPosition.y();
st::historyAttach.icon[_palette].paint(*_p, _composeArea.x() + attachIconLeft, controlsTop + attachIconTop, _rect.width());
auto right = st::historySendRight + st::historySendSize.width();
st::historyRecordVoice[_palette].paintInCenter(*_p, QRect(_composeArea.x() + _composeArea.width() - right, controlsTop, st::historySendSize.width(), st::historySendSize.height()));

View File

@@ -487,6 +487,11 @@ void Filler::addUserActions(not_null<UserData*> user) {
[=] { AddBotToGroup::Start(user); });
}
addPollAction(user);
if (!user->isBot()) {
_addAction(
tr::lng_chat_theme_change(tr::now),
[=] { controller->toggleChooseChatTheme(user); });
}
if (user->canExportChatHistory()) {
_addAction(
tr::lng_profile_export_chat(tr::now),

View File

@@ -125,6 +125,14 @@ void ActivateWindow(not_null<SessionController*> controller) {
Ui::ActivateWindowDelayed(window);
}
bool operator==(const PeerThemeOverride &a, const PeerThemeOverride &b) {
return (a.peer == b.peer) && (a.theme == b.theme);
}
bool operator!=(const PeerThemeOverride &a, const PeerThemeOverride &b) {
return !(a == b);
}
DateClickHandler::DateClickHandler(Dialogs::Key chat, QDate date)
: _chat(chat)
, _date(date) {
@@ -1208,6 +1216,10 @@ void SessionController::clearChooseReportMessages() {
content()->clearChooseReportMessages();
}
void SessionController::toggleChooseChatTheme(not_null<PeerData*> peer) {
content()->toggleChooseChatTheme(peer);
}
void SessionController::updateColumnLayout() {
content()->updateColumnLayout();
}
@@ -1397,7 +1409,7 @@ auto SessionController::cachedChatThemeValue(
const auto i = _customChatThemes.find(key);
if (i != end(_customChatThemes)) {
if (auto strong = i->second.theme.lock()) {
pushToLastUsed(strong);
pushLastUsedChatTheme(strong);
return rpl::single(std::move(strong));
}
}
@@ -1413,12 +1425,12 @@ auto SessionController::cachedChatThemeValue(
if (theme->key() != key) {
return false;
}
pushToLastUsed(theme);
pushLastUsedChatTheme(theme);
return true;
}) | rpl::take(limit));
}
void SessionController::pushToLastUsed(
void SessionController::pushLastUsedChatTheme(
const std::shared_ptr<Ui::ChatTheme> &theme) {
const auto i = ranges::find(_lastUsedCustomChatThemes, theme);
if (i == end(_lastUsedCustomChatThemes)) {
@@ -1444,6 +1456,21 @@ void SessionController::clearCachedChatThemes() {
_customChatThemes.clear();
}
void SessionController::overridePeerTheme(
not_null<PeerData*> peer,
std::shared_ptr<Ui::ChatTheme> theme) {
_peerThemeOverride = PeerThemeOverride{
peer,
theme ? theme : _defaultChatTheme,
};
}
void SessionController::clearPeerThemeOverride(not_null<PeerData*> peer) {
if (_peerThemeOverride.current().peer == peer.get()) {
_peerThemeOverride = PeerThemeOverride();
}
}
void SessionController::pushDefaultChatBackground() {
const auto background = Theme::Background();
const auto &paper = background->paper();

View File

@@ -75,6 +75,13 @@ enum class GifPauseReason {
using GifPauseReasons = base::flags<GifPauseReason>;
inline constexpr bool is_flag_type(GifPauseReason) { return true; };
struct PeerThemeOverride {
PeerData *peer = nullptr;
std::shared_ptr<Ui::ChatTheme> theme;
};
bool operator==(const PeerThemeOverride &a, const PeerThemeOverride &b);
bool operator!=(const PeerThemeOverride &a, const PeerThemeOverride &b);
class DateClickHandler : public ClickHandler {
public:
DateClickHandler(Dialogs::Key chat, QDate date);
@@ -378,6 +385,8 @@ public:
Fn<void(MessageIdsList)> done);
void clearChooseReportMessages();
void toggleChooseChatTheme(not_null<PeerData*> peer);
base::Variable<bool> &dialogsListFocused() {
return _dialogsListFocused;
}
@@ -412,6 +421,16 @@ public:
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>>;
void setChatStyleTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
void clearCachedChatThemes();
void pushLastUsedChatTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
void overridePeerTheme(
not_null<PeerData*> peer,
std::shared_ptr<Ui::ChatTheme> theme);
void clearPeerThemeOverride(not_null<PeerData*> peer);
[[nodiscard]] auto peerThemeOverrideValue() const
-> rpl::producer<PeerThemeOverride> {
return _peerThemeOverride.value();
}
struct PaintContextArgs {
not_null<Ui::ChatTheme*> theme;
@@ -464,7 +483,6 @@ private:
[[nodiscard]] Ui::ChatThemeBackgroundData backgroundData(
CachedTheme &theme,
bool generateGradient = true) const;
void pushToLastUsed(const std::shared_ptr<Ui::ChatTheme> &theme);
const not_null<Controller*> _window;
const std::unique_ptr<ChatHelpers::EmojiInteractions> _emojiInteractions;
@@ -500,6 +518,7 @@ private:
const std::unique_ptr<Ui::ChatStyle> _chatStyle;
std::weak_ptr<Ui::ChatTheme> _chatStyleTheme;
std::deque<std::shared_ptr<Ui::ChatTheme>> _lastUsedCustomChatThemes;
rpl::variable<PeerThemeOverride> _peerThemeOverride;
rpl::lifetime _lifetime;

View File

@@ -26,10 +26,11 @@ SHELL [ "scl", "enable", "devtoolset-9", "--", "bash", "-c" ]
RUN ln -s cmake3 /usr/bin/cmake
ENV LibrariesPath /usr/src/Libraries
ENV HFLAGS "-fstack-protector-all -fstack-clash-protection -fPIC -D_FORTIFY_SOURCE=2"
WORKDIR $LibrariesPath
FROM builder AS patches
RUN git clone $GIT/desktop-app/patches.git && cd patches && git checkout 9d2a07ba8b
RUN git clone $GIT/desktop-app/patches.git && cd patches && git checkout 872c8dc01c
FROM builder AS extra-cmake-modules
@@ -59,7 +60,7 @@ FROM builder AS zlib
RUN git clone -b v1.2.11 --depth=1 $GIT/madler/zlib.git
WORKDIR zlib
RUN ./configure
RUN CFLAGS=\"-O3 $HFLAGS\" ./configure --static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/zlib-cache" install
@@ -70,7 +71,8 @@ FROM builder AS xz
RUN git clone -b v5.2.5 https://git.tukaani.org/xz.git
WORKDIR xz
RUN cmake3 -B build . -DCMAKE_BUILD_TYPE=Release
RUN CFLAGS=\"$HFLAGS\" \
cmake3 -B build . -DCMAKE_BUILD_TYPE=Release
RUN cmake3 --build build -j$(nproc)
RUN DESTDIR="$LibrariesPath/xz-cache" cmake3 --install build
@@ -98,7 +100,7 @@ RUN git clone -b 0.4.17 --depth=1 $GIT/libproxy/libproxy.git
WORKDIR libproxy
RUN git apply ../patches/libproxy.patch
RUN cmake3 -B build . \
RUN CFLAGS=\"$HFLAGS\" CXXFLAGS=\"$HFLAGS\" cmake3 -B build . \
-DCMAKE_BUILD_TYPE=Release \
-DWITH_DBUS=OFF \
-DWITH_NM=OFF \
@@ -114,7 +116,7 @@ FROM builder AS mozjpeg
RUN git clone -b v4.0.1-rc2 --depth=1 $GIT/mozilla/mozjpeg.git
WORKDIR mozjpeg
RUN cmake3 -B build . \
RUN CFLAGS=\"$HFLAGS\" cmake3 -B build . \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DWITH_JPEG8=ON \
@@ -131,7 +133,7 @@ RUN git clone -b v1.3 --depth=1 $GIT/xiph/opus.git
WORKDIR opus
RUN ./autogen.sh
RUN ./configure
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./configure
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/opus-cache" install
@@ -142,7 +144,7 @@ FROM builder AS rnnoise
RUN git clone -b master --depth=1 $GIT/desktop-app/rnnoise
WORKDIR rnnoise
RUN cmake3 -B build . \
RUN CFLAGS=\"$HFLAGS\" cmake3 -B build . \
-DCMAKE_BUILD_TYPE=Release
RUN cmake3 --build build -j$(nproc)
@@ -158,7 +160,7 @@ FROM builder AS xcb-proto
RUN git clone -b xcb-proto-1.14 --depth=1 https://gitlab.freedesktop.org/xorg/proto/xcbproto.git
WORKDIR xcbproto
RUN ./autogen.sh --enable-static
RUN ./autogen.sh
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/xcb-proto-cache" install
@@ -171,7 +173,7 @@ COPY --from=xcb-proto ${LibrariesPath}/xcb-proto-cache /
RUN git clone -b libxcb-1.14 --depth=1 https://gitlab.freedesktop.org/xorg/lib/libxcb.git
WORKDIR libxcb
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/xcb-cache" install
@@ -202,7 +204,7 @@ COPY --from=xcb-util ${LibrariesPath}/xcb-util-cache /
RUN git clone -b 0.4.0 --depth=1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-image.git
WORKDIR libxcb-image
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/xcb-image-cache" install
@@ -211,7 +213,7 @@ FROM builder AS xcb-keysyms
RUN git clone -b 0.4.0 --depth=1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-keysyms.git
WORKDIR libxcb-keysyms
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/xcb-keysyms-cache" install
@@ -220,7 +222,7 @@ FROM builder AS xcb-render-util
RUN git clone -b 0.3.9 --depth=1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-render-util.git
WORKDIR libxcb-render-util
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/xcb-render-util-cache" install
@@ -228,7 +230,7 @@ FROM builder AS libXext
RUN git clone -b libXext-1.3.4 --depth=1 https://gitlab.freedesktop.org/xorg/lib/libxext.git
WORKDIR libxext
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/libXext-cache" install
@@ -239,7 +241,7 @@ FROM builder AS libXtst
RUN git clone -b libXtst-1.2.3 --depth=1 https://gitlab.freedesktop.org/xorg/lib/libxtst.git
WORKDIR libxtst
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/libXtst-cache" install
@@ -250,7 +252,7 @@ FROM builder AS libXfixes
RUN git clone -b libXfixes-5.0.3 --depth=1 https://gitlab.freedesktop.org/xorg/lib/libxfixes.git
WORKDIR libxfixes
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/libXfixes-cache" install
@@ -274,7 +276,7 @@ FROM builder AS libXrandr
RUN git clone -b libXrandr-1.5.2 --depth=1 https://gitlab.freedesktop.org/xorg/lib/libxrandr.git
WORKDIR libxrandr
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/libXrandr-cache" install
@@ -285,7 +287,7 @@ FROM builder AS libXrender
RUN git clone -b libXrender-0.9.10 --depth=1 https://gitlab.freedesktop.org/xorg/lib/libxrender.git
WORKDIR libxrender
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/libXrender-cache" install
@@ -296,7 +298,7 @@ FROM builder AS libXdamage
RUN git clone -b libXdamage-1.1.5 --depth=1 https://gitlab.freedesktop.org/xorg/lib/libxdamage.git
WORKDIR libxdamage
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/libXdamage-cache" install
@@ -307,7 +309,7 @@ FROM builder AS libXcomposite
RUN git clone -b libXcomposite-0.4.5 --depth=1 https://gitlab.freedesktop.org/xorg/lib/libxcomposite.git
WORKDIR libxcomposite
RUN ./autogen.sh --enable-static
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/libXcomposite-cache" install
@@ -396,7 +398,7 @@ COPY --from=drm ${LibrariesPath}/drm-cache /
RUN git clone -b 2.10.0 --depth=1 $GIT/intel/libva.git
WORKDIR libva
RUN ./autogen.sh \
RUN CFLAGS=\"-g -O2 $HFLAGS\" ./autogen.sh \
--enable-static \
--sysconfdir=/etc \
--with-drivers-path=/usr/lib/dri
@@ -440,8 +442,8 @@ RUN git clone -b release/4.4 --depth=1 $GIT/FFmpeg/FFmpeg.git ffmpeg
WORKDIR ffmpeg
RUN ./configure \
--extra-cflags="-DCONFIG_SAFE_BITSTREAM_READER=1" \
--extra-cxxflags="-DCONFIG_SAFE_BITSTREAM_READER=1" \
--extra-cflags=\"-DCONFIG_SAFE_BITSTREAM_READER=1 $HFLAGS\" \
--extra-cxxflags=\"-DCONFIG_SAFE_BITSTREAM_READER=1 $HFLAGS\" \
--disable-debug \
--disable-programs \
--disable-doc \
@@ -553,7 +555,7 @@ ADD https://api.github.com/repos/telegramdesktop/openal-soft/git/refs/heads/fix_
RUN git clone -b fix_pulse_default --depth=1 $GIT/telegramdesktop/openal-soft.git
WORKDIR openal-soft
RUN cmake3 -B build . \
RUN CFLAGS=\"$HFLAGS\" CXXFLAGS=\"$HFLAGS\" cmake3 -B build . \
-DCMAKE_BUILD_TYPE=Release \
-DLIBTYPE:STRING=STATIC \
-DALSOFT_EXAMPLES=OFF \
@@ -627,7 +629,7 @@ RUN git clone -b 2.10.6 --depth=1 $GIT/libsigcplusplus/libsigcplusplus.git
WORKDIR libsigcplusplus
ENV ACLOCAL_PATH="/usr/local/share/aclocal"
RUN NOCONFIGURE=1 ./autogen.sh
RUN ./configure --enable-maintainer-mode --enable-static --disable-documentation
RUN CFLAGS=\"-g -O2 $HFLAGS\" CXXFLAGS=\"-g -O2 $HFLAGS\" ./configure --enable-maintainer-mode --enable-static --disable-documentation
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/libsigcplusplus-cache" install
@@ -646,7 +648,7 @@ WORKDIR glibmm
RUN git apply ../patches/glibmm.patch
ENV ACLOCAL_PATH="/usr/local/share/aclocal"
RUN NOCONFIGURE=1 ./autogen.sh
RUN CC=\"gcc -flto\" CXX=\"g++ -flto\" AR=gcc-ar RANLIB=gcc-ranlib ./configure \
RUN CC=\"gcc -flto $HFLAGS\" CXX=\"g++ -flto $HFLAGS\" AR=gcc-ar RANLIB=gcc-ranlib ./configure \
--enable-maintainer-mode \
--enable-static \
--disable-documentation
@@ -810,7 +812,7 @@ RUN meson build
WORKDIR ../../..
RUN cmake3 -B out/Release . \
RUN CFLAGS=\"$HFLAGS\" CXXFLAGS=\"$HFLAGS\" cmake3 -B out/Release . \
-DCMAKE_BUILD_TYPE=Release \
-DTG_OWT_BUILD_AUDIO_BACKENDS=OFF \
-DTG_OWT_SPECIAL_TARGET=linux \
@@ -821,7 +823,8 @@ RUN cmake3 -B out/Release . \
RUN cmake3 --build out/Release -- -j$(nproc)
RUN cmake3 -B out/Debug . \
ENV HFLAGS_DEBUG="-fstack-protector-all -fstack-clash-protection -fPIC"
RUN CFLAGS=\"$HFLAGS_DEBUG\" CXXFLAGS=\"$HFLAGS_DEBUG\" cmake3 -B out/Debug . \
-DCMAKE_BUILD_TYPE=Debug \
-DTG_OWT_SPECIAL_TARGET=linux \
-DTG_OWT_LIBJPEG_INCLUDE_PATH=/usr/local/include \

View File

@@ -684,6 +684,7 @@ depends:yasm/yasm
""")
stage('openal-soft', """
version: 2
git clone -b wasapi_exact_device_time https://github.com/telegramdesktop/openal-soft.git
cd openal-soft
cd build

View File

@@ -1,7 +1,7 @@
AppVersion 3001004
AppVersion 3001005
AppVersionStrMajor 3.1
AppVersionStrSmall 3.1.4
AppVersionStr 3.1.4
AppVersionStrSmall 3.1.5
AppVersionStr 3.1.5
BetaChannel 1
AlphaVersion 0
AppVersionOriginal 3.1.4.beta
AppVersionOriginal 3.1.5.beta

View File

@@ -1,3 +1,13 @@
3.1.5 beta (28.09.21)
- Choose one of 8 new preset themes for any individual private chat.
- Click on '...' menu > 'Change Colors' to pick a theme.
- Both chat participants will see the same theme in that chat on all their devices.
- Each new theme features colorful gradient message bubbles, beautifully animated backgrounds and unique background patterns.
- All chat themes have day and night versions and will follow your overall dark mode settings.
- Implement main window rounded corners on Windows 11.
- Fix audio capture from AirPods on macOS.
3.1.4 beta (27.09.21)
- Fix crash in network availability init.

2
cmake

Submodule cmake updated: 1dacc0ac4f...18d7c34ce1