Hide all button added to default notifications.

This commit is contained in:
John Preston 2016-10-05 19:56:27 +03:00
parent 043cba0a64
commit 41dc0f4e98
29 changed files with 1021 additions and 591 deletions

View File

@ -249,6 +249,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_settings_include_muted" = "Include muted chats in unread count";
"lng_notification_preview" = "You have a new message";
"lng_notification_reply" = "Reply";
"lng_notification_hide_all" = "Hide all";
"lng_settings_section_general" = "General";
"lng_settings_change_lang" = "Change language";
@ -872,8 +874,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_send_image_too_large" = "Could not send a file, because it is larger than 1.5 GB :(";
"lng_send_folder" = "Could not send «{name}» because it is a directory :(";
"lng_notification_reply" = "Reply";
"lng_forward_choose" = "Choose recipient...";
"lng_forward_cant" = "Sorry, no way to forward here :(";
"lng_forward_confirm" = "Forward to {recipient}?";

View File

@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "core/single_timer.h"
class ApiWrap : public QObject, public RPCSender {
Q_OBJECT

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwindow.h"
#include "pspecific.h"
#include "core/single_timer.h"
class UpdateChecker;
class Application : public QApplication {

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "abstractbox.h"
#include "core/single_timer.h"
namespace Dialogs {
class Row;

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "abstractbox.h"
#include "core/single_timer.h"
class ConfirmBox;

View File

@ -97,9 +97,6 @@ enum {
MediaOverviewStartPerPage = 5,
MediaOverviewPreloadCount = 4,
// a new message from the same sender is attached to previous within 15 minutes
AttachMessageToPreviousSecondsDelta = 900,
AudioSimultaneousLimit = 4,
AudioCheckPositionTimeout = 100, // 100ms per check audio pos
AudioCheckPositionDelta = 2400, // update position called each 2400 samples

View File

@ -20,9 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#ifndef OS_MAC_OLD
#include <memory>
#endif // OS_MAC_OLD
#include "core/stl_subset.h"
namespace base {
namespace internal {
@ -392,191 +390,3 @@ public:
};
} // namespace base
// While we still use rpcDone() and rpcFail()
#include "mtproto/rpc_sender.h"
template <typename FunctionType>
struct LambdaUniqueHelper;
template <typename Lambda, typename R, typename ...Args>
struct LambdaUniqueHelper<R(Lambda::*)(Args...) const> {
using UniqueType = base::lambda_unique<R(Args...)>;
};
template <typename FunctionType>
using LambdaGetUnique = typename LambdaUniqueHelper<FunctionType>::UniqueType;
template <typename Base, typename FunctionType>
class RPCHandlerImplementation : public Base {
protected:
using Lambda = base::lambda_unique<FunctionType>;
using Parent = RPCHandlerImplementation<Base, FunctionType>;
public:
RPCHandlerImplementation(Lambda &&handler) : _handler(std_::move(handler)) {
}
protected:
Lambda _handler;
};
template <typename FunctionType>
using RPCDoneHandlerImplementation = RPCHandlerImplementation<RPCAbstractDoneHandler, FunctionType>;
template <typename R>
class RPCDoneHandlerImplementationBare : public RPCDoneHandlerImplementation<R(const mtpPrime*, const mtpPrime*)> { // done(from, end)
public:
using RPCDoneHandlerImplementation<R(const mtpPrime*, const mtpPrime*)>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler(from, end) : void(0);
}
};
template <typename R>
class RPCDoneHandlerImplementationBareReq : public RPCDoneHandlerImplementation<R(const mtpPrime*, const mtpPrime*, mtpRequestId)> { // done(from, end, req_id)
public:
using RPCDoneHandlerImplementation<R(const mtpPrime*, const mtpPrime*, mtpRequestId)>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler(from, end, requestId) : void(0);
}
};
template <typename R, typename T>
class RPCDoneHandlerImplementationPlain : public RPCDoneHandlerImplementation<R(const T&)> { // done(result)
public:
using RPCDoneHandlerImplementation<R(const T&)>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler(T(from, end)) : void(0);
}
};
template <typename R, typename T>
class RPCDoneHandlerImplementationReq : public RPCDoneHandlerImplementation<R(const T&, mtpRequestId)> { // done(result, req_id)
public:
using RPCDoneHandlerImplementation<R(const T&, mtpRequestId)>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler(T(from, end), requestId) : void(0);
}
};
template <typename R>
class RPCDoneHandlerImplementationNo : public RPCDoneHandlerImplementation<R()> { // done()
public:
using RPCDoneHandlerImplementation<R()>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler() : void(0);
}
};
template <typename R>
class RPCDoneHandlerImplementationNoReq : public RPCDoneHandlerImplementation<R(mtpRequestId)> { // done(req_id)
public:
using RPCDoneHandlerImplementation<R(mtpRequestId)>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler(requestId) : void(0);
}
};
template <typename R>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R(const mtpPrime*, const mtpPrime*)> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBare<R>(std_::move(lambda)));
}
template <typename R>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R(const mtpPrime*, const mtpPrime*, mtpRequestId)> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBareReq<R>(std_::move(lambda)));
}
template <typename R, typename T>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R(const T&)> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationPlain<R, T>(std_::move(lambda)));
}
template <typename R, typename T>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R(const T&, mtpRequestId)> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationReq<R, T>(std_::move(lambda)));
}
template <typename R>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R()> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNo<R>(std_::move(lambda)));
}
template <typename R>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R(mtpRequestId)> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNoReq<R>(std_::move(lambda)));
}
template <typename Lambda, typename = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::value>>
RPCDoneHandlerPtr rpcDone(Lambda &&lambda) {
return rpcDone_lambda_wrap_helper(LambdaGetUnique<decltype(&Lambda::operator())>(std_::move(lambda)));
}
template <typename FunctionType>
using RPCFailHandlerImplementation = RPCHandlerImplementation<RPCAbstractFailHandler, FunctionType>;
class RPCFailHandlerImplementationPlain : public RPCFailHandlerImplementation<bool(const RPCError&)> { // fail(error)
public:
using Parent::Parent;
bool operator()(mtpRequestId requestId, const RPCError &error) const override {
return _handler ? _handler(error) : true;
}
};
class RPCFailHandlerImplementationReq : public RPCFailHandlerImplementation<bool(const RPCError&, mtpRequestId)> { // fail(error, req_id)
public:
using Parent::Parent;
bool operator()(mtpRequestId requestId, const RPCError &error) const override {
return this->_handler ? this->_handler(error, requestId) : true;
}
};
class RPCFailHandlerImplementationNo : public RPCFailHandlerImplementation<bool()> { // fail()
public:
using Parent::Parent;
bool operator()(mtpRequestId requestId, const RPCError &error) const override {
return this->_handler ? this->_handler() : true;
}
};
class RPCFailHandlerImplementationNoReq : public RPCFailHandlerImplementation<bool(mtpRequestId)> { // fail(req_id)
public:
using Parent::Parent;
bool operator()(mtpRequestId requestId, const RPCError &error) const override {
return this->_handler ? this->_handler(requestId) : true;
}
};
inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_unique<bool(const RPCError&)> &&lambda) {
return RPCFailHandlerPtr(new RPCFailHandlerImplementationPlain(std_::move(lambda)));
}
inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_unique<bool(const RPCError&, mtpRequestId)> &&lambda) {
return RPCFailHandlerPtr(new RPCFailHandlerImplementationReq(std_::move(lambda)));
}
inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_unique<bool()> &&lambda) {
return RPCFailHandlerPtr(new RPCFailHandlerImplementationNo(std_::move(lambda)));
}
inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_unique<bool(mtpRequestId)> &&lambda) {
return RPCFailHandlerPtr(new RPCFailHandlerImplementationNoReq(std_::move(lambda)));
}
template <typename Lambda, typename = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::value>>
RPCFailHandlerPtr rpcFail(Lambda &&lambda) {
return rpcFail_lambda_wrap_helper(LambdaGetUnique<decltype(&Lambda::operator())>(std_::move(lambda)));
}

View File

@ -0,0 +1,80 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "core/single_timer.h"
#include "application.h"
SingleTimer::SingleTimer() {
QTimer::setSingleShot(true);
if (App::app()) {
connect(App::app(), SIGNAL(adjustSingleTimers()), this, SLOT(adjust()));
_inited = true;
}
}
void SingleTimer::setTimeoutHandler(base::lambda_unique<void()> &&handler) {
if (_handler && !handler) {
disconnect(this, SIGNAL(timeout()), this, SLOT(onTimeout()));
} else if (handler && !_handler) {
connect(this, SIGNAL(timeout()), this, SLOT(onTimeout()));
}
_handler = std_::move(handler);
}
void SingleTimer::adjust() {
uint64 n = getms(true);
if (isActive()) {
if (n >= _finishing) {
start(0);
} else {
start(_finishing - n);
}
}
}
void SingleTimer::onTimeout() {
if (_handler) {
_handler();
}
}
void SingleTimer::start(int msec) {
_finishing = getms(true) + (msec < 0 ? 0 : uint64(msec));
if (!_inited && App::app()) {
connect(App::app(), SIGNAL(adjustSingleTimers()), this, SLOT(adjust()));
_inited = true;
}
QTimer::start(msec);
}
void SingleTimer::startIfNotActive(int msec) {
if (isActive()) {
int remains = remainingTime();
if (remains > msec) {
start(msec);
} else if (!remains) {
start(1);
}
} else {
start(msec);
}
}

View File

@ -0,0 +1,50 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "core/basic_types.h"
#include "core/lambda_wrap.h"
class SingleTimer : public QTimer { // single shot timer with check
Q_OBJECT
public:
SingleTimer();
void setSingleShot(bool); // is not available
void start(); // is not available
void setTimeoutHandler(base::lambda_unique<void()> &&handler);
public slots:
void start(int msec);
void startIfNotActive(int msec);
private slots:
void adjust();
void onTimeout();
private:
uint64 _finishing = 0;
bool _inited = false;
base::lambda_unique<void()> _handler;
};

View File

