mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Added initial implementation of Windows tray.
This commit is contained in:
parent
56fdc7d39a
commit
70acc7a0e3
4 changed files with 269 additions and 1 deletions
|
@ -7,9 +7,237 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "platform/win/tray_win.h"
|
||||
|
||||
#include "base/invoke_queued.h"
|
||||
#include "base/qt_signal_producer.h"
|
||||
#include "core/application.h"
|
||||
#include "main/main_session.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_window.h"
|
||||
|
||||
#include <QtWidgets/QSystemTrayIcon>
|
||||
|
||||
namespace Platform {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kTooltipDelay = crl::time(10000);
|
||||
|
||||
[[nodiscard]] QImage IconWithCounter(
|
||||
Window::CounterLayerArgs &&args,
|
||||
bool supportMode,
|
||||
bool smallIcon) {
|
||||
static constexpr auto kCount = 3;
|
||||
static auto ScaledLogo = std::array<QImage, kCount>();
|
||||
static auto ScaledLogoNoMargin = std::array<QImage, kCount>();
|
||||
|
||||
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);
|
||||
|
||||
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 (supportMode) {
|
||||
Window::ConvertIconToBlack(result);
|
||||
}
|
||||
if (!args.count) {
|
||||
return result;
|
||||
} else if (smallIcon) {
|
||||
return Window::WithSmallCounter(std::move(result), std::move(args));
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
[[nodiscard]] QWidget *Parent() {
|
||||
Expects(Core::App().primaryWindow() != nullptr);
|
||||
return Core::App().primaryWindow()->widget();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Tray::Tray() {
|
||||
}
|
||||
|
||||
void Tray::createIcon() {
|
||||
if (!_icon) {
|
||||
_icon = base::make_unique_q<QSystemTrayIcon>(Parent());
|
||||
updateIcon();
|
||||
_icon->setToolTip(AppName.utf16());
|
||||
using Reason = QSystemTrayIcon::ActivationReason;
|
||||
base::qt_signal_producer(
|
||||
_icon.get(),
|
||||
&QSystemTrayIcon::activated
|
||||
) | rpl::start_with_next([=](Reason reason) {
|
||||
if (reason == QSystemTrayIcon::Context && _menu) {
|
||||
_aboutToShowRequests.fire({});
|
||||
InvokeQueued(_menu.get(), [=] {
|
||||
_menu->popup(QCursor::pos());
|
||||
});
|
||||
} else {
|
||||
_iconClicks.fire({});
|
||||
}
|
||||
}, _lifetime);
|
||||
} else {
|
||||
updateIcon();
|
||||
}
|
||||
|
||||
_icon->show();
|
||||
}
|
||||
|
||||
void Tray::destroyIcon() {
|
||||
_icon = nullptr;
|
||||
}
|
||||
|
||||
void Tray::updateIcon() {
|
||||
if (!_icon) {
|
||||
return;
|
||||
}
|
||||
const auto counter = Core::App().unreadBadge();
|
||||
const auto muted = Core::App().unreadBadgeMuted();
|
||||
const auto controller = Core::App().primaryWindow();
|
||||
const auto session = !controller
|
||||
? nullptr
|
||||
: !controller->sessionController()
|
||||
? nullptr
|
||||
: &controller->sessionController()->session();
|
||||
|
||||
const auto iconSizeSmall = QSize(
|
||||
GetSystemMetrics(SM_CXSMICON),
|
||||
GetSystemMetrics(SM_CYSMICON));
|
||||
const auto iconSizeBig = QSize(
|
||||
GetSystemMetrics(SM_CXICON),
|
||||
GetSystemMetrics(SM_CYICON));
|
||||
|
||||
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 && session->supportMode(),
|
||||
smallIcon));
|
||||
};
|
||||
|
||||
auto iconSmallPixmap16 = iconWithCounter(16, counter, true);
|
||||
auto iconSmallPixmap32 = iconWithCounter(32, counter, true);
|
||||
auto iconSmall = QIcon();
|
||||
iconSmall.addPixmap(iconSmallPixmap16);
|
||||
iconSmall.addPixmap(iconSmallPixmap32);
|
||||
// Force Qt to use right icon size, not the larger one.
|
||||
QIcon forTrayIcon;
|
||||
forTrayIcon.addPixmap(iconSizeSmall.width() >= 20
|
||||
? iconSmallPixmap32
|
||||
: iconSmallPixmap16);
|
||||
_icon->setIcon(forTrayIcon);
|
||||
}
|
||||
|
||||
void Tray::createMenu() {
|
||||
if (!_menu) {
|
||||
_menu = base::make_unique_q<Ui::PopupMenu>(nullptr);
|
||||
_menu->deleteOnHide(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Tray::destroyMenu() {
|
||||
_menu = nullptr;
|
||||
_actionsLifetime.destroy();
|
||||
}
|
||||
|
||||
void Tray::addAction(rpl::producer<QString> text, Fn<void()> &&callback) {
|
||||
if (!_menu) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto action = _menu->addAction(QString(), std::move(callback));
|
||||
std::move(
|
||||
text
|
||||
) | rpl::start_with_next([=](const QString &text) {
|
||||
action->setText(text);
|
||||
}, _actionsLifetime);
|
||||
}
|
||||
|
||||
void Tray::showTrayMessage() const {
|
||||
if (!cSeenTrayTooltip() && _icon) {
|
||||
_icon->showMessage(
|
||||
AppName.utf16(),
|
||||
tr::lng_tray_icon_text(tr::now),
|
||||
QSystemTrayIcon::Information,
|
||||
kTooltipDelay);
|
||||
cSetSeenTrayTooltip(true);
|
||||
Local::writeSettings();
|
||||
}
|
||||
}
|
||||
|
||||
bool Tray::hasTrayMessageSupport() const {
|
||||
return !cSeenTrayTooltip();
|
||||
}
|
||||
|
||||
rpl::producer<> Tray::aboutToShowRequests() const {
|
||||
return _aboutToShowRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<> Tray::showFromTrayRequests() const {
|
||||
return rpl::never<>();
|
||||
}
|
||||
|
||||
rpl::producer<> Tray::hideToTrayRequests() const {
|
||||
return rpl::never<>();
|
||||
}
|
||||
|
||||
rpl::producer<> Tray::iconClicks() const {
|
||||
return _iconClicks.events();
|
||||
}
|
||||
|
||||
rpl::lifetime &Tray::lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
||||
} // namespace Platform
|
||||
|
|
|
@ -9,6 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "platform/platform_tray.h"
|
||||
|
||||
#include "base/unique_qptr.h"
|
||||
|
||||
namespace Ui {
|
||||
class PopupMenu;
|
||||
} // namespace Ui
|
||||
|
||||
class QSystemTrayIcon;
|
||||
|
||||
namespace Platform {
|
||||
|
||||
class Tray final {
|
||||
|
@ -36,6 +44,14 @@ public:
|
|||
[[nodiscard]] rpl::lifetime &lifetime();
|
||||
|
||||
private:
|
||||
base::unique_qptr<QSystemTrayIcon> _icon;
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
|
||||
rpl::event_stream<> _iconClicks;
|
||||
rpl::event_stream<> _aboutToShowRequests;
|
||||
|
||||
rpl::lifetime _actionsLifetime;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "core/application.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
namespace Core {
|
||||
|
||||
Tray::Tray() {
|
||||
|
@ -40,6 +42,18 @@ void Tray::create() {
|
|||
) | rpl::start_with_next([=] {
|
||||
rebuildMenu();
|
||||
}, _tray.lifetime());
|
||||
|
||||
_tray.iconClicks(
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto skipTrayClick = (_lastTrayClickTime > 0)
|
||||
&& (crl::now() - _lastTrayClickTime
|
||||
< QApplication::doubleClickInterval());
|
||||
if (!skipTrayClick) {
|
||||
_activeForTrayIconAction = Core::App().isActiveForTrayMenu();
|
||||
_minimizeMenuItemClicks.fire({});
|
||||
_lastTrayClickTime = crl::now();
|
||||
}
|
||||
}, _tray.lifetime());
|
||||
}
|
||||
|
||||
void Tray::rebuildMenu() {
|
||||
|
@ -100,12 +114,20 @@ rpl::producer<> Tray::showFromTrayRequests() const {
|
|||
}
|
||||
|
||||
rpl::producer<> Tray::hideToTrayRequests() const {
|
||||
return rpl::merge(
|
||||
auto triggers = rpl::merge(
|
||||
_tray.hideToTrayRequests(),
|
||||
_minimizeMenuItemClicks.events() | rpl::filter([=] {
|
||||
return _activeForTrayIconAction;
|
||||
})
|
||||
);
|
||||
if (_tray.hasTrayMessageSupport()) {
|
||||
return std::move(triggers) | rpl::map([=]() -> rpl::empty_value {
|
||||
_tray.showTrayMessage();
|
||||
return {};
|
||||
});
|
||||
} else {
|
||||
return triggers;
|
||||
}
|
||||
}
|
||||
|
||||
void Tray::toggleSoundNotifications() {
|
||||
|
|
|
@ -30,6 +30,8 @@ private:
|
|||
Platform::Tray _tray;
|
||||
|
||||
bool _activeForTrayIconAction = false;
|
||||
crl::time _lastTrayClickTime = 0;
|
||||
|
||||
rpl::event_stream<> _textUpdates;
|
||||
rpl::event_stream<> _minimizeMenuItemClicks;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue