diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index a034d7a130..9a2a48ea9e 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1814,10 +1814,23 @@ PRIVATE settings.cpp settings.h stdafx.h + tray_accounts_menu.h tray.cpp tray.h ) +if (APPLE) + nice_target_sources(Telegram ${src_loc} + PRIVATE + tray_accounts_menu.cpp + ) +else() + nice_target_sources(Telegram ${src_loc} + PRIVATE + tray_accounts_menu_dummy.cpp + ) +endif() + if (NOT build_winstore) remove_target_sources(Telegram ${src_loc} platform/win/windows_start_task.cpp diff --git a/Telegram/SourceFiles/platform/mac/tray_mac.h b/Telegram/SourceFiles/platform/mac/tray_mac.h index 6667d3cb12..1e472ce3b1 100644 --- a/Telegram/SourceFiles/platform/mac/tray_mac.h +++ b/Telegram/SourceFiles/platform/mac/tray_mac.h @@ -12,6 +12,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unique_qptr.h" class QMenu; +class QIcon; + +namespace Ui { +class DynamicImage; +} // namespace Ui namespace Platform { @@ -38,6 +43,16 @@ public: void destroyMenu(); void addAction(rpl::producer text, Fn &&callback); + void addAction( + rpl::producer text, + Fn &&callback, + const QIcon &icon); + void addAction( + rpl::producer text, + Fn &&callback, + std::shared_ptr icon, + int size); + void addSeparator(); void showTrayMessage() const; [[nodiscard]] bool hasTrayMessageSupport() const; diff --git a/Telegram/SourceFiles/platform/mac/tray_mac.mm b/Telegram/SourceFiles/platform/mac/tray_mac.mm index a900ec444c..6f2d565826 100644 --- a/Telegram/SourceFiles/platform/mac/tray_mac.mm +++ b/Telegram/SourceFiles/platform/mac/tray_mac.mm @@ -13,9 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_controller.h" #include "window/window_session_controller.h" #include "ui/painter.h" +#include "ui/dynamic_image.h" #include "styles/style_window.h" #include +#include #import #import @@ -387,11 +389,19 @@ void Tray::destroyMenu() { } void Tray::addAction(rpl::producer text, Fn &&callback) { + addAction(std::move(text), std::move(callback), QIcon()); +} + +void Tray::addAction( + rpl::producer text, + Fn &&callback, + const QIcon &icon) { if (!_menu) { return; } const auto action = _menu->addAction(QString(), std::move(callback)); + action->setIcon(icon); std::move( text ) | rpl::on_next([=](const QString &text) { @@ -399,6 +409,43 @@ void Tray::addAction(rpl::producer text, Fn &&callback) { }, _actionsLifetime); } +void Tray::addAction( + rpl::producer text, + Fn &&callback, + std::shared_ptr icon, + int size) { + if (!_menu) { + return; + } + + const auto action = _menu->addAction(QString(), std::move(callback)); + if (icon) { + const auto updateIcon = crl::guard(action, [=] { + action->setIcon(QIcon(QPixmap::fromImage(icon->image(size)))); + }); + icon->subscribeToUpdates([=] { + Core::Sandbox::Instance().customEnterFromEventLoop([=] { + updateIcon(); + }); + }); + updateIcon(); + _actionsLifetime.add([icon = std::move(icon)] { + icon->subscribeToUpdates(nullptr); + }); + } + std::move( + text + ) | rpl::on_next([=](const QString &text) { + action->setText(text); + }, _actionsLifetime); +} + +void Tray::addSeparator() { + if (_menu) { + _menu->addSeparator(); + } +} + void Tray::showTrayMessage() const { } diff --git a/Telegram/SourceFiles/tray.cpp b/Telegram/SourceFiles/tray.cpp index 521886ee7e..f6a6214800 100644 --- a/Telegram/SourceFiles/tray.cpp +++ b/Telegram/SourceFiles/tray.cpp @@ -6,6 +6,7 @@ For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "tray.h" +#include "tray_accounts_menu.h" #include "core/application.h" #include "core/core_settings.h" @@ -51,6 +52,10 @@ void Tray::create() { rebuildMenu(); }, _tray.lifetime()); + TrayAccountsMenu::SetupChangesSubscription( + [=] { rebuildMenu(); }, + _tray.lifetime()); + _tray.iconClicks( ) | rpl::on_next([=] { const auto skipTrayClick = (_lastTrayClickTime > 0) @@ -97,6 +102,8 @@ void Tray::rebuildMenu() { _tray.addAction(tr::lng_quit_from_tray(), [] { Core::Quit(); }); + TrayAccountsMenu::Fill(_tray); + updateMenuText(); } diff --git a/Telegram/SourceFiles/tray_accounts_menu.cpp b/Telegram/SourceFiles/tray_accounts_menu.cpp new file mode 100644 index 0000000000..760ecfd425 --- /dev/null +++ b/Telegram/SourceFiles/tray_accounts_menu.cpp @@ -0,0 +1,81 @@ +/* +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 "tray_accounts_menu.h" + +#include "base/weak_ptr.h" +#include "base/qt/qt_key_modifiers.h" +#include "core/application.h" +#include "data/data_peer.h" +#include "data/data_user.h" +#include "info/profile/info_profile_values.h" +#include "main/main_account.h" +#include "main/main_domain.h" +#include "main/main_session.h" +#include "styles/style_window.h" +#include "ui/dynamic_thumbnails.h" + +namespace Core::TrayAccountsMenu { + +void SetupChangesSubscription(Fn callback, rpl::lifetime &lifetime) { + const auto accountSessionsLifetime = lifetime.make_state(); + const auto watchAccountSessions = [=] { + accountSessionsLifetime->destroy(); + for (const auto &[index, account] : Core::App().domain().accounts()) { + account->sessionChanges( + ) | rpl::on_next([=](Main::Session*) { + callback(); + }, *accountSessionsLifetime); + } + }; + Core::App().domain().accountsChanges() | rpl::on_next([=] { + watchAccountSessions(); + callback(); + }, lifetime); + watchAccountSessions(); +} + +void Fill(Platform::Tray &tray) { + auto accounts = std::vector>(); + for (const auto &account : Core::App().domain().orderedAccounts()) { + if (account->sessionExists()) { + accounts.push_back(account); + } + } + if (accounts.size() <= 1) { + return; + } + tray.addSeparator(); + constexpr auto kMaxLength = 30; + for (const auto account : accounts) { + const auto user = account->session().user(); + const auto weak = base::make_weak(account); + tray.addAction( + Info::Profile::NameValue( + user + ) | rpl::map([=](const QString &name) { + return (name.size() > kMaxLength) + ? (name.mid(0, kMaxLength) + Ui::kQEllipsis) + : name; + }), + [weak] { + const auto strong = weak.get(); + if (!strong || !strong->sessionExists()) { + return; + } + if (base::IsCtrlPressed()) { + Core::App().ensureSeparateWindowFor({ strong }); + } else { + Core::App().domain().maybeActivate(strong); + } + }, + Ui::MakeUserpicThumbnail(user, true), + st::notifyMacPhotoSize); + } +} + +} // namespace Core::TrayAccountsMenu diff --git a/Telegram/SourceFiles/tray_accounts_menu.h b/Telegram/SourceFiles/tray_accounts_menu.h new file mode 100644 index 0000000000..2d24931dfb --- /dev/null +++ b/Telegram/SourceFiles/tray_accounts_menu.h @@ -0,0 +1,19 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "platform/platform_tray.h" + +namespace Core::TrayAccountsMenu { + +void SetupChangesSubscription( + Fn callback, + rpl::lifetime &lifetime); +void Fill(Platform::Tray &tray); + +} // namespace Core::TrayAccountsMenu diff --git a/Telegram/SourceFiles/tray_accounts_menu_dummy.cpp b/Telegram/SourceFiles/tray_accounts_menu_dummy.cpp new file mode 100644 index 0000000000..1e135ed6fc --- /dev/null +++ b/Telegram/SourceFiles/tray_accounts_menu_dummy.cpp @@ -0,0 +1,20 @@ +/* +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 "tray_accounts_menu.h" + +namespace Core::TrayAccountsMenu { + +void SetupChangesSubscription( + [[maybe_unused]] Fn callback, + [[maybe_unused]] rpl::lifetime &lifetime) { +} + +void Fill([[maybe_unused]] Platform::Tray &tray) { +} + +} // namespace Core::TrayAccountsMenu