@ -345,36 +345,6 @@ uint64 getms(bool checked) {
#endif
}
SingleTimer::SingleTimer() : _finishing(0), _inited(false) {
QTimer::setSingleShot(true);
if (App::app()) {
connect(App::app(), SIGNAL(adjustSingleTimers()), this, SLOT(adjust()));
_inited = true;
}
}
void SingleTimer::start(int msec) {
_finishing = getms(true) + (msec < 0 ? 0 : uint64(msec));
if (!_inited && App::app()) {
connect(App::app(), SIGNAL(adjustSingleTimers()), this, SLOT(adjust()));
_inited = true;
}
QTimer::start(msec);
}
void SingleTimer::startIfNotActive(int msec) {
if (isActive()) {
int remains = remainingTime();
if (remains > msec) {
start(msec);
} else if (!remains) {
start(1);
}
} else {
start(msec);
}
}
uint64 msgid() {
#ifdef Q_OS_WIN
LARGE_INTEGER li;

View File

@ -172,37 +172,6 @@ void finish();
bool checkms(); // returns true if time has changed
uint64 getms(bool checked = false);
class SingleTimer : public QTimer { // single shot timer with check
Q_OBJECT
public:
SingleTimer();
void setSingleShot(bool); // is not available
void start(); // is not available
public slots:
void start(int msec);
void startIfNotActive(int msec);
void adjust() {
uint64 n = getms(true);
if (isActive()) {
if (n >= _finishing) {
start(0);
} else {
start(_finishing - n);
}
}
}
private:
uint64 _finishing;
bool _inited;
};
const static uint32 _md5_block_size = 64;
class HashMd5 {
public:

View File

@ -28,6 +28,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_dialogs.h"
#include "fileuploader.h"
namespace {
// a new message from the same sender is attached to previous within 15 minutes
constexpr int kAttachMessageToPreviousSecondsDelta = 900;
} // namespace
ReplyMarkupClickHandler::ReplyMarkupClickHandler(const HistoryItem *item, int row, int col)
: _itemId(item->fullId())
, _row(row)
@ -629,7 +636,7 @@ void HistoryItem::recountAttachToPrevious() {
&& !previos->serviceMsg()
&& !previos->isEmpty()
&& previos->from() == from()
&& (qAbs(previos->date.secsTo(date)) < AttachMessageToPreviousSecondsDelta);
&& (qAbs(previos->date.secsTo(date)) < kAttachMessageToPreviousSecondsDelta);
}
}
if (attach && !(_flags & MTPDmessage_ClientFlag::f_attach_to_previous)) {

View File

@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "history/history_common.h"
#include "history/field_autocomplete.h"
#include "window/section_widget.h"
#include "core/single_timer.h"
namespace InlineBots {
namespace Layout {

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localimageloader.h"
#include "history/history_common.h"
#include "core/single_timer.h"
namespace Dialogs {
class Row;

View File

@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "pspecific.h"
#include "ui/effects/rect_shadow.h"
#include "platform/platform_main_window.h"
#include "core/single_timer.h"
class MediaView;
class TitleWidget;
@ -41,14 +42,6 @@ namespace Settings {
class Widget;
} // namespace Settings
namespace Window {
namespace Notifications {
namespace Default {
class Widget;
} // namespace Default
} // namespace Notifications
} // namespace Window
class ConnectingWidget : public QWidget {
Q_OBJECT

View File

@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mtproto/core_types.h"
#include "mtproto/auth_key.h"
#include "mtproto/connection_abstract.h"
#include "core/single_timer.h"
namespace MTP {
namespace internal {

View File

@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "core/single_timer.h"
namespace MTP {
namespace internal {
@ -27,7 +29,6 @@ class Dcenter : public QObject {
Q_OBJECT
public:
Dcenter(int32 id, const AuthKeyPtr &key);
QReadWriteLock *keyMutex() const;
@ -46,21 +47,19 @@ public:
}
signals:
void authKeyCreated();
void layerWasInited(bool was);
private slots:
void authKeyWrite();
private:
mutable QReadWriteLock keyLock;
mutable QMutex initLock;
int32 _id;
AuthKeyPtr _key;
bool _connectionInited;
};
typedef QSharedPointer<Dcenter> DcenterPtr;
@ -70,21 +69,17 @@ class ConfigLoader : public QObject {
Q_OBJECT
public:
ConfigLoader();
void load();
void done();
public slots:
void enumDC();
signals:
void loaded();
private:
SingleTimer _enumDCTimer;
int32 _enumCurrent;
mtpRequestId _enumRequest;

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mtproto/core_types.h"
#include "mtproto/session.h"
#include "core/single_timer.h"
namespace MTP {
@ -57,17 +58,15 @@ class GlobalSlotCarrier : public QObject {
Q_OBJECT
public:
GlobalSlotCarrier();
public slots:
void checkDelayed();
void connectionFinished(Connection *connection);
private:
SingleTimer _timer;
};
GlobalSlotCarrier *globalSlotCarrier();

View File

@ -20,9 +20,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "core/lambda_wrap.h"
class RPCError {
public:
RPCError(const MTPrpcError &error) : _code(error.c_rpc_error().verror_code.v) {
QString text = qs(error.c_rpc_error().verror_message);
if (_code < 0 || _code >= 500) {
@ -827,3 +828,187 @@ protected:
typedef void (*MTPStateChangedHandler)(int32 dcId, int32 state);
typedef void(*MTPSessionResetHandler)(int32 dcId);
template <typename FunctionType>
struct LambdaUniqueHelper;
template <typename Lambda, typename R, typename ...Args>
struct LambdaUniqueHelper<R(Lambda::*)(Args...) const> {
using UniqueType = base::lambda_unique<R(Args...)>;
};
template <typename FunctionType>
using LambdaGetUnique = typename LambdaUniqueHelper<FunctionType>::UniqueType;
template <typename Base, typename FunctionType>
class RPCHandlerImplementation : public Base {
protected:
using Lambda = base::lambda_unique<FunctionType>;
using Parent = RPCHandlerImplementation<Base, FunctionType>;
public:
RPCHandlerImplementation(Lambda &&handler) : _handler(std_::move(handler)) {
}
protected:
Lambda _handler;
};
template <typename FunctionType>
using RPCDoneHandlerImplementation = RPCHandlerImplementation<RPCAbstractDoneHandler, FunctionType>;
template <typename R>
class RPCDoneHandlerImplementationBare : public RPCDoneHandlerImplementation<R(const mtpPrime*, const mtpPrime*)> { // done(from, end)
public:
using RPCDoneHandlerImplementation<R(const mtpPrime*, const mtpPrime*)>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler(from, end) : void(0);
}
};
template <typename R>
class RPCDoneHandlerImplementationBareReq : public RPCDoneHandlerImplementation<R(const mtpPrime*, const mtpPrime*, mtpRequestId)> { // done(from, end, req_id)
public:
using RPCDoneHandlerImplementation<R(const mtpPrime*, const mtpPrime*, mtpRequestId)>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler(from, end, requestId) : void(0);
}
};
template <typename R, typename T>
class RPCDoneHandlerImplementationPlain : public RPCDoneHandlerImplementation<R(const T&)> { // done(result)
public:
using RPCDoneHandlerImplementation<R(const T&)>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler(T(from, end)) : void(0);
}
};
template <typename R, typename T>
class RPCDoneHandlerImplementationReq : public RPCDoneHandlerImplementation<R(const T&, mtpRequestId)> { // done(result, req_id)
public:
using RPCDoneHandlerImplementation<R(const T&, mtpRequestId)>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler(T(from, end), requestId) : void(0);
}
};
template <typename R>
class RPCDoneHandlerImplementationNo : public RPCDoneHandlerImplementation<R()> { // done()
public:
using RPCDoneHandlerImplementation<R()>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler() : void(0);
}
};
template <typename R>
class RPCDoneHandlerImplementationNoReq : public RPCDoneHandlerImplementation<R(mtpRequestId)> { // done(req_id)
public:
using RPCDoneHandlerImplementation<R(mtpRequestId)>::Parent::Parent;
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const override {
return this->_handler ? this->_handler(requestId) : void(0);
}
};
template <typename R>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R(const mtpPrime*, const mtpPrime*)> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBare<R>(std_::move(lambda)));
}
template <typename R>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R(const mtpPrime*, const mtpPrime*, mtpRequestId)> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBareReq<R>(std_::move(lambda)));
}
template <typename R, typename T>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R(const T&)> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationPlain<R, T>(std_::move(lambda)));
}
template <typename R, typename T>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R(const T&, mtpRequestId)> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationReq<R, T>(std_::move(lambda)));
}
template <typename R>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R()> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNo<R>(std_::move(lambda)));
}
template <typename R>
inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_unique<R(mtpRequestId)> &&lambda) {
return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNoReq<R>(std_::move(lambda)));
}
template <typename Lambda, typename = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::value>>
RPCDoneHandlerPtr rpcDone(Lambda &&lambda) {
return rpcDone_lambda_wrap_helper(LambdaGetUnique<decltype(&Lambda::operator())>(std_::move(lambda)));
}
template <typename FunctionType>
using RPCFailHandlerImplementation = RPCHandlerImplementation<RPCAbstractFailHandler, FunctionType>;
class RPCFailHandlerImplementationPlain : public RPCFailHandlerImplementation<bool(const RPCError&)> { // fail(error)
public:
using Parent::Parent;
bool operator()(mtpRequestId requestId, const RPCError &error) const override {
return _handler ? _handler(error) : true;
}
};
class RPCFailHandlerImplementationReq : public RPCFailHandlerImplementation<bool(const RPCError&, mtpRequestId)> { // fail(error, req_id)
public:
using Parent::Parent;
bool operator()(mtpRequestId requestId, const RPCError &error) const override {
return this->_handler ? this->_handler(error, requestId) : true;
}
};
class RPCFailHandlerImplementationNo : public RPCFailHandlerImplementation<bool()> { // fail()
public:
using Parent::Parent;
bool operator()(mtpRequestId requestId, const RPCError &error) const override {
return this->_handler ? this->_handler() : true;
}
};
class RPCFailHandlerImplementationNoReq : public RPCFailHandlerImplementation<bool(mtpRequestId)> { // fail(req_id)
public:
using Parent::Parent;
bool operator()(mtpRequestId requestId, const RPCError &error) const override {
return this->_handler ? this->_handler(requestId) : true;
}
};
inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_unique<bool(const RPCError&)> &&lambda) {
return RPCFailHandlerPtr(new RPCFailHandlerImplementationPlain(std_::move(lambda)));
}
inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_unique<bool(const RPCError&, mtpRequestId)> &&lambda) {
return RPCFailHandlerPtr(new RPCFailHandlerImplementationReq(std_::move(lambda)));
}
inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_unique<bool()> &&lambda) {
return RPCFailHandlerPtr(new RPCFailHandlerImplementationNo(std_::move(lambda)));
}
inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_unique<bool(mtpRequestId)> &&lambda) {
return RPCFailHandlerPtr(new RPCFailHandlerImplementationNoReq(std_::move(lambda)));
}
template <typename Lambda, typename = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::value>>
RPCFailHandlerPtr rpcFail(Lambda &&lambda) {
return rpcFail_lambda_wrap_helper(LambdaGetUnique<decltype(&Lambda::operator())>(std_::move(lambda)));
}

