Compare commits

..

14 Commits

Author SHA1 Message Date
John Preston
63919422e0 Version 3.2: Fix build error on Linux. 2021-11-03 17:56:32 +04:00
John Preston
d6e6c51639 Version 3.2.
- Create special invite links that require admins
to approve users before they become members.
- Admins can view the applicants' profiles and bios
by tapping the Join Requests bar at the top of the chat.
- Add internal labels to your chat's Invite Links
to keep them organized.
- More Interactive Emoji - 👻, :dislike:, :face_vomiting:,
😂, 💸 or 🎃
2021-11-03 16:38:14 +04:00
John Preston
95a24d6aa1 Don't limit invite import aboutRequests text. 2021-11-03 16:27:51 +04:00
John Preston
fa28b0b405 Fallback to "Desktop" instead of "PC". 2021-11-03 16:24:37 +04:00
John Preston
42fcf4ceb2 Add patch system version component on macOS. 2021-11-03 16:18:31 +04:00
John Preston
3743dd0161 Fix small window icon counter.
Regression was introduced in aef45b3a1d.
2021-11-03 16:14:20 +04:00
John Preston
dab859ea29 Fix sponsored messages about text layout. 2021-11-03 16:01:38 +04:00
John Preston
a92394a81f Hide "Request admin approval" in public peers. 2021-11-03 16:01:25 +04:00
John Preston
7a57174ab1 Register sponsored view only on full message show. 2021-11-03 15:49:48 +04:00
23rd
52bacb3cde Improved tracking of views for sponsored messages. 2021-11-02 20:58:35 +03:00
23rd
8ad9770118 Fixed missed CallId alias. 2021-11-02 20:58:35 +03:00
John Preston
aef45b3a1d Refactor icon unread counter painting. 2021-11-02 20:12:57 +04:00
John Preston
87af865604 Fix warning about inactive QPainter. 2021-11-02 20:12:51 +04:00
John Preston
4efeaacf5c Add send context menu to StickerSetBox. 2021-11-02 20:11:20 +04:00
39 changed files with 540 additions and 384 deletions

View File

@@ -1331,7 +1331,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_invite_qr_title" = "Invite by QR Code";
"lng_group_invite_qr_about" = "Everyone on Telegram can scan this code to join your group.";
"lng_group_invite_qr_copied" = "QR Code copied to clipboard.";
"lng_group_invite_request_approve" = "Approve everyone who joins";
"lng_group_invite_request_approve" = "Request admin approval";
"lng_group_invite_about_approve" = "New users will be able to join the group only after having been approved by the admins.";
"lng_group_invite_about_no_approve" = "New users will be able to join the group without being approved by the admins.";
"lng_group_invite_about_approve_channel" = "New users will be able to join the channel only after having been approved by the admins.";

View File

