mirror of
https://github.com/vale981/tdesktop
synced 2025-03-10 04:56:43 -04:00

Currently the build without implicitly included precompiled header is not supported anyway (because Qt MOC source files do not include stdafx.h, they include plain headers). So when we decide to support building without implicitly included precompiled headers we'll have to fix all the headers anyway.
935 lines
27 KiB
C++
935 lines
27 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
It is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|
*/
|
|
#include "window/notifications_manager_default.h"
|
|
|
|
#include "platform/platform_notifications_manager.h"
|
|
#include "application.h"
|
|
#include "mainwindow.h"
|
|
#include "lang.h"
|
|
#include "ui/widgets/buttons.h"
|
|
#include "ui/widgets/input_fields.h"
|
|
#include "dialogs/dialogs_layout.h"
|
|
#include "window/themes/window_theme.h"
|
|
#include "styles/style_dialogs.h"
|
|
#include "styles/style_boxes.h"
|
|
#include "styles/style_window.h"
|
|
|
|
namespace Window {
|
|
namespace Notifications {
|
|
namespace Default {
|
|
namespace {
|
|
|
|
NeverFreedPointer<Manager> ManagerInstance;
|
|
|
|
int notificationMaxHeight() {
|
|
return st::notifyMinHeight + st::notifyReplyArea.heightMax + st::notifyBorderWidth;
|
|
}
|
|
|
|
QPoint notificationStartPosition() {
|
|
auto r = psDesktopRect();
|
|
auto isLeft = Notify::IsLeftCorner(Global::NotificationsCorner());
|
|
auto isTop = Notify::IsTopCorner(Global::NotificationsCorner());
|
|
auto x = (isLeft == rtl()) ? (r.x() + r.width() - st::notifyWidth - st::notifyDeltaX) : (r.x() + st::notifyDeltaX);
|
|
auto y = isTop ? r.y() : (r.y() + r.height());
|
|
return QPoint(x, y);
|
|
}
|
|
|
|
internal::Widget::Direction notificationShiftDirection() {
|
|
auto isTop = Notify::IsTopCorner(Global::NotificationsCorner());
|
|
return isTop ? internal::Widget::Direction::Down : internal::Widget::Direction::Up;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void Start() {
|
|
ManagerInstance.createIfNull();
|
|
}
|
|
|
|
Manager *GetManager() {
|
|
return ManagerInstance.data();
|
|
}
|
|
|
|
void Finish() {
|
|
ManagerInstance.clear();
|
|
}
|
|
|
|
Manager::Manager() {
|
|
subscribe(FileDownload::ImageLoaded(), [this] {
|
|
for_const (auto notification, _notifications) {
|
|
notification->updatePeerPhoto();
|
|
}
|
|
});
|
|
subscribe(Global::RefNotifySettingsChanged(), [this](Notify::ChangeType change) {
|
|
settingsChanged(change);
|
|
});
|
|
_inputCheckTimer.setTimeoutHandler([this] { checkLastInput(); });
|
|
}
|
|
|
|
bool Manager::hasReplyingNotification() const {
|
|
for_const (auto notification, _notifications) {
|
|
if (notification->isReplying()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Manager::settingsChanged(Notify::ChangeType change) {
|
|
if (change == Notify::ChangeType::Corner) {
|
|
auto startPosition = notificationStartPosition();
|
|
auto shiftDirection = notificationShiftDirection();
|
|
for_const (auto notification, _notifications) {
|
|
notification->updatePosition(startPosition, shiftDirection);
|
|
}
|
|
if (_hideAll) {
|
|
_hideAll->updatePosition(startPosition, shiftDirection);
|
|
}
|
|
} else if (change == Notify::ChangeType::MaxCount) {
|
|
int allow = Global::NotificationsCount();
|
|
for (int i = _notifications.size(); i != 0;) {
|
|
auto notification = _notifications[--i];
|
|
if (notification->isUnlinked()) continue;
|
|
if (--allow < 0) {
|
|
notification->unlinkHistory();
|
|
}
|
|
}
|
|
if (allow > 0) {
|
|
for (int i = 0; i != allow; ++i) {
|
|
showNextFromQueue();
|
|
}
|
|
}
|
|
} else if (change == Notify::ChangeType::DemoIsShown) {
|
|
auto demoIsShown = Global::NotificationsDemoIsShown();
|
|
_demoMasterOpacity.start([this] { demoMasterOpacityCallback(); }, demoIsShown ? 1. : 0., demoIsShown ? 0. : 1., st::notifyFastAnim);
|
|
}
|
|
}
|
|
|
|
void Manager::demoMasterOpacityCallback() {
|
|
for_const (auto notification, _notifications) {
|
|
notification->updateOpacity();
|
|
}
|
|
if (_hideAll) {
|
|
_hideAll->updateOpacity();
|
|
}
|
|
}
|
|
|
|
float64 Manager::demoMasterOpacity() const {
|
|
return _demoMasterOpacity.current(Global::NotificationsDemoIsShown() ? 0. : 1.);
|
|
}
|
|
|
|
void Manager::checkLastInput() {
|
|
auto replying = hasReplyingNotification();
|
|
auto waiting = false;
|
|
for_const (auto notification, _notifications) {
|
|
if (!notification->checkLastInput(replying)) {
|
|
waiting = true;
|
|
}
|
|
}
|
|
if (waiting) {
|
|
_inputCheckTimer.start(300);
|
|
}
|
|
}
|
|
|
|
void Manager::startAllHiding() {
|
|
if (!hasReplyingNotification()) {
|
|
int notHidingCount = 0;
|
|
for_const (auto notification, _notifications) {
|
|
if (notification->isShowing()) {
|
|
++notHidingCount;
|
|
} else {
|
|
notification->startHiding();
|
|
}
|
|
}
|
|
notHidingCount += _queuedNotifications.size();
|
|
if (_hideAll && notHidingCount < 2) {
|
|
_hideAll->startHiding();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Manager::stopAllHiding() {
|
|
for_const (auto notification, _notifications) {
|
|
notification->stopHiding();
|
|
}
|
|
if (_hideAll) {
|
|
_hideAll->stopHiding();
|
|
}
|
|
}
|
|
|
|
void Manager::showNextFromQueue() {
|
|
if (!_queuedNotifications.isEmpty()) {
|
|
int count = Global::NotificationsCount();
|
|
for_const (auto notification, _notifications) {
|
|
if (notification->isUnlinked()) continue;
|
|
--count;
|
|
}
|
|
if (count > 0) {
|
|
auto startPosition = notificationStartPosition();
|
|
auto startShift = 0;
|
|
auto shiftDirection = notificationShiftDirection();
|
|
do {
|
|
auto queued = _queuedNotifications.front();
|
|
_queuedNotifications.pop_front();
|
|
|
|
auto notification = std::make_unique<Notification>(
|
|
queued.history,
|
|
queued.peer,
|
|
queued.author,
|
|
queued.item,
|
|
queued.forwardedCount,
|
|
startPosition, startShift, shiftDirection);
|
|
Platform::Notifications::CustomNotificationShownHook(notification.get());
|
|
_notifications.push_back(notification.release());
|
|
--count;
|
|
} while (count > 0 && !_queuedNotifications.isEmpty());
|
|
|
|
_positionsOutdated = true;
|
|
checkLastInput();
|
|
}
|
|
}
|
|
if (_positionsOutdated) {
|
|
moveWidgets();
|
|
}
|
|
}
|
|
|
|
void Manager::moveWidgets() {
|
|
auto shift = st::notifyDeltaY;
|
|
int lastShift = 0, lastShiftCurrent = 0, count = 0;
|
|
for (int i = _notifications.size(); i != 0;) {
|
|
auto notification = _notifications[--i];
|
|
if (notification->isUnlinked()) continue;
|
|
|
|
notification->changeShift(shift);
|
|
shift += notification->height() + st::notifyDeltaY;
|
|
|
|
lastShiftCurrent = notification->currentShift();
|
|
lastShift = shift;
|
|
|
|
++count;
|
|
}
|
|
|
|
if (count > 1 || !_queuedNotifications.isEmpty()) {
|
|
auto deltaY = st::notifyHideAllHeight + st::notifyDeltaY;
|
|
if (!_hideAll) {
|
|
_hideAll = new HideAllButton(notificationStartPosition(), lastShiftCurrent, notificationShiftDirection());
|
|
}
|
|
_hideAll->changeShift(lastShift);
|
|
_hideAll->stopHiding();
|
|
} else if (_hideAll) {
|
|
_hideAll->startHidingFast();
|
|
}
|
|
}
|
|
|
|
void Manager::changeNotificationHeight(Notification *notification, int newHeight) {
|
|
auto deltaHeight = newHeight - notification->height();
|
|
if (!deltaHeight) return;
|
|
|
|
notification->addToHeight(deltaHeight);
|
|
auto index = _notifications.indexOf(notification);
|
|
if (index > 0) {
|
|
for (int i = 0; i != index; ++i) {
|
|
auto notification = _notifications[i];
|
|
if (notification->isUnlinked()) continue;
|
|
|
|
notification->addToShift(deltaHeight);
|
|
}
|
|
}
|
|
if (_hideAll) {
|
|
_hideAll->addToShift(deltaHeight);
|
|
}
|
|
}
|
|
|
|
void Manager::unlinkFromShown(Notification *remove) {
|
|
if (remove) {
|
|
if (remove->unlinkHistory()) {
|
|
_positionsOutdated = true;
|
|
}
|
|
}
|
|
showNextFromQueue();
|
|
}
|
|
|
|
void Manager::removeFromShown(Notification *remove) {
|
|
if (remove) {
|
|
auto index = _notifications.indexOf(remove);
|
|
if (index >= 0) {
|
|
_notifications.removeAt(index);
|
|
_positionsOutdated = true;
|
|
}
|
|
}
|
|
showNextFromQueue();
|
|
}
|
|
|
|
void Manager::removeHideAll(HideAllButton *remove) {
|
|
if (remove == _hideAll) {
|
|
_hideAll = nullptr;
|
|
}
|
|
}
|
|
void Manager::doShowNotification(HistoryItem *item, int forwardedCount) {
|
|
_queuedNotifications.push_back(QueuedNotification(item, forwardedCount));
|
|
showNextFromQueue();
|
|
}
|
|
|
|
void Manager::doClearAll() {
|
|
_queuedNotifications.clear();
|
|
for_const (auto notification, _notifications) {
|
|
notification->unlinkHistory();
|
|
}
|
|
showNextFromQueue();
|
|
}
|
|
|
|
void Manager::doClearAllFast() {
|
|
_queuedNotifications.clear();
|
|
auto notifications = base::take(_notifications);
|
|
for_const (auto notification, notifications) {
|
|
delete notification;
|
|
}
|
|
delete base::take(_hideAll);
|
|
}
|
|
|
|
void Manager::doClearFromHistory(History *history) {
|
|
for (auto i = _queuedNotifications.begin(); i != _queuedNotifications.cend();) {
|
|
if (i->history == history) {
|
|
i = _queuedNotifications.erase(i);
|
|
} else {
|
|
++i;
|
|
}
|
|
}
|
|
for_const (auto notification, _notifications) {
|
|
if (notification->unlinkHistory(history)) {
|
|
_positionsOutdated = true;
|
|
}
|
|
}
|
|
showNextFromQueue();
|
|
}
|
|
|
|
void Manager::doClearFromItem(HistoryItem *item) {
|
|
for (auto i = 0, queuedCount = _queuedNotifications.size(); i != queuedCount; ++i) {
|
|
if (_queuedNotifications[i].item == item) {
|
|
_queuedNotifications.removeAt(i);
|
|
break;
|
|
}
|
|
}
|
|
for_const (auto notification, _notifications) {
|
|
// Calls unlinkFromShown() -> showNextFromQueue()
|
|
notification->itemRemoved(item);
|
|
}
|
|
}
|
|
|
|
void Manager::doUpdateAll() {
|
|
for_const (auto notification, _notifications) {
|
|
notification->updateNotifyDisplay();
|
|
}
|
|
}
|
|
|
|
Manager::~Manager() {
|
|
clearAllFast();
|
|
}
|
|
|
|
namespace internal {
|
|
|
|
Widget::Widget(QPoint startPosition, int shift, Direction shiftDirection) : TWidget(nullptr)
|
|
, _startPosition(startPosition)
|
|
, _direction(shiftDirection)
|
|
, a_shift(shift)
|
|
, _a_shift(animation(this, &Widget::step_shift)) {
|
|
setWindowOpacity(0.);
|
|
|
|
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint);
|
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
|
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
|
|
|
|
_a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::notifyFastAnim);
|
|
}
|
|
|
|
void Widget::destroyDelayed() {
|
|
hide();
|
|
if (_deleted) return;
|
|
_deleted = true;
|
|
|
|
// Ubuntu has a lag if deleteLater() called immediately.
|
|
#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
|
|
QTimer::singleShot(1000, [this] { delete this; });
|
|
#else // Q_OS_LINUX32 || Q_OS_LINUX64
|
|
deleteLater();
|
|
#endif // Q_OS_LINUX32 || Q_OS_LINUX64
|
|
}
|
|
|
|
void Widget::opacityAnimationCallback() {
|
|
updateOpacity();
|
|
update();
|
|
if (!_a_opacity.animating() && _hiding) {
|
|
destroyDelayed();
|
|
}
|
|
}
|
|
|
|
void Widget::step_shift(float64 ms, bool timer) {
|
|
float64 dt = ms / float64(st::notifyFastAnim);
|
|
if (dt >= 1) {
|
|
a_shift.finish();
|
|
} else {
|
|
a_shift.update(dt, anim::linear);
|
|
}
|
|
moveByShift();
|
|
}
|
|
|
|
void Widget::hideSlow() {
|
|
hideAnimated(st::notifySlowHide, anim::easeInCirc);
|
|
}
|
|
|
|
void Widget::hideFast() {
|
|
hideAnimated(st::notifyFastAnim, anim::linear);
|
|
}
|
|
|
|
void Widget::hideStop() {
|
|
if (_hiding) {
|
|
_hiding = false;
|
|
_a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::notifyFastAnim);
|
|
}
|
|
}
|
|
|
|
void Widget::hideAnimated(float64 duration, const anim::transition &func) {
|
|
_hiding = true;
|
|
_a_opacity.start([this] { opacityAnimationCallback(); }, 1., 0., duration, func);
|
|
}
|
|
|
|
void Widget::updateOpacity() {
|
|
if (auto manager = ManagerInstance.data()) {
|
|
setWindowOpacity(_a_opacity.current(_hiding ? 0. : 1.) * manager->demoMasterOpacity());
|
|
}
|
|
}
|
|
|
|
void Widget::changeShift(int top) {
|
|
a_shift.start(top);
|
|
_a_shift.start();
|
|
}
|
|
|
|
void Widget::updatePosition(QPoint startPosition, Direction shiftDirection) {
|
|
_startPosition = startPosition;
|
|
_direction = shiftDirection;
|
|
moveByShift();
|
|
}
|
|
|
|
void Widget::addToHeight(int add) {
|
|
auto newHeight = height() + add;
|
|
auto newPosition = computePosition(newHeight);
|
|
updateGeometry(newPosition.x(), newPosition.y(), width(), newHeight);
|
|
psUpdateOverlayed(this);
|
|
}
|
|
|
|
void Widget::updateGeometry(int x, int y, int width, int height) {
|
|
setGeometry(x, y, width, height);
|
|
update();
|
|
}
|
|
|
|
void Widget::addToShift(int add) {
|
|
a_shift.add(add);
|
|
moveByShift();
|
|
}
|
|
|
|
void Widget::moveByShift() {
|
|
move(computePosition(height()));
|
|
}
|
|
|
|
QPoint Widget::computePosition(int height) const {
|
|
auto realShift = qRound(a_shift.current());
|
|
if (_direction == Direction::Up) {
|
|
realShift = -realShift - height;
|
|
}
|
|
return QPoint(_startPosition.x(), _startPosition.y() + realShift);
|
|
}
|
|
|
|
Background::Background(QWidget *parent) : TWidget(parent) {
|
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
|
}
|
|
|
|
void Background::paintEvent(QPaintEvent *e) {
|
|
Painter p(this);
|
|
|
|
p.fillRect(rect(), st::notificationBg);
|
|
p.fillRect(0, 0, st::notifyBorderWidth, height(), st::notifyBorder);
|
|
p.fillRect(width() - st::notifyBorderWidth, 0, st::notifyBorderWidth, height(), st::notifyBorder);
|
|
p.fillRect(st::notifyBorderWidth, height() - st::notifyBorderWidth, width() - 2 * st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder);
|
|
}
|
|
|
|
Notification::Notification(History *history, PeerData *peer, PeerData *author, HistoryItem *msg, int forwardedCount, QPoint startPosition, int shift, Direction shiftDirection) : Widget(startPosition, shift, shiftDirection)
|
|
, _history(history)
|
|
, _peer(peer)
|
|
, _author(author)
|
|
, _item(msg)
|
|
, _forwardedCount(forwardedCount)
|
|
#ifdef Q_OS_WIN
|
|
, _started(GetTickCount())
|
|
#endif // Q_OS_WIN
|
|
, _close(this, st::notifyClose)
|
|
, _reply(this, lang(lng_notification_reply), st::defaultBoxButton) {
|
|
auto position = computePosition(st::notifyMinHeight);
|
|
updateGeometry(position.x(), position.y(), st::notifyWidth, st::notifyMinHeight);
|
|
|
|
_userpicLoaded = _peer ? _peer->userpicLoaded() : true;
|
|
updateNotifyDisplay();
|
|
|
|
_hideTimer.setSingleShot(true);
|
|
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideByTimer()));
|
|
|
|
_close->setClickedCallback([this] {
|
|
unlinkHistoryInManager();
|
|
});
|
|
_close->setAcceptBoth(true);
|
|
_close->moveToRight(st::notifyClosePos.x(), st::notifyClosePos.y());
|
|
_close->show();
|
|
|
|
_reply->setClickedCallback([this] {
|
|
showReplyField();
|
|
});
|
|
_replyPadding = st::notifyMinHeight - st::notifyPhotoPos.y() - st::notifyPhotoSize;
|
|
_reply->moveToRight(_replyPadding, height() - _reply->height() - _replyPadding);
|
|
_reply->hide();
|
|
|
|
prepareActionsCache();
|
|
|
|
subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &data) {
|
|
if (data.paletteChanged()) {
|
|
updateNotifyDisplay();
|
|
if (!_buttonsCache.isNull()) {
|
|
prepareActionsCache();
|
|
}
|
|
update();
|
|
if (_background) {
|
|
_background->update();
|
|
}
|
|
}
|
|
});
|
|
|
|
show();
|
|
}
|
|
|
|
void Notification::prepareActionsCache() {
|
|
auto replyCache = myGrab(_reply);
|
|
auto fadeWidth = st::notifyFadeRight.width();
|
|
auto actionsTop = st::notifyTextTop + st::msgNameFont->height;
|
|
auto actionsCacheWidth = _reply->width() + _replyPadding + fadeWidth;
|
|
auto actionsCacheHeight = height() - actionsTop;
|
|
auto actionsCacheImg = QImage(actionsCacheWidth * cIntRetinaFactor(), actionsCacheHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
|
actionsCacheImg.setDevicePixelRatio(cRetinaFactor());
|
|
actionsCacheImg.fill(Qt::transparent);
|
|
{
|
|
Painter p(&actionsCacheImg);
|
|
st::notifyFadeRight.fill(p, rtlrect(0, 0, fadeWidth, actionsCacheHeight, actionsCacheWidth));
|
|
p.fillRect(rtlrect(fadeWidth, 0, actionsCacheWidth - fadeWidth, actionsCacheHeight, actionsCacheWidth), st::notificationBg);
|
|
p.drawPixmapRight(_replyPadding, _reply->y() - actionsTop, actionsCacheWidth, replyCache);
|
|
}
|
|
_buttonsCache = App::pixmapFromImageInPlace(std::move(actionsCacheImg));
|
|
}
|
|
|
|
bool Notification::checkLastInput(bool hasReplyingNotifications) {
|
|
if (!_waitingForInput) return true;
|
|
|
|
auto wasUserInput = true; // TODO
|
|
#ifdef Q_OS_WIN
|
|
LASTINPUTINFO lii;
|
|
lii.cbSize = sizeof(LASTINPUTINFO);
|
|
BOOL res = GetLastInputInfo(&lii);
|
|
wasUserInput = (!res || lii.dwTime >= _started);
|
|
#endif // Q_OS_WIN
|
|
if (wasUserInput) {
|
|
_waitingForInput = false;
|
|
if (!hasReplyingNotifications) {
|
|
_hideTimer.start(st::notifyWaitLongHide);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Notification::onReplyResize() {
|
|
changeHeight(st::notifyMinHeight + _replyArea->height() + st::notifyBorderWidth);
|
|
}
|
|
|
|
void Notification::onReplySubmit(bool ctrlShiftEnter) {
|
|
sendReply();
|
|
}
|
|
|
|
void Notification::onReplyCancel() {
|
|
unlinkHistoryInManager();
|
|
}
|
|
|
|
void Notification::updateGeometry(int x, int y, int width, int height) {
|
|
if (height > st::notifyMinHeight) {
|
|
if (!_background) {
|
|
_background.create(this);
|
|
}
|
|
_background->setGeometry(0, st::notifyMinHeight, width, height - st::notifyMinHeight);
|
|
} else if (_background) {
|
|
_background.destroy();
|
|
}
|
|
Widget::updateGeometry(x, y, width, height);
|
|
}
|
|
|
|
void Notification::paintEvent(QPaintEvent *e) {
|
|
Painter p(this);
|
|
p.setClipRect(e->rect());
|
|
p.drawPixmap(0, 0, _cache);
|
|
|
|
auto buttonsLeft = st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft;
|
|
auto buttonsTop = st::notifyTextTop + st::msgNameFont->height;
|
|
if (a_actionsOpacity.animating(getms())) {
|
|
p.setOpacity(a_actionsOpacity.current());
|
|
p.drawPixmapRight(0, buttonsTop, width(), _buttonsCache);
|
|
} else if (_actionsVisible) {
|
|
p.drawPixmapRight(0, buttonsTop, width(), _buttonsCache);
|
|
}
|
|
}
|
|
|
|
void Notification::actionsOpacityCallback() {
|
|
update();
|
|
if (!a_actionsOpacity.animating() && _actionsVisible) {
|
|
_reply->show();
|
|
}
|
|
}
|
|
|
|
void Notification::updateNotifyDisplay() {
|
|
if (!_history || !_peer || (!_item && _forwardedCount < 2)) return;
|
|
|
|
auto options = Manager::getNotificationOptions(_item);
|
|
_hideReplyButton = options.hideReplyButton;
|
|
|
|
int32 w = width(), h = height();
|
|
QImage img(w * cIntRetinaFactor(), h * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
|
if (cRetina()) img.setDevicePixelRatio(cRetinaFactor());
|
|
img.fill(st::notificationBg->c);
|
|
|
|
{
|
|
Painter p(&img);
|
|
p.fillRect(0, 0, w - st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder);
|
|
p.fillRect(w - st::notifyBorderWidth, 0, st::notifyBorderWidth, h - st::notifyBorderWidth, st::notifyBorder);
|
|
p.fillRect(st::notifyBorderWidth, h - st::notifyBorderWidth, w - st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder);
|
|
p.fillRect(0, st::notifyBorderWidth, st::notifyBorderWidth, h - st::notifyBorderWidth, st::notifyBorder);
|
|
|
|
if (!options.hideNameAndPhoto) {
|
|
_history->peer->loadUserpic(true, true);
|
|
_history->peer->paintUserpicLeft(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize);
|
|
} else {
|
|
static QPixmap icon = App::pixmapFromImageInPlace(App::wnd()->iconLarge().scaled(st::notifyPhotoSize, st::notifyPhotoSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
|
icon.setDevicePixelRatio(cRetinaFactor());
|
|
p.drawPixmap(st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), icon);
|
|
}
|
|
|
|
int32 itemWidth = w - st::notifyPhotoPos.x() - st::notifyPhotoSize - st::notifyTextLeft - st::notifyClosePos.x() - st::notifyClose.width;
|
|
|
|
QRect rectForName(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyTextTop, itemWidth, st::msgNameFont->height);
|
|
if (!options.hideNameAndPhoto) {
|
|
if (auto chatTypeIcon = Dialogs::Layout::ChatTypeIcon(_history->peer, false, false)) {
|
|
chatTypeIcon->paint(p, rectForName.topLeft(), w);
|
|
rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip);
|
|
}
|
|
}
|
|
|
|
if (!options.hideMessageText) {
|
|
const HistoryItem *textCachedFor = 0;
|
|
Text itemTextCache(itemWidth);
|
|
QRect r(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyItemTop + st::msgNameFont->height, itemWidth, 2 * st::dialogsTextFont->height);
|
|
if (_item) {
|
|
auto active = false, selected = false;
|
|
_item->drawInDialog(p, r, active, selected, textCachedFor, itemTextCache);
|
|
} else if (_forwardedCount > 1) {
|
|
p.setFont(st::dialogsTextFont);
|
|
if (_author) {
|
|
itemTextCache.setText(st::dialogsTextStyle, _author->name);
|
|
p.setPen(st::dialogsTextFgService);
|
|
itemTextCache.drawElided(p, r.left(), r.top(), r.width(), st::dialogsTextFont->height);
|
|
r.setTop(r.top() + st::dialogsTextFont->height);
|
|
}
|
|
p.setPen(st::dialogsTextFg);
|
|
p.drawText(r.left(), r.top() + st::dialogsTextFont->ascent, lng_forward_messages(lt_count, _forwardedCount));
|
|
}
|
|
} else {
|
|
static QString notifyText = st::dialogsTextFont->elided(lang(lng_notification_preview), itemWidth);
|
|
p.setFont(st::dialogsTextFont);
|
|
p.setPen(st::dialogsTextFgService);
|
|
p.drawText(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyItemTop + st::msgNameFont->height + st::dialogsTextFont->ascent, notifyText);
|
|
}
|
|
|
|
p.setPen(st::dialogsNameFg);
|
|
if (!options.hideNameAndPhoto) {
|
|
_history->peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
|
|
} else {
|
|
p.setFont(st::msgNameFont);
|
|
static QString notifyTitle = st::msgNameFont->elided(qsl("Telegram Desktop"), rectForName.width());
|
|
p.drawText(rectForName.left(), rectForName.top() + st::msgNameFont->ascent, notifyTitle);
|
|
}
|
|
}
|
|
|
|
_cache = App::pixmapFromImageInPlace(std::move(img));
|
|
if (!canReply()) {
|
|
toggleActionButtons(false);
|
|
}
|
|
update();
|
|
}
|
|
|
|
void Notification::updatePeerPhoto() {
|
|
if (_userpicLoaded || !_peer || !_peer->userpicLoaded()) {
|
|
return;
|
|
}
|
|
_userpicLoaded = true;
|
|
|
|
auto img = _cache.toImage();
|
|
{
|
|
Painter p(&img);
|
|
_peer->paintUserpicLeft(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize);
|
|
}
|
|
_cache = App::pixmapFromImageInPlace(std::move(img));
|
|
update();
|
|
}
|
|
|
|
void Notification::itemRemoved(HistoryItem *deleted) {
|
|
if (_item && _item == deleted) {
|
|
_item = nullptr;
|
|
unlinkHistoryInManager();
|
|
}
|
|
}
|
|
|
|
bool Notification::canReply() const {
|
|
return !_hideReplyButton && (_item != nullptr) && !App::passcoded() && (Global::NotifyView() <= dbinvShowPreview);
|
|
}
|
|
|
|
void Notification::unlinkHistoryInManager() {
|
|
if (auto manager = ManagerInstance.data()) {
|
|
manager->unlinkFromShown(this);
|
|
}
|
|
}
|
|
|
|
void Notification::toggleActionButtons(bool visible) {
|
|
if (_actionsVisible != visible) {
|
|
_actionsVisible = visible;
|
|
a_actionsOpacity.start([this] { actionsOpacityCallback(); }, _actionsVisible ? 0. : 1., _actionsVisible ? 1. : 0., st::notifyActionsDuration);
|
|
_reply->clearState();
|
|
_reply->hide();
|
|
}
|
|
}
|
|
|
|
void Notification::showReplyField() {
|
|
activateWindow();
|
|
|
|
if (_replyArea) {
|
|
_replyArea->setFocus();
|
|
return;
|
|
}
|
|
stopHiding();
|
|
|
|
_background.create(this);
|
|
_background->setGeometry(0, st::notifyMinHeight, width(), st::notifySendReply.height + st::notifyBorderWidth);
|
|
_background->show();
|
|
|
|
_replyArea.create(this, st::notifyReplyArea, lang(lng_message_ph), QString());
|
|
_replyArea->resize(width() - st::notifySendReply.width - 2 * st::notifyBorderWidth, st::notifySendReply.height);
|
|
_replyArea->moveToLeft(st::notifyBorderWidth, st::notifyMinHeight);
|
|
_replyArea->show();
|
|
_replyArea->setFocus();
|
|
_replyArea->setMaxLength(MaxMessageSize);
|
|
_replyArea->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
|
|
|
// Catch mouse press event to activate the window.
|
|
QCoreApplication::instance()->installEventFilter(this);
|
|
connect(_replyArea, SIGNAL(resized()), this, SLOT(onReplyResize()));
|
|
connect(_replyArea, SIGNAL(submitted(bool)), this, SLOT(onReplySubmit(bool)));
|
|
connect(_replyArea, SIGNAL(cancelled()), this, SLOT(onReplyCancel()));
|
|
|
|
_replySend.create(this, st::notifySendReply);
|
|
_replySend->moveToRight(st::notifyBorderWidth, st::notifyMinHeight);
|
|
_replySend->show();
|
|
_replySend->setClickedCallback([this] { sendReply(); });
|
|
|
|
toggleActionButtons(false);
|
|
|
|
onReplyResize();
|
|
update();
|
|
}
|
|
|
|
void Notification::sendReply() {
|
|
if (!_history) return;
|
|
|
|
if (auto manager = ManagerInstance.data()) {
|
|
auto peerId = _history->peer->id;
|
|
auto msgId = _item ? _item->id : ShowAtUnreadMsgId;
|
|
manager->notificationReplied(peerId, msgId, _replyArea->getLastText());
|
|
|
|
manager->startAllHiding();
|
|
}
|
|
}
|
|
|
|
void Notification::changeHeight(int newHeight) {
|
|
if (auto manager = ManagerInstance.data()) {
|
|
manager->changeNotificationHeight(this, newHeight);
|
|
}
|
|
}
|
|
|
|
bool Notification::unlinkHistory(History *history) {
|
|
auto unlink = _history && (history == _history || !history);
|
|
if (unlink) {
|
|
hideFast();
|
|
_history = nullptr;
|
|
_item = nullptr;
|
|
}
|
|
return unlink;
|
|
}
|
|
|
|
void Notification::enterEventHook(QEvent *e) {
|
|
if (!_history) return;
|
|
if (auto manager = ManagerInstance.data()) {
|
|
manager->stopAllHiding();
|
|
}
|
|
if (!_replyArea && canReply()) {
|
|
toggleActionButtons(true);
|
|
}
|
|
}
|
|
|
|
void Notification::leaveEventHook(QEvent *e) {
|
|
if (!_history) return;
|
|
if (auto manager = ManagerInstance.data()) {
|
|
manager->startAllHiding();
|
|
}
|
|
toggleActionButtons(false);
|
|
}
|
|
|
|
void Notification::startHiding() {
|
|
if (!_history) return;
|
|
hideSlow();
|
|
}
|
|
|
|
void Notification::mousePressEvent(QMouseEvent *e) {
|
|
if (!_history) return;
|
|
|
|
if (e->button() == Qt::RightButton) {
|
|
unlinkHistoryInManager();
|
|
} else {
|
|
e->ignore();
|
|
if (auto manager = ManagerInstance.data()) {
|
|
auto peerId = _history->peer->id;
|
|
auto msgId = _item ? _item->id : ShowAtUnreadMsgId;
|
|
manager->notificationActivated(peerId, msgId);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Notification::eventFilter(QObject *o, QEvent *e) {
|
|
if (e->type() == QEvent::MouseButtonPress) {
|
|
if (auto receiver = qobject_cast<QWidget*>(o)) {
|
|
if (isAncestorOf(receiver)) {
|
|
activateWindow();
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Notification::stopHiding() {
|
|
if (!_history) return;
|
|
_hideTimer.stop();
|
|
Widget::hideStop();
|
|
}
|
|
|
|
void Notification::onHideByTimer() {
|
|
startHiding();
|
|
}
|
|
|
|
Notification::~Notification() {
|
|
if (auto manager = ManagerInstance.data()) {
|
|
manager->removeFromShown(this);
|
|
}
|
|
}
|
|
|
|
HideAllButton::HideAllButton(QPoint startPosition, int shift, Direction shiftDirection) : Widget(startPosition, shift, shiftDirection) {
|
|
setCursor(style::cur_pointer);
|
|
|
|
auto position = computePosition(st::notifyHideAllHeight);
|
|
updateGeometry(position.x(), position.y(), st::notifyWidth, st::notifyHideAllHeight);
|
|
hide();
|
|
createWinId();
|
|
|
|
subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &data) {
|
|
if (data.paletteChanged()) {
|
|
update();
|
|
}
|
|
});
|
|
|
|
show();
|
|
}
|
|
|
|
void HideAllButton::startHiding() {
|
|
hideSlow();
|
|
}
|
|
|
|
void HideAllButton::startHidingFast() {
|
|
hideFast();
|
|
}
|
|
|
|
void HideAllButton::stopHiding() {
|
|
hideStop();
|
|
}
|
|
|
|
HideAllButton::~HideAllButton() {
|
|
if (auto manager = ManagerInstance.data()) {
|
|
manager->removeHideAll(this);
|
|
}
|
|
}
|
|
|
|
void HideAllButton::enterEventHook(QEvent *e) {
|
|
_mouseOver = true;
|
|
update();
|
|
}
|
|
|
|
void HideAllButton::leaveEventHook(QEvent *e) {
|
|
_mouseOver = false;
|
|
update();
|
|
}
|
|
|
|
void HideAllButton::mousePressEvent(QMouseEvent *e) {
|
|
_mouseDown = true;
|
|
}
|
|
|
|
void HideAllButton::mouseReleaseEvent(QMouseEvent *e) {
|
|
auto mouseDown = base::take(_mouseDown);
|
|
if (mouseDown && _mouseOver) {
|
|
if (auto manager = ManagerInstance.data()) {
|
|
manager->clearAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
void HideAllButton::paintEvent(QPaintEvent *e) {
|
|
Painter p(this);
|
|
p.setClipRect(e->rect());
|
|
|
|
p.fillRect(rect(), _mouseOver ? st::lightButtonBgOver : st::lightButtonBg);
|
|
p.fillRect(0, 0, width(), st::notifyBorderWidth, st::notifyBorder);
|
|
p.fillRect(0, height() - st::notifyBorderWidth, width(), st::notifyBorderWidth, st::notifyBorder);
|
|
p.fillRect(0, st::notifyBorderWidth, st::notifyBorderWidth, height() - 2 * st::notifyBorderWidth, st::notifyBorder);
|
|
p.fillRect(width() - st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorderWidth, height() - 2 * st::notifyBorderWidth, st::notifyBorder);
|
|
|
|
p.setFont(st::defaultLinkButton.font);
|
|
p.setPen(st::defaultLinkButton.color);
|
|
p.drawText(rect(), lang(lng_notification_hide_all), style::al_center);
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace Default
|
|
} // namespace Notifications
|
|
} // namespace Window
|