View File

@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mtproto/connection.h"
#include "mtproto/dcenter.h"
#include "mtproto/rpc_sender.h"
#include "core/single_timer.h"
namespace MTP {
namespace internal {
@ -31,7 +32,6 @@ class Session;
class SessionData {
public:
SessionData(Session *creator)
: _session(0)
, _salt(0)
@ -227,7 +227,6 @@ class Session : public QObject {
Q_OBJECT
public:
Session(int32 dcenter);
void restart();
@ -237,7 +236,6 @@ public:
void unpaused();
int32 getDcWithShift() const;
~Session();
QReadWriteLock *keyMutex() const;
void notifyKeyCreated(const AuthKeyPtr &key);
@ -255,15 +253,15 @@ public:
void sendPrepared(const mtpRequest &request, uint64 msCanWait = 0, bool newRequest = true); // nulls msgId and seqNo in request, if newRequest = true
signals:
~Session();
signals:
void authKeyCreated();
void needToSend();
void needToPing();
void needToRestart();
public slots:
void needToResumeAndSend();
mtpRequestId resend(quint64 msgId, quint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
@ -283,7 +281,6 @@ public slots:
void sendMsgsStateInfo(quint64 msgId, QByteArray data);
private:
Connection *_connection;
bool _killed;

View File

@ -39,7 +39,7 @@ namespace Platform {
void MacPrivate::activeSpaceChanged() {
if (auto manager = Window::Notifications::Default::manager()) {
manager->enumerateWidgets([](QWidget *widget) {
manager->enumerateNotifications([](QWidget *widget) {
objc_activateWnd(widget->winId());
});
}

View File

@ -106,13 +106,12 @@ namespace anim {
public:
using ValueType = float64;
fvalue() {
fvalue() = default;
fvalue(float64 from) : _cur(from), _from(from) {
}
fvalue(const float64 &from) : _cur(from), _from(from), _delta(0) {
fvalue(float64 from, float64 to) : _cur(from), _from(from), _delta(to - from) {
}
fvalue(const float64 &from, const float64 &to) : _cur(from), _from(from), _delta(to - from) {
}
void start(const float64 &to) {
void start(float64 to) {
_from = _cur;
_delta = to - _from;
}
@ -120,13 +119,21 @@ namespace anim {
_delta = _from + _delta - _cur;
_from = _cur;
}
const float64 &current() const {
float64 from() const {
return _from;
}
float64 current() const {
return _cur;
}
float64 to() const {
return _from + _delta;
}
fvalue &update(const float64 &dt, transition func) {
void add(float64 delta) {
_from += delta;
_cur += delta;
}
fvalue &update(float64 dt, transition func) {
_cur = _from + (*func)(_delta, dt);
return *this;
}
@ -137,19 +144,20 @@ namespace anim {
}
private:
float64 _cur, _from, _delta;
float64 _cur = 0.;
float64 _from = 0.;
float64 _delta = 0.;
};
class ivalue { // int animated value
public:
using ValueType = int32;
using ValueType = int;
ivalue() {
ivalue() = default;
ivalue(int from) : _cur(from), _from(float64(from)) {
}
ivalue(int32 from) : _cur(from), _from(float64(from)), _delta(0) {
}
ivalue(int32 from, int32 to) : _cur(from), _from(float64(from)), _delta(float64(to - from)) {
ivalue(int from, int to) : _cur(from), _from(float64(from)), _delta(float64(to - from)) {
}
void start(int32 to) {
_from = float64(_cur);
@ -159,13 +167,21 @@ namespace anim {
_delta = _from + _delta - float64(_cur);
_from = float64(_cur);
}
int32 current() const {
int from() const {
return _from;
}
int current() const {
return _cur;
}
int32 to() const {
return _from + _delta;
int to() const {
return qRound(_from + _delta);
}
ivalue &update(const float64 &dt, transition func) {
void add(int delta) {
_from += delta;
_cur += delta;
}
ivalue &update(float64 dt, transition func) {
_cur = qRound(_from + (*func)(_delta, dt));
return *this;
}
@ -176,8 +192,9 @@ namespace anim {
}
private:
int32 _cur;
float64 _from, _delta;
int _cur = 0;
float64 _from = 0.;
float64 _delta = 0.;
};
@ -185,15 +202,24 @@ namespace anim {
public:
using ValueType = QColor;
cvalue() {
}
cvalue(const QColor &from) : _cur(from), _from_r(from.redF()), _from_g(from.greenF()), _from_b(from.blueF()), _from_a(from.alphaF()), _delta_r(0), _delta_g(0), _delta_b(0), _delta_a(0) {
cvalue() = default;
cvalue(const QColor &from)
: _cur(from)
, _from_r(from.redF())
, _from_g(from.greenF())
, _from_b(from.blueF())
, _from_a(from.alphaF()) {
}
cvalue(const QColor &from, const QColor &to)
: _cur(from)
, _from_r(from.redF()), _from_g(from.greenF()), _from_b(from.blueF()), _from_a(from.alphaF())
, _delta_r(to.redF() - from.redF()), _delta_g(to.greenF() - from.greenF()), _delta_b(to.blueF() - from.blueF()), _delta_a(to.alphaF() - from.alphaF())
{
, _from_r(from.redF())
, _from_g(from.greenF())
, _from_b(from.blueF())
, _from_a(from.alphaF())
, _delta_r(to.redF() - from.redF())
, _delta_g(to.greenF() - from.greenF())
, _delta_b(to.blueF() - from.blueF())
, _delta_a(to.alphaF() - from.alphaF()) {
}
void start(const QColor &to) {
_from_r = _cur.redF();
@ -215,6 +241,14 @@ namespace anim {
_from_b = _cur.blueF();
_from_a = _cur.alphaF();
}
QColor from() const {
QColor result;
result.setRedF(_from_r);
result.setGreenF(_from_g);
result.setBlueF(_from_b);
result.setAlphaF(_from_a);
return result;
}
const QColor &current() const {
return _cur;
}
@ -247,7 +281,14 @@ namespace anim {
private:
QColor _cur;
float64 _from_r, _from_g, _from_b, _from_a, _delta_r, _delta_g, _delta_b, _delta_a;
float64 _from_r = 0.;
float64 _from_g = 0.;
float64 _from_b = 0.;
float64 _from_a = 0.;
float64 _delta_r = 0.;
float64 _delta_g = 0.;
float64 _delta_b = 0.;
float64 _delta_a = 0.;
};

View File

@ -735,7 +735,7 @@ void InputArea::paintEvent(QPaintEvent *e) {
Painter p(this);
QRect r(rect().intersected(e->rect()));
p.fillRect(r, st::white->b);
p.fillRect(r, st::white);
if (_st.border) {
p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg->b);
}

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "ui/toast/toast.h"
#include "core/single_timer.h"
namespace Ui {
namespace Toast {

View File

@ -49,6 +49,10 @@ int notificationWidth() {
return result;
}
int notificationMaxHeight() {
return st::notifyMinHeight + st::notifyReplyArea.heightMax + st::notifyBorderWidth;
}
} // namespace
void start() {
@ -65,69 +69,173 @@ void finish() {
Manager::Manager() {
subscribe(FileDownload::ImageLoaded(), [this] {
for_const (auto widget, _widgets) {
widget->updatePeerPhoto();
for_const (auto notification, _notifications) {
notification->updatePeerPhoto();
}
});
_inputCheckTimer.setTimeoutHandler([this] { checkLastInput(); });
}
void Manager::checkLastInput() {
auto waiting = false;
for_const (auto notification, _notifications) {
if (!notification->checkLastInput()) {
waiting = true;
}
}
if (waiting) {
_inputCheckTimer.start(300);
}
}
void Manager::startAllHiding() {
for_const (auto widget, _widgets) {
widget->startHiding();
auto hasReplyingNotification = false;
for_const (auto notification, _notifications) {
if (notification->isReplying()) {
hasReplyingNotification = true;
break;
}
}
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 widget, _widgets) {
widget->stopHiding();
for_const (auto notification, _notifications) {
notification->stopHiding();
}
if (_hideAll) {
_hideAll->stopHiding();
}
}
void Manager::showNextFromQueue() {
if (_queuedNotifications.isEmpty()) {
return;
if (!_queuedNotifications.isEmpty()) {
int count = kNotifyWindowsCount;
for_const (auto notification, _notifications) {
if (notification->isUnlinked()) continue;
--count;
}
if (count > 0) {
auto position = notificationStartPosition();
do {
auto queued = _queuedNotifications.front();
_queuedNotifications.pop_front();
auto notification = std_::make_unique<Notification>(
queued.history,
queued.peer,
queued.author,
queued.item,
queued.forwardedCount,
position);
Platform::Notifications::defaultNotificationShown(notification.get());
_notifications.push_back(notification.release());
--count;
} while (count > 0 && !_queuedNotifications.isEmpty());
_positionsOutdated = true;
checkLastInput();
}
}
int count = kNotifyWindowsCount;
for_const (auto widget, _widgets) {
if (widget->isUnlinked()) continue;
--count;
}
if (count <= 0) {
return;
}
auto r = psDesktopRect();
auto x = r.x() + r.width() - notificationWidth() - st::notifyDeltaX;
auto y = r.y() + r.height();
do {
auto queued = _queuedNotifications.front();
_queuedNotifications.pop_front();
auto widget = std_::make_unique<Widget>(queued.history, queued.peer, queued.author, queued.item, queued.forwardedCount, x, y);
Platform::Notifications::defaultNotificationShown(widget.get());
_widgets.push_back(widget.release());
--count;
} while (count > 0 && !_queuedNotifications.isEmpty());
auto bottom = y - st::notifyDeltaY;
for_const (auto widget, _widgets) {
if (widget->isUnlinked() < 0) continue;
widget->moveTop(y - widget->height(), y);
y -= widget->height() + st::notifyDeltaY;
if (_positionsOutdated) {
moveWidgets();
}
}
void Manager::removeFromShown(Widget *remove) {
QPoint Manager::notificationStartPosition() const {
auto r = psDesktopRect();
auto x = r.x() + r.width() - notificationWidth() - st::notifyDeltaX;
auto y = r.y() + r.height();
return QPoint(x, y);
}
void Manager::moveWidgets() {
auto startPosition = notificationStartPosition();
auto top = startPosition.y();
int firstLeft = 0, firstTopCurrent = 0, firstTop = 0, count = 0;
for (int i = _notifications.size(); i != 0;) {
auto notification = _notifications[--i];
if (notification->isUnlinked()) continue;
top -= notification->height() + st::notifyDeltaY;
notification->moveTop(top);
firstLeft = notification->x();
firstTopCurrent = notification->y();
firstTop = top;
++count;
}
if (count > 1 || !_queuedNotifications.isEmpty()) {
auto deltaY = st::notifyHideAll.height + st::notifyDeltaY;
if (!_hideAll) {
_hideAll = new HideAllButton(QPoint(firstLeft, firstTopCurrent - deltaY));
}
_hideAll->moveTop(firstTop - deltaY);
_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, Notification::AddToHeight::Above);
auto index = _notifications.indexOf(notification);
if (index > 0) {
for (int i = 0; i != index; ++i) {
auto notification = _notifications[i];
if (notification->isUnlinked()) continue;
notification->addToTop(-deltaHeight);
}
}
if (_hideAll) {
_hideAll->addToTop(-deltaHeight);
}
}
void Manager::unlinkFromShown(Notification *remove) {
if (remove) {
auto index = _widgets.indexOf(remove);
if (index >= 0) {
_widgets.removeAt(index);
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();
@ -135,18 +243,19 @@ void Manager::doShowNotification(HistoryItem *item, int forwardedCount) {
void Manager::doClearAll() {
_queuedNotifications.clear();
for_const (auto widget, _widgets) {
widget->unlinkHistory();
for_const (auto notification, _notifications) {
notification->unlinkHistory();
}
showNextFromQueue();
}
void Manager::doClearAllFast() {
_queuedNotifications.clear();
auto widgets = createAndSwap(_widgets);
for_const (auto widget, widgets) {
widget->deleteLater();
auto notifications = createAndSwap(_notifications);
for_const (auto notification, notifications) {
notification->deleteLater();
}
showNextFromQueue();
}
void Manager::doClearFromHistory(History *history) {
@ -157,21 +266,24 @@ void Manager::doClearFromHistory(History *history) {
++i;
}
}
for_const (auto widget, _widgets) {
widget->unlinkHistory(history);
for_const (auto notification, _notifications) {
if (notification->unlinkHistory(history)) {
_positionsOutdated = true;
}
}
showNextFromQueue();
}
void Manager::doClearFromItem(HistoryItem *item) {
for_const (auto widget, _widgets) {
widget->itemRemoved(item);
for_const (auto notification, _notifications) {
// Calls unlinkFromShown() -> showNextFromQueue()
notification->itemRemoved(item);
}
}
void Manager::doUpdateAll() {
for_const (auto widget, _widgets) {
widget->updateNotifyDisplay();
for_const (auto notification, _notifications) {
notification->updateNotifyDisplay();
}
}
@ -179,7 +291,116 @@ Manager::~Manager() {
clearAllFast();
}
Widget::Widget(History *history, PeerData *peer, PeerData *author, HistoryItem *msg, int forwardedCount, int x, int y) : TWidget(nullptr)
namespace internal {
Widget::Widget(QPoint position) : TWidget(nullptr)
, _opacityDuration(st::notifyFastAnim)
, a_opacity(0, 1)
, a_func(anim::linear)
, _a_appearance(animation(this, &Widget::step_appearance))
, a_top(position.y())
, _a_movement(animation(this, &Notification::step_movement)) {
setWindowOpacity(0.);
setAttribute(Qt::WA_OpaquePaintEvent);
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
_a_appearance.start();
}
void Widget::step_appearance(float64 ms, bool timer) {
float64 dt = ms / float64(_opacityDuration);
if (dt >= 1) {
a_opacity.finish();
_a_appearance.stop();
if (_hiding) {
deleteLater();
}
} else {
a_opacity.update(dt, a_func);
}
setWindowOpacity(a_opacity.current());
update();
}
void Widget::step_movement(float64 ms, bool timer) {
float64 dt = ms / float64(st::notifyFastAnim);
if (dt >= 1) {
a_top.finish();
} else {
a_top.update(dt, anim::linear);
}
move(x(), a_top.current());
update();
}
void Widget::hideSlow() {
animHide(st::notifySlowHide, st::notifySlowHideFunc);
}
void Widget::hideFast() {
animHide(st::notifyFastAnim, anim::linear);
}
void Widget::hideStop() {
if (_hiding) {
_opacityDuration = st::notifyFastAnim;
a_func = anim::linear;
a_opacity.start(1);
_hiding = false;
_a_appearance.start();
}
}
void Widget::animHide(float64 duration, anim::transition func) {
_opacityDuration = duration;
a_func = func;
a_opacity.start(0);
_hiding = true;
_a_appearance.start();
}
void Widget::moveTop(int top) {
a_top.start(top);
_a_movement.start();
}
void Widget::addToHeight(int add, AddToHeight aboveOrBelow) {
int newHeight = height() + add;
if (aboveOrBelow == AddToHeight::Above) {
a_top.add(-add);
}
updateGeometry(x(), a_top.current(), width(), newHeight);
}
void Widget::updateGeometry(int x, int y, int width, int height) {
setGeometry(x, y, width, height);
update();
}
void Widget::addToTop(int add) {
a_top.add(add);
move(x(), a_top.current());
}
Background::Background(QWidget *parent) : TWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
}
void Background::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(rect(), st::notifyBg);
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 position) : Widget(position)
, _history(history)
, _peer(peer)
, _author(author)
@ -189,26 +410,17 @@ Widget::Widget(History *history, PeerData *peer, PeerData *author, HistoryItem *
, _started(GetTickCount())
#endif // Q_OS_WIN && !Q_OS_WINRT
, _close(this, st::notifyClose)
, _reply(this, lang(lng_notification_reply), st::defaultBoxButton)
, _alphaDuration(st::notifyFastAnim)
, a_opacity(0)
, a_func(anim::linear)
, a_top(y)
, a_bottom(y + st::notifyMinHeight)
, _a_movement(animation(this, &Widget::step_movement))
, _a_appearance(animation(this, &Widget::step_appearance)) {
setGeometry(x, a_top.current(), notificationWidth(), a_bottom.current() - a_top.current());
, _reply(this, lang(lng_notification_reply), st::defaultBoxButton) {
updateGeometry(position.x(), position.y(), notificationWidth(), st::notifyMinHeight);
_userpicLoaded = _peer ? _peer->userpicLoaded() : true;
updateNotifyDisplay();
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideByTimer()));
_inputTimer.setSingleShot(true);
connect(&_inputTimer, SIGNAL(timeout()), this, SLOT(checkLastInput()));
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideByTimer()));
_close->setClickedCallback([this] {
unlinkHistoryAndNotify();
unlinkHistoryInManager();
});
_close->setAcceptBoth(true);
_close->moveToRight(st::notifyClosePos.x(), st::notifyClosePos.y());
@ -223,21 +435,15 @@ Widget::Widget(History *history, PeerData *peer, PeerData *author, HistoryItem *
prepareActionsCache();
a_opacity.start(1);
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
show();
setWindowOpacity(a_opacity.current());
_alphaDuration = st::notifyFastAnim;
_a_appearance.start();
checkLastInput();
}
void Widget::prepareActionsCache() {
void Notification::prepareActionsCache() {
auto replyCache = myGrab(_reply);
auto fadeWidth = st::notifyFadeRight.width();
auto actionsTop = st::notifyTextTop + st::msgNameFont->height;
@ -254,53 +460,73 @@ void Widget::prepareActionsCache() {
_buttonsCache = App::pixmapFromImageInPlace(std_::move(actionsCacheImg));
}
void Widget::checkLastInput() {
bool Notification::checkLastInput() {
if (!_waitingForInput) return true;
auto wasUserInput = true; // TODO
#if defined Q_OS_WIN && !defined Q_OS_WINRT
LASTINPUTINFO lii;
lii.cbSize = sizeof(LASTINPUTINFO);
BOOL res = GetLastInputInfo(&lii);
if (!res || lii.dwTime >= _started) {
_hideTimer.start(st::notifyWaitLongHide);
} else {
_inputTimer.start(300);
wasUserInput = (!res || lii.dwTime >= _started);
#endif // Q_OS_WIN && !Q_OS_WINRT
if (wasUserInput) {
_waitingForInput = false;
if (!isReplying()) {
_hideTimer.start(st::notifyWaitLongHide);
}
return true;
}
#else // Q_OS_WIN && !Q_OS_WINRT
// TODO
if (true) {
_hideTimer.start(st::notifyWaitLongHide);
} else {
_inputTimer.start(300);
}
#endif // else for Q_OS_WIN && !Q_OS_WINRT
return false;
}
void Widget::onReplyResize() {
_a_movement.stop();
auto newHeight = st::notifyMinHeight + _replyArea->height() + st::notifyBorderWidth;
auto delta = height() - newHeight;
if (delta != 0) {
auto top = a_top.current() + delta;
a_top = anim::ivalue(top, top);
setGeometry(x(), top, width(), a_bottom.current() - top);
update();
}
void Notification::onReplyResize() {
changeHeight(st::notifyMinHeight + _replyArea->height() + st::notifyBorderWidth);
}
void Widget::onReplySubmit(bool ctrlShiftEnter) {
void Notification::onReplySubmit(bool ctrlShiftEnter) {
sendReply();
}
void Widget::onReplyCancel() {
unlinkHistoryAndNotify();
void Notification::onReplyCancel() {
unlinkHistoryInManager();
}
void Widget::moveTop(int top, int bottom) {
a_top.start(top);
a_bottom.start(bottom);
_a_movement.start();
void Notification::updateGeometry(int x, int y, int width, int height) {
if (height > st::notifyMinHeight) {
if (!_background) {
_background = new Background(this);
}
_background->setGeometry(0, st::notifyMinHeight, width, height - st::notifyMinHeight);
} else if (_background) {
_background.destroy();
}
Widget::updateGeometry(x, y, width, height);
}
void Widget::updateNotifyDisplay() {
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;
int32 w = width(), h = height();
@ -372,50 +598,48 @@ void Widget::updateNotifyDisplay() {
update();
}
void Widget::updatePeerPhoto() {
if (!peerPhoto->isNull() && peerPhoto->loaded()) {
auto img = _cache.toImage();
{
Painter p(&img);
p.drawPixmap(st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), peerPhoto->pix(st::notifyPhotoSize));
}
peerPhoto = ImagePtr();
_cache = App::pixmapFromImageInPlace(std_::move(img));
update();
void Notification::updatePeerPhoto() {
if (_userpicLoaded || !_peer || !_peer->userpicLoaded()) {
return;
}
_userpicLoaded = true;
auto img = _cache.toImage();
{
Painter p(&img);
_peer->paintUserpicLeft(p, st::notifyPhotoSize, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width());
}
_cache = App::pixmapFromImageInPlace(std_::move(img));
}
void Widget::itemRemoved(HistoryItem *deleted) {
void Notification::itemRemoved(HistoryItem *deleted) {
if (_item && _item == deleted) {
_item = nullptr;
unlinkHistoryAndNotify();
unlinkHistoryInManager();
}
}
void Widget::unlinkHistoryAndNotify() {
unlinkHistory();
void Notification::unlinkHistoryInManager() {
if (auto manager = ManagerInstance.data()) {
manager->showNextFromQueue();
manager->unlinkFromShown(this);
}
}
void Widget::toggleActionButtons(bool visible) {
void Notification::toggleActionButtons(bool visible) {
if (_actionsVisible != visible) {
_actionsVisible = visible;
_reply->hide();
a_actionsOpacity.start([this] { actionsOpacityCallback(); }, _actionsVisible ? 0. : 1., _actionsVisible ? 1. : 0., st::notifyActionsDuration);
_reply->hide();
}
}
void Widget::actionsOpacityCallback() {
update();
if (!a_actionsOpacity.animating() && _actionsVisible) {
_reply->show();
}
}
void Widget::showReplyField() {
void Notification::showReplyField() {
if (_replyArea) return;
stopHiding();
_background = new Background(this);
_background->setGeometry(0, st::notifyMinHeight, width(), st::notifySendReply.height + st::notifyBorderWidth);
_background->show();
_replyArea = new InputArea(this, st::notifyReplyArea, lang(lng_message_ph), QString());
_replyArea->resize(width() - st::notifySendReply.width - 2 * st::notifyBorderWidth, st::notifySendReply.height);
@ -435,37 +659,42 @@ void Widget::showReplyField() {
toggleActionButtons(false);
a_top.finish();
a_bottom.finish();
_a_movement.stop();
auto top = a_top.current() - _replyArea->height() - st::notifyBorderWidth;
auto bottom = a_top.current() + st::notifyMinHeight;
a_top = anim::ivalue(top, top);
a_bottom = anim::ivalue(bottom, bottom);
setGeometry(x(), top, width(), bottom - top);
onReplyResize();
update();
}
void Widget::sendReply() {
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 Widget::unlinkHistory(History *history) {
if (!history || history == _history) {
animHide(st::notifyFastAnim, anim::linear);
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) {
if (_history->peer->id != 4456802837) {
int a = 0;
}
hideFast();
_history = nullptr;
_item = nullptr;
}
return unlink;
}
void Widget::enterEvent(QEvent *e) {
void Notification::enterEvent(QEvent *e) {
if (!_history) return;
if (auto manager = ManagerInstance.data()) {
manager->stopAllHiding();
@ -475,7 +704,7 @@ void Widget::enterEvent(QEvent *e) {
}
}
void Widget::leaveEvent(QEvent *e) {
void Notification::leaveEvent(QEvent *e) {
if (!_history) return;
if (auto manager = ManagerInstance.data()) {
manager->startAllHiding();
@ -483,15 +712,16 @@ void Widget::leaveEvent(QEvent *e) {
toggleActionButtons(false);
}
void Widget::startHiding() {
_hideTimer.start(st::notifyWaitShortHide);
void Notification::startHiding() {
if (!_history) return;
hideSlow();
}
void Widget::mousePressEvent(QMouseEvent *e) {
void Notification::mousePressEvent(QMouseEvent *e) {
if (!_history) return;
if (e->button() == Qt::RightButton) {
unlinkHistoryAndNotify();
unlinkHistoryInManager();
} else {
e->ignore();
if (auto manager = ManagerInstance.data()) {
@ -502,88 +732,89 @@ void Widget::mousePressEvent(QMouseEvent *e) {
}
}
void Widget::paintEvent(QPaintEvent *e) {
Painter p(this);
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);
}
if (height() > st::notifyMinHeight) {
auto replyAreaHeight = height() - st::notifyMinHeight - st::notifyBorderWidth;
if (replyAreaHeight > 0) {
p.fillRect(st::notifyBorderWidth, st::notifyMinHeight, width() - 2 * st::notifyBorderWidth, replyAreaHeight, st::notifyBg);
}
p.fillRect(0, st::notifyMinHeight, st::notifyBorderWidth, height() - st::notifyMinHeight, st::notifyBorder);
p.fillRect(width() - st::notifyBorderWidth, st::notifyMinHeight, st::notifyBorderWidth, height() - st::notifyMinHeight, st::notifyBorder);
p.fillRect(st::notifyBorderWidth, height() - st::notifyBorderWidth, width() - 2 * st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder);
}
}
void Widget::animHide(float64 duration, anim::transition func) {
void Notification::stopHiding() {
if (!_history) return;
_alphaDuration = duration;
a_func = func;
a_opacity.start(0);
_hiding = true;
_a_appearance.start();
}
void Widget::stopHiding() {
if (!_history) return;
_alphaDuration = st::notifyFastAnim;
a_func = anim::linear;
a_opacity.start(1);
_hiding = false;
_hideTimer.stop();
_a_appearance.start();
Widget::hideStop();
}
void Widget::hideByTimer() {
if (!_history) return;
animHide(st::notifySlowHide, st::notifySlowHideFunc);
void Notification::onHideByTimer() {
startHiding();
}
void Widget::step_appearance(float64 ms, bool timer) {
float64 dt = ms / float64(_alphaDuration);
if (dt >= 1) {
a_opacity.finish();
_a_appearance.stop();
if (_hiding) {
deleteLater();
}
} else {
a_opacity.update(dt, a_func);
}
setWindowOpacity(a_opacity.current());
update();
}
void Widget::step_movement(float64 ms, bool timer) {
float64 dt = ms / float64(st::notifyFastAnim);
if (dt >= 1) {
a_top.finish();
a_bottom.finish();
} else {
a_top.update(dt, anim::linear);
a_bottom.update(dt, anim::linear);
}
setGeometry(x(), a_top.current(), width(), a_bottom.current() - a_top.current());
update();
}
Widget::~Widget() {
Notification::~Notification() {
if (auto manager = ManagerInstance.data()) {
manager->removeFromShown(this);
}
}
HideAllButton::HideAllButton(QPoint position) : Widget(position) {
setCursor(style::cur_pointer);
updateGeometry(position.x(), position.y(), notificationWidth(), st::notifyHideAll.height);
hide();
createWinId();
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::enterEvent(QEvent *e) {
_mouseOver = true;
update();
}
void HideAllButton::leaveEvent(QEvent *e) {
_mouseOver = false;
update();
}
void HideAllButton::mousePressEvent(QMouseEvent *e) {
_mouseDown = true;
}
void HideAllButton::mouseReleaseEvent(QMouseEvent *e) {
auto mouseDown = createAndSwap(_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::notifyHideAll.textBgOver : st::notifyHideAll.textBg);
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::btnDefLink.font);
p.setPen(st::btnDefLink.color);
p.drawText(rect(), lang(lng_notification_hide_all), style::al_center);
}
} // namespace internal
} // namespace Default
} // namespace Notifications
} // namespace Window

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "window/notifications_manager.h"
#include "core/single_timer.h"
namespace Ui {
class IconButton;
@ -29,9 +30,12 @@ class IconButton;
namespace Window {
namespace Notifications {
namespace Default {
namespace internal {
class Notification;
class HideAllButton;
} // namespace internal
class Manager;
class Widget;
void start();
Manager *manager();
@ -41,21 +45,21 @@ class Manager : public Notifications::Manager, private base::Subscriber {
public:
Manager();
void showNextFromQueue();
void removeFromShown(Widget *remove);
void startAllHiding();
void stopAllHiding();
template <typename Method>
void enumerateWidgets(Method method) {
for_const (auto widget, _widgets) {
method(widget);
void enumerateNotifications(Method method) {
for_const (auto notification, _notifications) {
method(notification);
}
}
~Manager();
private:
using Notification = internal::Notification;
friend class Notification;
using HideAllButton = internal::HideAllButton;
friend class HideAllButton;
void doUpdateAll() override;
void doShowNotification(HistoryItem *item, int forwardedCount) override;
void doClearAll() override;
@ -63,8 +67,25 @@ private:
void doClearFromHistory(History *history) override;
void doClearFromItem(HistoryItem *item) override;
using Widgets = QList<Widget*>;
Widgets _widgets;
void showNextFromQueue();
void unlinkFromShown(Notification *remove);
void removeFromShown(Notification *remove);
void removeHideAll(HideAllButton *remove);
void startAllHiding();
void stopAllHiding();
void checkLastInput();
QPoint notificationStartPosition() const;
void moveWidgets();
void changeNotificationHeight(Notification *widget, int newHeight);
using Notifications = QList<Notification*>;
Notifications _notifications;
HideAllButton *_hideAll = nullptr;
bool _positionsOutdated = false;
SingleTimer _inputCheckTimer;
struct QueuedNotification {
QueuedNotification(HistoryItem *item, int forwardedCount)
@ -86,52 +107,109 @@ private:
};
namespace internal {
class Widget : public TWidget {
Q_OBJECT
public:
Widget(History *history, PeerData *peer, PeerData *author, HistoryItem *item, int forwardedCount, int x, int y);
Widget(QPoint position);
void startHiding();
void stopHiding();
void moveTop(int top, int bottom);
void updateNotifyDisplay();
void updatePeerPhoto();
void itemRemoved(HistoryItem *del);
int isUnlinked() const {
return !_history;
bool isShowing() const {
return _a_appearance.animating() && !_hiding;
}
void moveTop(int top);
void unlinkHistory(History *history = nullptr);
~Widget();
enum class AddToHeight {
Above,
Below,
};
void addToHeight(int add, AddToHeight aboveOrBelow);
void addToTop(int add);
protected:
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void hideSlow();
void hideFast();
void hideStop();
private slots:
void hideByTimer();
void checkLastInput();
void onReplyResize();
void onReplySubmit(bool ctrlShiftEnter);
void onReplyCancel();
virtual void updateGeometry(int x, int y, int width, int height);
private:
void animHide(float64 duration, anim::transition func);
void step_appearance(float64 ms, bool timer);
void step_movement(float64 ms, bool timer);
void unlinkHistoryAndNotify();
bool _hiding = false;
float64 _opacityDuration;
anim::fvalue a_opacity;
anim::transition a_func;
Animation _a_appearance;
anim::ivalue a_top;
Animation _a_movement;
};
class Background : public TWidget {
public:
Background(QWidget *parent);
protected:
void paintEvent(QPaintEvent *e) override;
};
class Notification : public Widget {
Q_OBJECT
public:
Notification(History *history, PeerData *peer, PeerData *author, HistoryItem *item, int forwardedCount, QPoint position);
void startHiding();
void stopHiding();
void updateNotifyDisplay();
void updatePeerPhoto();
bool isUnlinked() const {
return !_history;
}
bool isReplying() const {
return (_replyArea != nullptr) && !isUnlinked();
}
// Called only by Manager.
void itemRemoved(HistoryItem *del);
bool unlinkHistory(History *history = nullptr);
bool checkLastInput();
~Notification();
protected:
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
private slots:
void onHideByTimer();
void onReplyResize();
void onReplySubmit(bool ctrlShiftEnter);
void onReplyCancel();
private:
void unlinkHistoryInManager();
void toggleActionButtons(bool visible);
void prepareActionsCache();
void actionsOpacityCallback();
void showReplyField();
void sendReply();
void changeHeight(int newHeight);
void updateGeometry(int x, int y, int width, int height) override;
void actionsOpacityCallback();
QPixmap _cache;
bool _actionsVisible = false;
FloatAnimation a_actionsOpacity;
QPixmap _buttonsCache;
#if defined Q_OS_WIN && !defined Q_OS_WINRT
uint64 _started;
@ -144,29 +222,43 @@ private:
int _forwardedCount;
ChildWidget<IconedButton> _close;
ChildWidget<BoxButton> _reply;
ChildWidget<Background> _background = { nullptr };
ChildWidget<InputArea> _replyArea = { nullptr };
ChildWidget<Ui::IconButton> _replySend = { nullptr };
QPixmap _cache;
float64 _alphaDuration;
QTimer _hideTimer, _inputTimer;
bool _hiding = false;
bool _waitingForInput = true;
anim::ivalue a_top, a_bottom;
Animation _a_movement;
anim::fvalue a_opacity;
anim::transition a_func;
Animation _a_appearance;
QTimer _hideTimer;
int _replyPadding = 0;
bool _actionsVisible = false;
FloatAnimation a_actionsOpacity;
QPixmap _buttonsCache;
ImagePtr peerPhoto;
bool _userpicLoaded = false;
};
class HideAllButton : public Widget {
public:
HideAllButton(QPoint position);
void startHiding();
void startHidingFast();
void stopHiding();
~HideAllButton();
protected:
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
bool _mouseOver = false;
bool _mouseDown = false;
};
} // namespace internal
} // namespace Default
} // namespace Notifications
} // namespace Window

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "window/notifications_manager.h"
#include "core/single_timer.h"
namespace Window {
namespace Notifications {

View File

@ -35,8 +35,7 @@ notifyItemTop: 12px;
notifyTextLeft: 12px;
notifyTextTop: 7px;
notifySlowHideFunc: transition(easeInCirc);
notifyWaitShortHide: 0;
notifyWaitLongHide: 20000;
notifyWaitLongHide: 3000;
notifyFastAnim: 150;
notifyMinWidth: 316px;
notifyMinHeight: 80px;
@ -44,6 +43,9 @@ notifyDeltaX: 6px;
notifyDeltaY: 7px;
notifyActionsDuration: 200;
notifyHideAll: RoundButton(defaultBoxButton) {
}
notifyFadeRight: icon {{ "fade_horizontal_right", notifyBg }};
notifyReplyArea: InputArea(defaultInputArea) {
font: normalFont;

View File

@ -205,6 +205,8 @@
'<(src_loc)/core/qthelp_url.h',
'<(src_loc)/core/runtime_composer.cpp',
'<(src_loc)/core/runtime_composer.h',
'<(src_loc)/core/single_timer.cpp',
'<(src_loc)/core/single_timer.h',
'<(src_loc)/core/stl_subset.h',
'<(src_loc)/core/utils.cpp',
'<(src_loc)/core/utils.h',