@@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="3.1.13.0" />
Version="3.2.0.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,13,0
PRODUCTVERSION 3,1,13,0
FILEVERSION 3,2,0,0
PRODUCTVERSION 3,2,0,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.13.0"
VALUE "FileVersion", "3.2.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "3.1.13.0"
VALUE "ProductVersion", "3.2.0.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,1,13,0
PRODUCTVERSION 3,1,13,0
FILEVERSION 3,2,0,0
PRODUCTVERSION 3,2,0,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.13.0"
VALUE "FileVersion", "3.2.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "3.1.13.0"
VALUE "ProductVersion", "3.2.0.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -98,6 +98,7 @@ confirmInviteAbout: FlatLabel(boxLabel) {
confirmInviteStatus: FlatLabel(confirmInviteAbout) {
textFg: windowSubTextFg;
style: boxLabelStyle;
maxHeight: 0px;
}
confirmInviteAboutPadding: margins(36px, 4px, 36px, 10px);
confirmInviteAboutRequestsPadding: margins(36px, 9px, 36px, 15px);
@@ -373,7 +374,7 @@ aboutVersionLink: LinkButton(defaultLinkButton) {
aboutTextTop: 34px;
aboutSkip: 14px;
aboutLabel: FlatLabel(defaultFlatLabel) {
minWidth: 330px;
minWidth: 300px;
align: align(topleft);
style: TextStyle(defaultTextStyle) {
lineHeight: 22px;

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer.h"
#include "data/data_user.h"
#include "data/data_channel.h"
#include "data/data_changes.h"
#include "data/data_session.h"
#include "data/data_histories.h"
@@ -1211,9 +1212,10 @@ void EditLink(
}
};
const auto isGroup = !peer->isBroadcast();
const auto isPublic = peer->isChannel() && peer->asChannel()->isPublic();
*box = Ui::show(
(creating
? Box(Ui::CreateInviteLinkBox, isGroup, done)
? Box(Ui::CreateInviteLinkBox, isGroup, isPublic, done)
: Box(
Ui::EditInviteLinkBox,
Fields{
@@ -1223,6 +1225,7 @@ void EditLink(
.usageLimit = data.usageLimit,
.requestApproval = data.requestApproval,
.isGroup = isGroup,
.isPublic = isPublic,
},
done)),
Ui::LayerOption::KeepOther);

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_origin.h"
#include "data/data_document_media.h"
#include "data/stickers/data_stickers.h"
#include "chat_helpers/send_context_menu.h"
#include "lang/lang_keys.h"
#include "ui/boxes/confirm_box.h"
#include "core/application.h"
@@ -35,6 +36,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "api/api_toggling_media.h"
#include "api/api_common.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "styles/style_layers.h"
@@ -87,6 +90,7 @@ protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void leaveEventHook(QEvent *e) override;
@@ -113,6 +117,8 @@ private:
void gotSet(const MTPmessages_StickerSet &set);
void installDone(const MTPmessages_StickerSetInstallResult &result);
void send(not_null<DocumentData*> sticker, Api::SendOptions options);
not_null<Lottie::MultiPlayer*> getLottiePlayer();
void showPreview();
@@ -144,6 +150,8 @@ private:
base::Timer _previewTimer;
int _previewShown = -1;
base::unique_qptr<Ui::PopupMenu> _menu;
rpl::event_stream<uint64> _setInstalled;
rpl::event_stream<uint64> _setArchived;
rpl::event_stream<> _updateControls;
@@ -577,10 +585,14 @@ void StickerSetBox::Inner::installDone(
}
void StickerSetBox::Inner::mousePressEvent(QMouseEvent *e) {
int index = stickerFromGlobalPos(e->globalPos());
if (index >= 0 && index < _pack.size()) {
_previewTimer.callOnce(QApplication::startDragTime());
if (e->button() != Qt::LeftButton) {
return;
}
const auto index = stickerFromGlobalPos(e->globalPos());
if (index < 0 || index >= _pack.size()) {
return;
}
_previewTimer.callOnce(QApplication::startDragTime());
}
void StickerSetBox::Inner::mouseMoveEvent(QMouseEvent *e) {
@@ -605,18 +617,62 @@ void StickerSetBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
_previewShown = -1;
return;
}
if (_previewTimer.isActive()) {
_previewTimer.cancel();
const auto index = stickerFromGlobalPos(e->globalPos());
if (index >= 0 && index < _pack.size() && !isMasksSet()) {
const auto sticker = _pack[index];
Ui::PostponeCall(crl::guard(_controller, [=] {
if (_controller->content()->sendExistingDocument(sticker)) {
Ui::hideSettingsAndLayer();
}
}));
}
if (!_previewTimer.isActive()) {
return;
}
_previewTimer.cancel();
const auto index = stickerFromGlobalPos(e->globalPos());
if (index < 0 || index >= _pack.size() || isMasksSet()) {
return;
}
send(_pack[index], Api::SendOptions());
}
void StickerSetBox::Inner::send(
not_null<DocumentData*> sticker,
Api::SendOptions options) {
const auto controller = _controller;
Ui::PostponeCall(controller, [=] {
if (controller->content()->sendExistingDocument(sticker, options)) {
Ui::hideSettingsAndLayer();
}
});
}
void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) {
const auto index = stickerFromGlobalPos(e->globalPos());
if (index < 0 || index >= _pack.size()) {
return;
}
const auto type = _controller->content()->sendMenuType();
if (type == SendMenu::Type::Disabled) {
return;
}
_previewTimer.cancel();
_menu = base::make_unique_q<Ui::PopupMenu>(this);
const auto document = _pack[index];
const auto sendSelected = [=](Api::SendOptions options) {
send(document, options);
};
SendMenu::FillSendMenu(
_menu.get(),
type,
SendMenu::DefaultSilentCallback(sendSelected),
SendMenu::DefaultScheduleCallback(this, type, sendSelected));
const auto toggleFavedSticker = [=] {
Api::ToggleFavedSticker(
document,
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0));
};
_menu->addAction(
(document->owner().stickers().isFaved(document)
? tr::lng_faved_stickers_remove
: tr::lng_faved_stickers_add)(tr::now),
toggleFavedSticker);
_menu->popup(QCursor::pos());
}
void StickerSetBox::Inner::updateSelected() {

View File

@@ -101,7 +101,7 @@ public:
[[nodiscard]] not_null<UserData*> user() const {
return _user;
}
[[nodiscard]] uint64 id() const {
[[nodiscard]] CallId id() const {
return _id;
}
[[nodiscard]] bool isIncomingWaiting() const;
@@ -278,7 +278,7 @@ private:
bytes::vector _randomPower;
MTP::AuthKey::Data _authKey;
uint64 _id = 0;
CallId _id = 0;
uint64 _accessHash = 0;
uint64 _keyFingerprint = 0;

View File

@@ -141,12 +141,7 @@ Application::Application(not_null<Launcher*> launcher)
, _langpack(std::make_unique<Lang::Instance>())
, _langCloudManager(std::make_unique<Lang::CloudManager>(langpack()))
, _emojiKeywords(std::make_unique<ChatHelpers::EmojiKeywords>())
, _logo(Window::LoadLogo())
, _logoNoMargin(Window::LoadLogoNoMargin())
, _autoLockTimer([=] { checkAutoLock(); }) {
Expects(!_logo.isNull());
Expects(!_logoNoMargin.isNull());
Ui::Integration::Set(&_private->uiIntegration);
passcodeLockChanges(

View File

@@ -145,12 +145,6 @@ public:
bool hideMediaView();
[[nodiscard]] QPoint getPointForCallPanelCenter() const;
[[nodiscard]] QImage logo() const {
return _logo;
}
[[nodiscard]] QImage logoNoMargin() const {
return _logoNoMargin;
}
void startSettingsAndBackground();
[[nodiscard]] Settings &settings() {
@@ -354,9 +348,6 @@ private:
Media::Player::FloatDelegate *_defaultFloatPlayerDelegate = nullptr;
Media::Player::FloatDelegate *_replacementFloatPlayerDelegate = nullptr;
const QImage _logo;
const QImage _logoNoMargin;
rpl::variable<bool> _passcodeLock;
bool _screenIsLocked = false;

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 = 3001013;
constexpr auto AppVersionStr = "3.1.13";
constexpr auto AppBetaVersion = true;
constexpr auto AppVersion = 3002000;
constexpr auto AppVersionStr = "3.2";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "core/click_handler_types.h"
#include "window/window_session_controller.h"
#include "window/main_window.h" // Window::LogoNoMargin.
#include "ui/image/image.h"
#include "ui/empty_userpic.h"
#include "ui/text/text_options.h"
@@ -302,7 +303,7 @@ Image *PeerData::currentUserpic(
_userpicEmpty = nullptr;
} else if (isNotificationsUser()) {
static auto result = Image(
Core::App().logoNoMargin().scaledToWidth(
Window::LogoNoMargin().scaledToWidth(
kUserpicSize,
Qt::SmoothTransformation));
return &result;

View File

@@ -89,22 +89,27 @@ bool SponsoredMessages::append(not_null<History*> history) {
HistoryMessageMarkupData());
entryIt->item.reset(std::move(local));
// Since sponsored posts are only created on demand for display,
// we can send a request to view immediately.
view(entryIt);
return true;
}
bool SponsoredMessages::canHaveFor(not_null<History*> history) const {
return history->isChannel();
}
void SponsoredMessages::request(not_null<History*> history) {
if (!canHaveFor(history)) {
return;
}
auto &request = _requests[history];
if (request.requestId || TooEarlyForRequest(request.lastReceived)) {
return;
}
const auto channel = history->peer->asChannel();
Assert(channel != nullptr);
request.requestId = _session->api().request(
MTPchannels_GetSponsoredMessages(
_session->data().channel(history->channelId())->inputChannel
)).done([=](const MTPmessages_sponsoredMessages &result) {
channel->inputChannel)
).done([=](const MTPmessages_sponsoredMessages &result) {
parse(history, result);
}).fail([=](const MTP::Error &error) {
_requests.remove(history);
@@ -171,21 +176,44 @@ void SponsoredMessages::clearItems(not_null<History*> history) {
list.showedAll = false;
}
void SponsoredMessages::view(const std::vector<Entry>::iterator entryIt) {
const auto randomId = entryIt->sponsored.randomId;
const SponsoredMessages::Entry *SponsoredMessages::find(
const FullMsgId &fullId) const {
if (!fullId.channel) {
return nullptr;
}
const auto history = _session->data().history(
peerFromChannel(fullId.channel));
const auto it = _data.find(history);
if (it == end(_data)) {
return nullptr;
}
auto &list = it->second;
const auto entryIt = ranges::find_if(list.entries, [&](const Entry &e) {
return e.item->fullId() == fullId;
});
if (entryIt == end(list.entries)) {
return nullptr;
}
return &*entryIt;
}
void SponsoredMessages::view(const FullMsgId &fullId) {
const auto entryPtr = find(fullId);
if (!entryPtr) {
return;
}
const auto randomId = entryPtr->sponsored.randomId;
auto &request = _viewRequests[randomId];
if (request.requestId || TooEarlyForRequest(request.lastReceived)) {
return;
}
const auto history = entryIt->sponsored.history;
if (!history) {
return;
}
const auto channel = entryPtr->item->history()->peer->asChannel();
Assert(channel != nullptr);
request.requestId = _session->api().request(
MTPchannels_ViewSponsoredMessage(
_session->data().channel(history->channelId())->inputChannel,
MTP_bytes(randomId)
)).done([=] {
channel->inputChannel,
MTP_bytes(randomId))
).done([=] {
auto &request = _viewRequests[randomId];
request.lastReceived = crl::now();
request.requestId = 0;
@@ -195,20 +223,11 @@ void SponsoredMessages::view(const std::vector<Entry>::iterator entryIt) {
}
MsgId SponsoredMessages::channelPost(const FullMsgId &fullId) const {
const auto history = _session->data().history(
peerFromChannel(fullId.channel));
const auto it = _data.find(history);
if (it == end(_data)) {
const auto entryPtr = find(fullId);
if (!entryPtr) {
return ShowAtUnreadMsgId;
}
auto &list = it->second;
const auto entryIt = ranges::find_if(list.entries, [&](const Entry &e) {
return e.item->fullId() == fullId;
});
if (entryIt == end(list.entries)) {
return ShowAtUnreadMsgId;
}
const auto msgId = entryIt->sponsored.msgId;
const auto msgId = entryPtr->sponsored.msgId;
return msgId ? msgId : ShowAtUnreadMsgId;
}

View File

@@ -36,11 +36,14 @@ public:
SponsoredMessages &operator=(const SponsoredMessages &other) = delete;
~SponsoredMessages();
[[nodiscard]] bool canHaveFor(not_null<History*> history) const;
void request(not_null<History*> history);
[[nodiscard]] bool append(not_null<History*> history);
void clearItems(not_null<History*> history);
[[nodiscard]] MsgId channelPost(const FullMsgId &fullId) const;
void view(const FullMsgId &fullId);
private:
using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;
struct Entry {
@@ -65,7 +68,7 @@ private:
const MTPSponsoredMessage &message);
void clearOldRequests();
void view(const std::vector<Entry>::iterator entryIt);
const Entry *find(const FullMsgId &fullId) const;
const not_null<Main::Session*> _session;

View File

@@ -2674,10 +2674,6 @@ QString History::topPromotionMessage() const {
return _topPromotedMessage;
}
bool History::canHaveSponsoredMessages() const {
return isChannel();
}
bool History::clearUnreadOnClientSide() const {
if (!session().supportMode()) {
return false;

View File

@@ -282,8 +282,6 @@ public:
[[nodiscard]] bool topPromotionAboutShown() const;
void markTopPromotionAboutShown();
[[nodiscard]] bool canHaveSponsoredMessages() const;
MsgId minMsgId() const;
MsgId maxMsgId() const;
MsgId msgIdForRead() const;

View File

@@ -72,6 +72,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_histories.h"
#include "data/data_changes.h"
#include "data/stickers/data_stickers.h"
#include "data/data_sponsored_messages.h"
#include "facades.h"
#include "app.h"
#include "styles/style_chat.h"
@@ -678,15 +679,19 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
seltoy - mtop);
view->draw(p, context);
if (item->hasViews()) {
session().api().views().scheduleIncrement(item);
}
if (item->isUnreadMention() && !item->isUnreadMedia()) {
readMentions.insert(item);
_widget->enqueueMessageHighlight(view);
const auto height = view->height();
const auto middle = top + height / 2;
if (_visibleAreaBottom >= middle
&& _visibleAreaTop <= middle) {
if (item->hasViews()) {
session().api().views().scheduleIncrement(item);
}
if (item->isUnreadMention() && !item->isUnreadMedia()) {
readMentions.insert(item);
_widget->enqueueMessageHighlight(view);
}
}
const auto height = view->height();
top += height;
context.translate(0, -height);
p.translate(0, height);
@@ -735,6 +740,10 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
if (!item->out() && item->unread()) {
readTill = item;
}
if (item->isSponsored()) {
session().data().sponsoredMessages().view(
item->fullId());
}
}
if (_visibleAreaBottom >= middle
&& _visibleAreaTop <= middle) {

View File

@@ -2189,11 +2189,9 @@ void HistoryWidget::showHistory(
showAboutTopPromotion();
{
const auto hasSponsored = _history->canHaveSponsoredMessages();
_scroll->setTrackingContent(hasSponsored);
if (hasSponsored) {
session().data().sponsoredMessages().request(_history);
}
auto &sponsored = session().data().sponsoredMessages();
sponsored.request(_history);
_scroll->setTrackingContent(sponsored.canHaveFor(_history));
}
} else {
_chooseForReport = nullptr;

View File

@@ -265,6 +265,7 @@ public:
void confirmDeleteSelected();
void clearSelected();
[[nodiscard]] SendMenu::Type sendMenuType() const;
bool sendExistingDocument(
not_null<DocumentData*> document,
Api::SendOptions options);
@@ -374,7 +375,6 @@ private:
void sendWithModifiers(Qt::KeyboardModifiers modifiers);
void sendSilent();
void sendScheduled();
[[nodiscard]] SendMenu::Type sendMenuType() const;
[[nodiscard]] SendMenu::Type sendButtonMenuType() const;
void handlePendingHistoryUpdate();
void fullInfoUpdated();

View File

@@ -1067,8 +1067,18 @@ void MainWidget::inlineResultLoadFailed(FileLoader *loader, bool started) {
//Ui::repaintInlineItem();
}
SendMenu::Type MainWidget::sendMenuType() const {
return _history->sendMenuType();
}
bool MainWidget::sendExistingDocument(not_null<DocumentData*> document) {
return _history->sendExistingDocument(document, Api::SendOptions());
return sendExistingDocument(document, Api::SendOptions());
}
bool MainWidget::sendExistingDocument(
not_null<DocumentData*> document,
Api::SendOptions options) {
return _history->sendExistingDocument(document, options);
}
void MainWidget::dialogsCancelled() {

View File

@@ -29,8 +29,13 @@ class Error;
namespace Api {
struct SendAction;
struct SendOptions;
} // namespace Api
namespace SendMenu {
enum class Type;
} // namespace SendMenu
namespace Main {
class Session;
} // namespace Main
@@ -150,7 +155,11 @@ public:
QPixmap grabForShowAnimation(const Window::SectionSlideParams &params);
void checkMainSectionToLayer();
bool sendExistingDocument(not_null<DocumentData*> sticker);
[[nodiscard]] SendMenu::Type sendMenuType() const;
bool sendExistingDocument(not_null<DocumentData*> document);
bool sendExistingDocument(
not_null<DocumentData*> document,
Api::SendOptions options);
bool isActive() const;
[[nodiscard]] bool doWeMarkAsRead() const;

View File

@@ -81,17 +81,6 @@ void FeedLangTestingKey(int key) {
MainWindow::MainWindow(not_null<Window::Controller*> controller)
: Platform::MainWindow(controller) {
auto logo = Core::App().logo();
icon16 = logo.scaledToWidth(16, Qt::SmoothTransformation);
icon32 = logo.scaledToWidth(32, Qt::SmoothTransformation);
icon64 = logo.scaledToWidth(64, Qt::SmoothTransformation);
auto logoNoMargin = Core::App().logoNoMargin();
iconbig16 = logoNoMargin.scaledToWidth(16, Qt::SmoothTransformation);
iconbig32 = logoNoMargin.scaledToWidth(32, Qt::SmoothTransformation);
iconbig64 = logoNoMargin.scaledToWidth(64, Qt::SmoothTransformation);
resize(st::windowDefaultWidth, st::windowDefaultHeight);
setLocale(QLocale(QLocale::English, QLocale::UnitedStates));
@@ -842,122 +831,6 @@ void MainWindow::updateControlsGeometry() {
if (_main) _main->checkMainSectionToLayer();
}
void MainWindow::placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) {
QPainter p(&img);
QString cnt = (count < 100) ? QString("%1").arg(count) : QString("..%1").arg(count % 10, 1, 10, QChar('0'));
int32 cntSize = cnt.size();
p.setBrush(bg->b);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::Antialiasing);
int32 fontSize;
if (size == 16) {
fontSize = 8;
} else if (size == 32) {
fontSize = (cntSize < 2) ? 12 : 12;
} else {
fontSize = (cntSize < 2) ? 22 : 22;
}
style::font f = { fontSize, 0, 0 };
int32 w = f->width(cnt), d, r;
if (size == 16) {
d = (cntSize < 2) ? 2 : 1;
r = (cntSize < 2) ? 4 : 3;
} else if (size == 32) {
d = (cntSize < 2) ? 5 : 2;
r = (cntSize < 2) ? 8 : 7;
} else {
d = (cntSize < 2) ? 9 : 4;
r = (cntSize < 2) ? 16 : 14;
}
p.drawRoundedRect(QRect(shift.x() + size - w - d * 2, shift.y() + size - f->height, w + d * 2, f->height), r, r);
p.setFont(f->f);
p.setPen(color->p);
p.drawText(shift.x() + size - w - d, shift.y() + size - f->height + f->ascent, cnt);
}
QImage MainWindow::iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) {
bool layer = false;
if (size < 0) {
size = -size;
layer = true;
}
if (layer) {
if (size != 16 && size != 20 && size != 24) size = 32;
// platform/linux/main_window_linux depends on count used the same
// way for all the same (count % 1000) values.
QString cnt = (count < 1000) ? QString("%1").arg(count) : QString("..%1").arg(count % 100, 2, 10, QChar('0'));
QImage result(size, size, QImage::Format_ARGB32);
int32 cntSize = cnt.size();
result.fill(Qt::transparent);
{
QPainter p(&result);
p.setBrush(bg);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::Antialiasing);
int32 fontSize;
if (size == 16) {
fontSize = (cntSize < 2) ? 11 : ((cntSize < 3) ? 11 : 8);
} else if (size == 20) {
fontSize = (cntSize < 2) ? 14 : ((cntSize < 3) ? 13 : 10);
} else if (size == 24) {
fontSize = (cntSize < 2) ? 17 : ((cntSize < 3) ? 16 : 12);
} else {
fontSize = (cntSize < 2) ? 22 : ((cntSize < 3) ? 20 : 16);
}
style::font f = { fontSize, 0, 0 };
int32 w = f->width(cnt), d, r;
if (size == 16) {
d = (cntSize < 2) ? 5 : ((cntSize < 3) ? 2 : 1);
r = (cntSize < 2) ? 8 : ((cntSize < 3) ? 7 : 3);
} else if (size == 20) {
d = (cntSize < 2) ? 6 : ((cntSize < 3) ? 2 : 1);
r = (cntSize < 2) ? 10 : ((cntSize < 3) ? 9 : 5);
} else if (size == 24) {
d = (cntSize < 2) ? 7 : ((cntSize < 3) ? 3 : 1);
r = (cntSize < 2) ? 12 : ((cntSize < 3) ? 11 : 6);
} else {
d = (cntSize < 2) ? 9 : ((cntSize < 3) ? 4 : 2);
r = (cntSize < 2) ? 16 : ((cntSize < 3) ? 14 : 8);
}
p.drawRoundedRect(QRect(size - w - d * 2, size - f->height, w + d * 2, f->height), r, r);
p.setFont(f);
p.setPen(fg);
p.drawText(size - w - d, size - f->height + f->ascent, cnt);
}
return result;
} else {
if (size != 16 && size != 32) size = 64;
}
QImage img(smallIcon ? ((size == 16) ? iconbig16 : (size == 32 ? iconbig32 : iconbig64)) : ((size == 16) ? icon16 : (size == 32 ? icon32 : icon64)));
if (const auto controller = sessionController()) {
if (controller->session().supportMode()) {
Window::ConvertIconToBlack(img);
}
}
if (!count) return img;
if (smallIcon) {
placeSmallCounter(img, size, count, bg, QPoint(), fg);
} else {
QPainter p(&img);
p.drawPixmap(
size / 2,
size / 2,
Ui::PixmapFromImage(
iconWithCounter(-size / 2, count, bg, fg, false)));
}
return img;
}
void MainWindow::sendPaths() {
if (controller().locked()) {
return;

View File

@@ -71,9 +71,6 @@ public:
void sendPaths();
QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) override;
void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) override;
bool contentOverlapped(const QRect &globalRect);
bool contentOverlapped(QWidget *w, QPaintEvent *e) {
return contentOverlapped(QRect(w->mapToGlobal(e->rect().topLeft()), e->rect().size()));
@@ -148,8 +145,6 @@ private:
std::unique_ptr<Media::SystemMediaControlsManager> _mediaControlsManager;
QImage icon16, icon32, icon64, iconbig16, iconbig32, iconbig64;
crl::time _lastTrayClickTime = 0;
QPoint _lastMousePosition;
bool _activeForTrayIconAction = true;

View File

@@ -305,7 +305,7 @@ QIcon TrayIconGen(int counter, bool muted) {
}
}
} else {
currentImageBack = Core::App().logo();
currentImageBack = Window::Logo();
}
if (dprSize(currentImageBack) != desiredSize) {
@@ -324,20 +324,19 @@ QIcon TrayIconGen(int counter, bool muted) {
: st::trayCounterBg;
const auto &fg = st::trayCounterFg;
if (iconSize >= 22) {
auto layerSize = -16;
if (iconSize >= 48) {
layerSize = -32;
} else if (iconSize >= 36) {
layerSize = -24;
} else if (iconSize >= 32) {
layerSize = -20;
}
const auto layer = App::wnd()->iconWithCounter(
layerSize,
counter,
bg,
fg,
false);
const auto layerSize = (iconSize >= 48)
? 32
: (iconSize >= 36)
? 24
: (iconSize >= 32)
? 20
: 16;
const auto layer = Window::GenerateCounterLayer({
.size = layerSize,
.count = counter,
.bg = bg,
.fg = fg,
});
QPainter p(&iconImage);
p.drawImage(
@@ -345,13 +344,12 @@ QIcon TrayIconGen(int counter, bool muted) {
iconImage.height() - layer.height() - 1,
layer);
} else {
App::wnd()->placeSmallCounter(
iconImage,
16,
counter,
bg,
QPoint(),
fg);
iconImage = Window::WithSmallCounter(std::move(iconImage), {
.size = 16,
.count = counter,
.bg = bg,
.fg = fg,
});
}
}

View File

@@ -20,13 +20,6 @@ class MainWindow : public Window::MainWindow {
public:
explicit MainWindow(not_null<Window::Controller*> controller);
virtual QImage iconWithCounter(
int size,
int count,
style::color bg,
style::color fg,
bool smallIcon) = 0;
void psShowTrayMenu();
bool trayAvailable() {
@@ -54,14 +47,6 @@ protected:
void psTrayMenuUpdated();
void psSetupTrayIcon();
virtual void placeSmallCounter(
QImage &img,
int size,
int count,
style::color bg,
const QPoint &shift,
style::color color) = 0;
private:
class Private;
friend class Private;

View File

@@ -25,8 +25,6 @@ public:
bool psFilterNativeEvent(void *event);
virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0;
int getCustomTitleHeight() const {
return _customTitleHeight;
}
@@ -77,7 +75,6 @@ protected:
void psTrayMenuUpdated();
void psSetupTrayIcon();
virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0;
void closeWithoutDestroy() override;
void createGlobalMenu() override;

View File

@@ -13,7 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/win/windows_dlls.h"
#include "platform/win/windows_event_filter.h"
#include "window/notifications_manager.h"
#include "window/window_session_controller.h"
#include "mainwindow.h"
#include "main/main_session.h"
#include "base/crc32hash.h"
#include "base/platform/win/base_windows_wrl.h"
#include "base/platform/base_platform_info.h"
@@ -59,47 +61,77 @@ constexpr auto kKeepActiveForTrayIcon = crl::time(500);
using namespace Microsoft::WRL;
HICON createHIconFromQIcon(const QIcon &icon, int xSize, int ySize) {
[[nodiscard]] HICON NativeIcon(const QIcon &icon, QSize size) {
if (!icon.isNull()) {
const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize)));
if (!pm.isNull()) {
return qt_pixmapToWinHICON(pm);
const auto pixmap = icon.pixmap(icon.actualSize(size));
if (!pixmap.isNull()) {
return qt_pixmapToWinHICON(pixmap);
}
}
return nullptr;
}
HWND createTaskbarHider() {
HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0);
HWND hWnd = 0;
[[nodiscard]] QImage IconWithCounter(
Window::CounterLayerArgs &&args,
Main::Session *session,
bool smallIcon) {
static constexpr auto kCount = 3;
static auto ScaledLogo = std::array<QImage, kCount>();
static auto ScaledLogoNoMargin = std::array<QImage, kCount>();
QString cn = QString("TelegramTaskbarHider");
LPCWSTR _cn = (LPCWSTR)cn.utf16();
WNDCLASSEX wc;
struct Dimensions {
int index = 0;
int size = 0;
};
const auto d = [&]() -> Dimensions {
switch (args.size) {
case 16:
return {
.index = 0,
.size = 16,
};
case 32:
return {
.index = 1,
.size = 32,
};
default:
return {
.index = 2,
.size = 64,
};
}
}();
Assert(d.index < kCount);
wc.cbSize = sizeof(wc);
wc.style = 0;
wc.lpfnWndProc = DefWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = appinst;
wc.hIcon = 0;
wc.hCursor = 0;
wc.hbrBackground = 0;
wc.lpszMenuName = NULL;
wc.lpszClassName = _cn;
wc.hIconSm = 0;
if (!RegisterClassEx(&wc)) {
DEBUG_LOG(("Application Error: could not register taskbar hider window class, error: %1").arg(GetLastError()));
return hWnd;
auto &scaled = smallIcon ? ScaledLogoNoMargin : ScaledLogo;
auto result = [&] {
auto &image = scaled[d.index];
if (image.isNull()) {
image = (smallIcon
? Window::LogoNoMargin()
: Window::Logo()).scaledToWidth(
d.size,
Qt::SmoothTransformation);
}
return image;
}();
if (session && session->supportMode()) {
Window::ConvertIconToBlack(result);
}
hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, _cn, 0, WS_POPUP, 0, 0, 0, 0, 0, 0, appinst, 0);
if (!hWnd) {
DEBUG_LOG(("Application Error: could not create taskbar hider window class, error: %1").arg(GetLastError()));
return hWnd;
if (!args.count) {
return result;
} else if (smallIcon) {
return Window::WithSmallCounter(std::move(result), std::move(args));
}
return hWnd;
QPainter p(&result);
const auto half = d.size / 2;
args.size = half;
p.drawPixmap(
half,
half,
Ui::PixmapFromImage(Window::GenerateCounterLayer(std::move(args))));
return result;
}
ComPtr<ITaskbarList3> taskbarList;
@@ -202,7 +234,8 @@ void MainWindow::psSetupTrayIcon() {
if (!trayIcon) {
trayIcon = new QSystemTrayIcon(this);
auto icon = QIcon(Ui::PixmapFromImage(Core::App().logoNoMargin()));
const auto icon = QIcon(Ui::PixmapFromImage(
QImage(Window::LogoNoMargin())));
trayIcon->setIcon(icon);
connect(
@@ -327,46 +360,70 @@ void MainWindow::unreadCounterChangedHook() {
void MainWindow::updateIconCounters() {
const auto counter = Core::App().unreadBadge();
const auto muted = Core::App().unreadBadgeMuted();
const auto controller = sessionController();
const auto session = controller ? &controller->session() : nullptr;
auto iconSizeSmall = QSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
auto iconSizeBig = QSize(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
const auto iconSizeSmall = QSize(
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON));
const auto iconSizeBig = QSize(
GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON));
auto &bg = (muted ? st::trayCounterBgMute : st::trayCounterBg);
auto &fg = st::trayCounterFg;
auto iconSmallPixmap16 = Ui::PixmapFromImage(
iconWithCounter(16, counter, bg, fg, true));
auto iconSmallPixmap32 = Ui::PixmapFromImage(
iconWithCounter(32, counter, bg, fg, true));
const auto &bg = muted ? st::trayCounterBgMute : st::trayCounterBg;
const auto &fg = st::trayCounterFg;
const auto counterArgs = [&](int size, int counter) {
return Window::CounterLayerArgs{
.size = size,
.count = counter,
.bg = bg,
.fg = fg,
};
};
const auto iconWithCounter = [&](int size, int counter, bool smallIcon) {
return Ui::PixmapFromImage(IconWithCounter(
counterArgs(size, counter),
session,
smallIcon));
};
auto iconSmallPixmap16 = iconWithCounter(16, counter, true);
auto iconSmallPixmap32 = iconWithCounter(32, counter, true);
QIcon iconSmall, iconBig;
iconSmall.addPixmap(iconSmallPixmap16);
iconSmall.addPixmap(iconSmallPixmap32);
iconBig.addPixmap(Ui::PixmapFromImage(
iconWithCounter(32, taskbarList.Get() ? 0 : counter, bg, fg, false)));
iconBig.addPixmap(Ui::PixmapFromImage(
iconWithCounter(64, taskbarList.Get() ? 0 : counter, bg, fg, false)));
const auto bigCounter = taskbarList.Get() ? 0 : counter;
iconBig.addPixmap(iconWithCounter(32, bigCounter, false));
iconBig.addPixmap(iconWithCounter(64, bigCounter, false));
if (trayIcon) {
// Force Qt to use right icon size, not the larger one.
QIcon forTrayIcon;
forTrayIcon.addPixmap(iconSizeSmall.width() >= 20 ? iconSmallPixmap32 : iconSmallPixmap16);
forTrayIcon.addPixmap(iconSizeSmall.width() >= 20
? iconSmallPixmap32
: iconSmallPixmap16);
trayIcon->setIcon(forTrayIcon);
}
psDestroyIcons();
ps_iconSmall = createHIconFromQIcon(iconSmall, iconSizeSmall.width(), iconSizeSmall.height());
ps_iconBig = createHIconFromQIcon(iconBig, iconSizeBig.width(), iconSizeBig.height());
SendMessage(ps_hWnd, WM_SETICON, 0, (LPARAM)ps_iconSmall);
SendMessage(ps_hWnd, WM_SETICON, 1, (LPARAM)(ps_iconBig ? ps_iconBig : ps_iconSmall));
if (taskbarList.Get()) {
ps_iconSmall = NativeIcon(iconSmall, iconSizeSmall);
ps_iconBig = NativeIcon(iconBig, iconSizeBig);
SendMessage(ps_hWnd, WM_SETICON, ICON_SMALL, (LPARAM)ps_iconSmall);
SendMessage(ps_hWnd, WM_SETICON, ICON_BIG, (LPARAM)(ps_iconBig ? ps_iconBig : ps_iconSmall));
if (taskbarList) {
if (counter > 0) {
const auto pixmap = [&](int size) {
return Ui::PixmapFromImage(Window::GenerateCounterLayer(
counterArgs(size, counter)));
};
QIcon iconOverlay;
iconOverlay.addPixmap(Ui::PixmapFromImage(
iconWithCounter(-16, counter, bg, fg, false)));
iconOverlay.addPixmap(Ui::PixmapFromImage(
iconWithCounter(-32, counter, bg, fg, false)));
ps_iconOverlay = createHIconFromQIcon(iconOverlay, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
iconOverlay.addPixmap(pixmap(16));
iconOverlay.addPixmap(pixmap(32));
ps_iconOverlay = NativeIcon(iconOverlay, iconSizeSmall);
}
auto description = (counter > 0) ? tr::lng_unread_bar(tr::now, lt_count, counter) : QString();
taskbarList->SetOverlayIcon(ps_hWnd, ps_iconOverlay, description.toStdWString().c_str());
const auto description = (counter > 0)
? tr::lng_unread_bar(tr::now, lt_count, counter).toStdWString()
: std::wstring();
taskbarList->SetOverlayIcon(ps_hWnd, ps_iconOverlay, description.c_str());
}
SetWindowPos(ps_hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}

View File

@@ -30,8 +30,6 @@ public:
void psRefreshTaskbarIcon();
virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0;
[[nodiscard]] static uint32 TaskbarCreatedMsgId();
static void TaskbarCreated();
@@ -59,7 +57,6 @@ protected:
void psTrayMenuUpdated();
void psSetupTrayIcon();
virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0;
void showTrayTooltip() override;

View File

@@ -258,7 +258,7 @@ void NotificationsCount::prepareNotificationSampleSmall() {
void NotificationsCount::prepareNotificationSampleUserpic() {
if (_notificationSampleUserpic.isNull()) {
_notificationSampleUserpic = Ui::PixmapFromImage(
Core::App().logoNoMargin().scaled(
Window::LogoNoMargin().scaled(
st::notifyPhotoSize * cIntRetinaFactor(),
st::notifyPhotoSize * cIntRetinaFactor(),
Qt::IgnoreAspectRatio,

View File

@@ -57,6 +57,7 @@ void EditInviteLinkBox(
const auto link = data.link;
const auto isGroup = data.isGroup;
const auto isPublic = data.isPublic;
box->setTitle(link.isEmpty()
? tr::lng_group_invite_new_title()
: tr::lng_group_invite_edit_title());
@@ -64,14 +65,14 @@ void EditInviteLinkBox(
const auto container = box->verticalLayout();
const auto addTitle = [&](
not_null<VerticalLayout*> container,
rpl::producer<QString> text,
style::margins margins = style::margins()) {
rpl::producer<QString> text) {
container->add(
object_ptr<FlatLabel>(
container,
std::move(text),
st::settingsSubsectionTitle),
st::settingsSubsectionTitlePadding + margins);
(st::settingsSubsectionTitlePadding
+ style::margins(0, st::settingsSectionSkip, 0, 0)));
};
const auto addDivider = [&](
not_null<VerticalLayout*> container,
@@ -105,25 +106,43 @@ void EditInviteLinkBox(
const auto state = box->lifetime().make_state<State>(State{
.expireValue = expire,
.usageValue = usage,
.requestApproval = data.requestApproval,
.requestApproval = (data.requestApproval && !isPublic),
});
const auto requestApproval = container->add(
object_ptr<SettingsButton>(
const auto requestApproval = isPublic
? nullptr
: container->add(
object_ptr<SettingsButton>(
container,
tr::lng_group_invite_request_approve(),
st::settingsButton),
style::margins{ 0, 0, 0, st::settingsSectionSkip });
if (requestApproval) {
requestApproval->toggleOn(state->requestApproval.value());
state->requestApproval = requestApproval->toggledValue();
addDivider(container, rpl::conditional(
state->requestApproval.value(),
(isGroup
? tr::lng_group_invite_about_approve()
: tr::lng_group_invite_about_approve_channel()),
(isGroup
? tr::lng_group_invite_about_no_approve()
: tr::lng_group_invite_about_no_approve_channel())));
}
const auto labelField = container->add(
object_ptr<Ui::InputField>(
container,
tr::lng_group_invite_request_approve(),
st::settingsButton),
style::margins{ 0, 0, 0, st::settingsSectionSkip });
requestApproval->toggleOn(state->requestApproval.value());
state->requestApproval = requestApproval->toggledValue();
addDivider(container, rpl::conditional(
state->requestApproval.value(),
(isGroup
? tr::lng_group_invite_about_approve()
: tr::lng_group_invite_about_approve_channel()),
(isGroup
? tr::lng_group_invite_about_no_approve()
: tr::lng_group_invite_about_no_approve_channel())));
st::defaultInputField,
tr::lng_group_invite_label_header(),
data.label),
style::margins(
st::settingsSubsectionTitlePadding.left(),
st::settingsSectionSkip,
st::settingsSubsectionTitlePadding.right(),
st::settingsSectionSkip * 2));
labelField->setMaxLength(kMaxLabelLength);
addDivider(container, tr::lng_group_invite_label_about());
addTitle(container, tr::lng_group_invite_expire_title());
const auto expiresWrap = container->add(
@@ -138,10 +157,7 @@ void EditInviteLinkBox(
container,
object_ptr<VerticalLayout>(container)));
const auto usagesInner = usagesSlide->entity();
addTitle(
usagesInner,
tr::lng_group_invite_usage_title(),
style::margins(0, st::settingsSectionSkip, 0, 0));
addTitle(usagesInner, tr::lng_group_invite_usage_title());
const auto usagesWrap = usagesInner->add(
object_ptr<VerticalLayout>(usagesInner),
style::margins(0, 0, 0, st::settingsSectionSkip));
@@ -297,20 +313,6 @@ void EditInviteLinkBox(
regenerate();
const auto labelField = container->add(
object_ptr<Ui::InputField>(
container,
st::defaultInputField,
tr::lng_group_invite_label_header(),
data.label),
style::margins(
st::settingsSubsectionTitlePadding.left(),
st::settingsSectionSkip,
st::settingsSubsectionTitlePadding.right(),
st::settingsSectionSkip * 2));
labelField->setMaxLength(kMaxLabelLength);
addDivider(container, tr::lng_group_invite_label_about());
usagesSlide->toggleOn(state->requestApproval.value() | rpl::map(!_1));
usagesSlide->finishAnimating();
@@ -334,6 +336,7 @@ void EditInviteLinkBox(
.usageLimit = usageLimit,
.requestApproval = state->requestApproval.current(),
.isGroup = isGroup,
.isPublic = isPublic,
});
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
@@ -342,10 +345,11 @@ void EditInviteLinkBox(
void CreateInviteLinkBox(
not_null<GenericBox*> box,
bool isGroup,
bool isPublic,
Fn<void(InviteLinkFields)> done) {
EditInviteLinkBox(
box,
InviteLinkFields{ .isGroup = isGroup },
InviteLinkFields{ .isGroup = isGroup, .isPublic = isPublic },
std::move(done));
}

View File

@@ -18,6 +18,7 @@ struct InviteLinkFields {
int usageLimit = 0;
bool requestApproval = false;
bool isGroup = false;
bool isPublic = false;
};
void EditInviteLinkBox(
@@ -28,6 +29,7 @@ void EditInviteLinkBox(
void CreateInviteLinkBox(
not_null<Ui::GenericBox*> box,
bool isGroup,
bool isPublic,
Fn<void(InviteLinkFields)> done);
} // namespace Ui

View File

@@ -49,12 +49,14 @@ constexpr auto kSaveWindowPositionTimeout = crl::time(1000);
} // namespace
QImage LoadLogo() {
return QImage(qsl(":/gui/art/logo_256.png"));
const QImage &Logo() {
static const auto result = QImage(u":/gui/art/logo_256.png"_q);
return result;
}
QImage LoadLogoNoMargin() {
return QImage(qsl(":/gui/art/logo_256_no_margin.png"));
const QImage &LogoNoMargin() {
static const auto result = QImage(u":/gui/art/logo_256_no_margin.png"_q);
return result;
}
void ConvertIconToBlack(QImage &image) {
@@ -105,7 +107,7 @@ void ConvertIconToBlack(QImage &image) {
}
QIcon CreateOfficialIcon(Main::Session *session) {
auto image = Core::IsAppLaunched() ? Core::App().logo() : LoadLogo();
auto image = Logo();
if (session && session->supportMode()) {
ConvertIconToBlack(image);
}
@@ -156,6 +158,144 @@ QIcon CreateIcon(Main::Session *session) {
return result;
}
QImage GenerateCounterLayer(CounterLayerArgs &&args) {
// platform/linux/main_window_linux depends on count used the same
// way for all the same (count % 1000) values.
const auto count = args.count.value();
const auto text = (count < 1000)
? QString::number(count)
: u"..%1"_q.arg(count % 100, 2, 10, QChar('0'));
const auto textSize = text.size();
struct Dimensions {
int size = 0;
int font = 0;
int delta = 0;
int radius = 0;
};
const auto d = [&]() -> Dimensions {
switch (args.size.value()) {
case 16:
return {
.size = 16,
.font = ((textSize < 2) ? 11 : (textSize < 3) ? 11 : 8),
.delta = ((textSize < 2) ? 5 : (textSize < 3) ? 2 : 1),
.radius = ((textSize < 2) ? 8 : (textSize < 3) ? 7 : 3),
};
case 20:
return {
.size = 20,
.font = ((textSize < 2) ? 14 : (textSize < 3) ? 13 : 10),
.delta = ((textSize < 2) ? 6 : (textSize < 3) ? 2 : 1),
.radius = ((textSize < 2) ? 10 : (textSize < 3) ? 9 : 5),
};
case 24:
return {
.size = 24,
.font = ((textSize < 2) ? 17 : (textSize < 3) ? 16 : 12),
.delta = ((textSize < 2) ? 7 : (textSize < 3) ? 3 : 1),
.radius = ((textSize < 2) ? 12 : (textSize < 3) ? 11 : 6),
};
default:
return {
.size = 32,
.font = ((textSize < 2) ? 22 : (textSize < 3) ? 20 : 16),
.delta = ((textSize < 2) ? 9 : (textSize < 3) ? 4 : 2),
.radius = ((textSize < 2) ? 16 : (textSize < 3) ? 14 : 8),
};
}
}();
auto result = QImage(d.size, d.size, QImage::Format_ARGB32);
result.fill(Qt::transparent);
auto p = QPainter(&result);
auto hq = PainterHighQualityEnabler(p);
const auto f = style::font{ d.font, 0, 0 };
const auto w = f->width(text);
p.setBrush(args.bg.value());
p.setPen(Qt::NoPen);
p.drawRoundedRect(
QRect(
d.size - w - d.delta * 2,
d.size - f->height,
w + d.delta * 2,
f->height),
d.radius,
d.radius);
p.setFont(f);
p.setPen(args.fg.value());
p.drawText(d.size - w - d.delta, d.size - f->height + f->ascent, text);
p.end();
return result;
}
QImage WithSmallCounter(QImage image, CounterLayerArgs &&args) {
const auto count = args.count.value();
const auto text = (count < 100)
? QString::number(count)
: QString("..%1").arg(count % 10, 1, 10, QChar('0'));
const auto textSize = text.size();
struct Dimensions {
int size = 0;
int font = 0;
int delta = 0;
int radius = 0;
};
const auto d = [&]() -> Dimensions {
switch (args.size.value()) {
case 16:
return {
.size = 16,
.font = 8,
.delta = ((textSize < 2) ? 2 : 1),
.radius = ((textSize < 2) ? 4 : 3),
};
case 32:
return {
.size = 32,
.font = 12,
.delta = ((textSize < 2) ? 5 : 2),
.radius = ((textSize < 2) ? 8 : 7),
};
default:
return {
.size = 64,
.font = 22,
.delta = ((textSize < 2) ? 9 : 4),
.radius = ((textSize < 2) ? 16 : 14),
};
}
}();
auto p = QPainter(&image);
auto hq = PainterHighQualityEnabler(p);
const auto f = style::font{ d.font, 0, 0 };
const auto w = f->width(text);
p.setBrush(args.bg.value());
p.setPen(Qt::NoPen);
p.drawRoundedRect(
QRect(
d.size - w - d.delta * 2,
d.size - f->height,
w + d.delta * 2,
f->height),
d.radius,
d.radius);
p.setFont(f);
p.setPen(args.fg.value());
p.drawText(d.size - w - d.delta, d.size - f->height + f->ascent, text);
p.end();
return image;
}
MainWindow::MainWindow(not_null<Controller*> controller)
: _controller(controller)
, _positionUpdatedTimer([=] { savePosition(); })

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h"
#include "base/object_ptr.h"
#include "core/core_settings.h"
#include "base/required.h"
#include <QtWidgets/QSystemTrayIcon>
@@ -35,11 +36,21 @@ class SessionController;
class TitleWidget;
struct TermsLock;
QImage LoadLogo();
QImage LoadLogoNoMargin();
QIcon CreateIcon(Main::Session *session = nullptr);
[[nodiscard]] const QImage &Logo();
[[nodiscard]] const QImage &LogoNoMargin();
[[nodiscard]] QIcon CreateIcon(Main::Session *session = nullptr);
void ConvertIconToBlack(QImage &image);
struct CounterLayerArgs {
base::required<int> size = 16;
base::required<int> count = 1;
base::required<style::color> bg;
base::required<style::color> fg;
};
[[nodiscard]] QImage GenerateCounterLayer(CounterLayerArgs &&args);
[[nodiscard]] QImage WithSmallCounter(QImage image, CounterLayerArgs &&args);
class MainWindow : public Ui::RpWindow {
public:
explicit MainWindow(not_null<Controller*> controller);

View File

@@ -87,7 +87,7 @@ Manager::QueuedNotification::QueuedNotification(
QPixmap Manager::hiddenUserpicPlaceholder() const {
if (_hiddenUserpicPlaceholder.isNull()) {
_hiddenUserpicPlaceholder = Ui::PixmapFromImage(
Core::App().logoNoMargin().scaled(
LogoNoMargin().scaled(
st::notifyPhotoSize,
st::notifyPhotoSize,
Qt::IgnoreAspectRatio,

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "window/notifications_utilities.h"
#include "window/main_window.h"
#include "base/platform/base_platform_file_utilities.h"
#include "base/random.h"
#include "core/application.h"
@@ -79,7 +80,7 @@ QString CachedUserpics::get(
peer->saveUserpic(view, v.path, st::notifyMacPhotoSize);
}
} else {
Core::App().logoNoMargin().save(v.path, "PNG");
LogoNoMargin().save(v.path, "PNG");
}
i = _images.insert(key, v);
_someSavedFlag = true;

View File

@@ -1,7 +1,7 @@
AppVersion 3001013
AppVersionStrMajor 3.1
AppVersionStrSmall 3.1.13
AppVersionStr 3.1.13
BetaChannel 1
AppVersion 3002000
AppVersionStrMajor 3.2
AppVersionStrSmall 3.2
AppVersionStr 3.2.0
BetaChannel 0
AlphaVersion 0
AppVersionOriginal 3.1.13.beta
AppVersionOriginal 3.2

View File

@@ -1,3 +1,10 @@
3.2 (03.11.21)
- Create special invite links that require admins to approve users before they become members.
- Admins can view the applicants' profiles and bios by tapping the Join Requests bar at the top of the chat.
- Add internal labels to your chat's Invite Links to keep them organized.
- More Interactive Emoji - :ghost:, :dislike:, :face_vomiting:, :joy:, :money_with_wings: or :jack_o_lantern:
3.1.13 beta (02.11.21)
- Fix requests to groups / channels processing.