Improve calls panel in macOS.

Use Qt::Dialog instead of Qt::Tool which works better with window
activation / deactivation handling.

Stop displaying the panel on all spaces when the call is established.
This commit is contained in:
John Preston 2017-05-02 12:08:08 +03:00
parent 0cdac83f8a
commit 5f2e295d63
14 changed files with 149 additions and 129 deletions

View File

@ -184,7 +184,7 @@ void Panel::refreshCallbacks() {
} }
void Panel::initLayout() { void Panel::initLayout() {
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::Tool); setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::Dialog);
setAttribute(Qt::WA_MacAlwaysShowToolWindow); setAttribute(Qt::WA_MacAlwaysShowToolWindow);
setAttribute(Qt::WA_NoSystemBackground, true); setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true); setAttribute(Qt::WA_TranslucentBackground, true);
@ -267,8 +267,9 @@ bool Panel::isGoodUserPhoto(PhotoData *photo) {
void Panel::initGeometry() { void Panel::initGeometry() {
auto center = Messenger::Instance().getPointForCallPanelCenter(); auto center = Messenger::Instance().getPointForCallPanelCenter();
_useTransparency = Platform::TransparentWindowsSupported(center); _useTransparency = Platform::TranslucentWindowsSupported(center);
_padding = _useTransparency ? st::callShadow.extend : style::margins(); setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);
_padding = _useTransparency ? st::callShadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth);
_contentTop = _padding.top() + st::callWidth; _contentTop = _padding.top() + st::callWidth;
auto screen = QApplication::desktop()->screenGeometry(center); auto screen = QApplication::desktop()->screenGeometry(center);
auto rect = QRect(0, 0, st::callWidth, st::callHeight); auto rect = QRect(0, 0, st::callWidth, st::callHeight);
@ -359,10 +360,17 @@ void Panel::updateStatusGeometry() {
void Panel::paintEvent(QPaintEvent *e) { void Panel::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
if (_useTransparency) { if (_useTransparency) {
Platform::StartTranslucentPaint(p, e);
p.drawPixmapLeft(0, 0, width(), _cache); p.drawPixmapLeft(0, 0, width(), _cache);
} else { } else {
p.drawPixmapLeft(0, 0, width(), _userPhoto); p.drawPixmapLeft(_padding.left(), _padding.top(), width(), _userPhoto);
p.fillRect(myrtlrect(0, st::callWidth, width(), height() - st::callWidth), st::callBg); auto callBgOpaque = st::callBg->c;
callBgOpaque.setAlpha(255);
auto brush = QBrush(callBgOpaque);
p.fillRect(0, 0, width(), _padding.top(), brush);
p.fillRect(myrtlrect(0, _padding.top(), _padding.left(), _contentTop - _padding.top()), brush);
p.fillRect(myrtlrect(width() - _padding.right(), _padding.top(), _padding.right(), _contentTop - _padding.top()), brush);
p.fillRect(0, _contentTop, width(), height() - _contentTop, brush);
} }
if (!_fingerprint.empty()) { if (!_fingerprint.empty()) {
@ -381,10 +389,16 @@ void Panel::paintEvent(QPaintEvent *e) {
void Panel::mousePressEvent(QMouseEvent *e) { void Panel::mousePressEvent(QMouseEvent *e) {
auto dragArea = myrtlrect(_padding.left(), _padding.top(), st::callWidth, st::callWidth); auto dragArea = myrtlrect(_padding.left(), _padding.top(), st::callWidth, st::callWidth);
if (e->button() == Qt::LeftButton && dragArea.contains(e->pos())) { if (e->button() == Qt::LeftButton) {
if (dragArea.contains(e->pos())) {
_dragging = true; _dragging = true;
_dragStartMousePosition = e->globalPos(); _dragStartMousePosition = e->globalPos();
_dragStartMyPosition = QPoint(x(), y()); _dragStartMyPosition = QPoint(x(), y());
} else if (!rect().contains(e->pos())) {
if (_call && _call->state() == State::Established) {
hideDeactivated();
}
}
} }
} }
@ -458,10 +472,17 @@ void Panel::stateChanged(State state) {
if (_fingerprint.empty() && _call && _call->isKeyShaForFingerprintReady()) { if (_fingerprint.empty() && _call && _call->isKeyShaForFingerprintReady()) {
fillFingerprint(); fillFingerprint();
} }
if (state == State::Established && !isActiveWindow()) { if ((state == State::Starting) || (state == State::WaitingIncoming)) {
Platform::ReInitOnTopPanel(this);
} else {
Platform::DeInitOnTopPanel(this);
}
if (state == State::Established) {
if (!isActiveWindow()) {
hideDeactivated(); hideDeactivated();
} }
} }
}
void Panel::fillFingerprint() { void Panel::fillFingerprint() {
_fingerprint = ComputeEmojiFingerprint(_call.get()); _fingerprint = ComputeEmojiFingerprint(_call.get());

View File

@ -377,10 +377,7 @@ void finish() {
_psEventFilter = nullptr; _psEventFilter = nullptr;
} }
void SetWatchingMediaKeys(bool watching) { bool TranslucentWindowsSupported(QPoint globalPosition) {
}
bool TransparentWindowsSupported(QPoint globalPosition) {
if (auto app = static_cast<QGuiApplication*>(QCoreApplication::instance())) { if (auto app = static_cast<QGuiApplication*>(QCoreApplication::instance())) {
if (auto native = app->platformNativeInterface()) { if (auto native = app->platformNativeInterface()) {
if (auto desktop = QApplication::desktop()) { if (auto desktop = QApplication::desktop()) {
@ -405,9 +402,6 @@ bool TransparentWindowsSupported(QPoint globalPosition) {
return false; return false;
} }
void InitOnTopPanel(QWidget *panel) {
}
namespace ThirdParty { namespace ThirdParty {
void start() { void start() {

View File

@ -20,6 +20,25 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <execinfo.h> #include <execinfo.h>
#include <signal.h> #include <signal.h>
namespace Platform {
inline void SetWatchingMediaKeys(bool watching) {
}
inline void StartTranslucentPaint(QPainter &p, QPaintEvent *e) {
}
inline void InitOnTopPanel(QWidget *panel) {
}
inline void DeInitOnTopPanel(QWidget *panel) {
}
inline void ReInitOnTopPanel(QWidget *panel) {
}
} // namespace Platform
inline QString psServerPrefix() { inline QString psServerPrefix() {
return qsl("/tmp/"); return qsl("/tmp/");
} }

View File

@ -48,9 +48,6 @@ public:
return _customTitleHeight; return _customTitleHeight;
} }
// It is placed here while the window handles activeSpaceDidChange event.
void customNotificationCreated(QWidget *notification);
~MainWindow(); ~MainWindow();
class Private; class Private;
@ -104,16 +101,9 @@ private:
void updateTitleCounter(); void updateTitleCounter();
void updateIconCounters(); void updateIconCounters();
class CustomNotificationHandle;
friend class CustomNotificationHandle;
void customNotificationDestroyed(CustomNotificationHandle *handle);
void activateCustomNotifications();
friend class Private; friend class Private;
std::unique_ptr<Private> _private; std::unique_ptr<Private> _private;
std::set<CustomNotificationHandle*> _customNotifications;
mutable bool psIdle; mutable bool psIdle;
mutable QTimer psIdleTimer; mutable QTimer psIdleTimer;

View File

@ -73,7 +73,6 @@ public:
void willEnterFullScreen(); void willEnterFullScreen();
void willExitFullScreen(); void willExitFullScreen();
void activateCustomNotifications();
void initCustomTitle(NSWindow *window, NSView *view); void initCustomTitle(NSWindow *window, NSView *view);
@ -92,25 +91,6 @@ private:
}; };
class MainWindow::CustomNotificationHandle : public QObject {
public:
CustomNotificationHandle(QWidget *parent) : QObject(parent) {
}
void activate() {
auto widget = static_cast<QWidget*>(parent());
NSWindow *wnd = [reinterpret_cast<NSView *>(widget->winId()) window];
[wnd orderFront:wnd];
}
~CustomNotificationHandle() {
if (auto window = App::wnd()) {
window->customNotificationDestroyed(this);
}
}
};
} // namespace Platform } // namespace Platform
@implementation MainWindowObserver { @implementation MainWindowObserver {
@ -126,7 +106,6 @@ public:
} }
- (void) activeSpaceDidChange:(NSNotification *)aNotification { - (void) activeSpaceDidChange:(NSNotification *)aNotification {
_private->activateCustomNotifications();
} }
- (void) darkModeChanged:(NSNotification *)aNotification { - (void) darkModeChanged:(NSNotification *)aNotification {
@ -211,10 +190,6 @@ void MainWindow::Private::willExitFullScreen() {
_public->setTitleVisible(true); _public->setTitleVisible(true);
} }
void MainWindow::Private::activateCustomNotifications() {
_public->activateCustomNotifications();
}
void MainWindow::Private::enableShadow(WId winId) { void MainWindow::Private::enableShadow(WId winId) {
// [[(NSView*)winId window] setStyleMask:NSBorderlessWindowMask]; // [[(NSView*)winId window] setStyleMask:NSBorderlessWindowMask];
// [[(NSView*)winId window] setHasShadow:YES]; // [[(NSView*)winId window] setHasShadow:YES];
@ -527,20 +502,6 @@ void MainWindow::psUpdateSysMenu(Qt::WindowState state) {
void MainWindow::psUpdateMargins() { void MainWindow::psUpdateMargins() {
} }
void MainWindow::customNotificationCreated(QWidget *notification) {
_customNotifications.insert(object_ptr<CustomNotificationHandle>(notification));
}
void MainWindow::customNotificationDestroyed(CustomNotificationHandle *handle) {
_customNotifications.erase(handle);
}
void MainWindow::activateCustomNotifications() {
for (auto handle : _customNotifications) {
handle->activate();
}
}
void MainWindow::updateGlobalMenuHook() { void MainWindow::updateGlobalMenuHook() {
if (!App::wnd() || !positionInited()) return; if (!App::wnd() || !positionInited()) return;

View File

@ -396,19 +396,14 @@ void finish() {
objc_finish(); objc_finish();
} }
bool TransparentWindowsSupported(QPoint globalPosition) { void StartTranslucentPaint(QPainter &p, QPaintEvent *e) {
return true; #ifdef OS_MAC_OLD
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(e->rect(), Qt::transparent);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
#endif // OS_MAC_OLD
} }
namespace ThirdParty {
void start() {
}
void finish() {
}
} // namespace ThirdParty
} // namespace Platform } // namespace Platform
void psNewVersion() { void psNewVersion() {

View File

@ -19,6 +19,23 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "platform/mac/specific_mac_p.h" #include "platform/mac/specific_mac_p.h"
namespace Platform {
inline bool TranslucentWindowsSupported(QPoint globalPosition) {
return true;
}
namespace ThirdParty {
inline void start() {
}
inline void finish() {
}
} // namespace ThirdParty
} // namespace Platform
inline QString psServerPrefix() { inline QString psServerPrefix() {
#ifndef OS_MAC_STORE #ifndef OS_MAC_STORE
return qsl("/tmp/"); return qsl("/tmp/");

View File

@ -215,11 +215,25 @@ void InitOnTopPanel(QWidget *panel) {
[platformPanel setFloatingPanel:YES]; [platformPanel setFloatingPanel:YES];
[platformPanel setHidesOnDeactivate:NO]; [platformPanel setHidesOnDeactivate:NO];
if (auto window = App::wnd()) { objc_ignoreApplicationActivationRightNow();
window->customNotificationCreated(panel);
} }
objc_ignoreApplicationActivationRightNow(); void DeInitOnTopPanel(QWidget *panel) {
auto platformWindow = [reinterpret_cast<NSView*>(panel->winId()) window];
t_assert([platformWindow isKindOfClass:[NSPanel class]]);
auto platformPanel = static_cast<NSPanel*>(platformWindow);
auto newBehavior = ([platformPanel collectionBehavior] & (~NSWindowCollectionBehaviorCanJoinAllSpaces)) | NSWindowCollectionBehaviorMoveToActiveSpace;
[platformPanel setCollectionBehavior:newBehavior];
}
void ReInitOnTopPanel(QWidget *panel) {
auto platformWindow = [reinterpret_cast<NSView*>(panel->winId()) window];
t_assert([platformWindow isKindOfClass:[NSPanel class]]);
auto platformPanel = static_cast<NSPanel*>(platformWindow);
auto newBehavior = ([platformPanel collectionBehavior] & (~NSWindowCollectionBehaviorMoveToActiveSpace)) | NSWindowCollectionBehaviorCanJoinAllSpaces;
[platformPanel setCollectionBehavior:newBehavior];
} }
} // namespace Platform } // namespace Platform

View File

@ -34,9 +34,11 @@ void start();
void finish(); void finish();
void SetWatchingMediaKeys(bool watching); void SetWatchingMediaKeys(bool watching);
bool TransparentWindowsSupported(QPoint globalPosition); bool TranslucentWindowsSupported(QPoint globalPosition);
void StartTranslucentPaint(QPainter &p, QPaintEvent *e);
void InitOnTopPanel(QWidget *panel); void InitOnTopPanel(QWidget *panel);
void DeInitOnTopPanel(QWidget *panel);
void ReInitOnTopPanel(QWidget *panel);
namespace ThirdParty { namespace ThirdParty {

View File

@ -527,16 +527,6 @@ void finish() {
EventFilter::destroy(); EventFilter::destroy();
} }
void SetWatchingMediaKeys(bool watching) {
}
bool TransparentWindowsSupported(QPoint globalPosition) {
return true;
}
void InitOnTopPanel(QWidget *panel) {
}
namespace ThirdParty { namespace ThirdParty {
void start() { void start() {

View File

@ -22,6 +22,29 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <windows.h> #include <windows.h>
namespace Platform {
inline void SetWatchingMediaKeys(bool watching) {
}
inline bool TranslucentWindowsSupported(QPoint globalPosition) {
return true;
}
inline void StartTranslucentPaint(QPainter &p, QPaintEvent *e) {
}
inline void InitOnTopPanel(QWidget *panel) {
}
inline void DeInitOnTopPanel(QWidget *panel) {
}
inline void ReInitOnTopPanel(QWidget *panel) {
}
} // namespace Platform
inline QString psServerPrefix() { inline QString psServerPrefix() {
return qsl("Global\\"); return qsl("Global\\");
} }

View File

@ -107,11 +107,9 @@ PopupMenu::Actions &PopupMenu::actions() {
void PopupMenu::paintEvent(QPaintEvent *e) { void PopupMenu::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
#ifdef OS_MAC_OLD if (_useTransparency) {
p.setCompositionMode(QPainter::CompositionMode_Source); Platform::StartTranslucentPaint(p, e);
p.fillRect(e->rect(), Qt::transparent); }
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
#endif // OS_MAC_OLD
auto ms = getms(); auto ms = getms();
if (_a_show.animating(ms)) { if (_a_show.animating(ms)) {
@ -425,7 +423,8 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou
auto origin = PanelAnimation::Origin::TopLeft; auto origin = PanelAnimation::Origin::TopLeft;
auto w = p - QPoint(0, _padding.top()); auto w = p - QPoint(0, _padding.top());
auto r = Sandbox::screenGeometry(p); auto r = Sandbox::screenGeometry(p);
_useTransparency = Platform::TransparentWindowsSupported(p); _useTransparency = Platform::TranslucentWindowsSupported(p);
setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);
handleCompositingUpdate(); handleCompositingUpdate();
if (rtl()) { if (rtl()) {
if (w.x() - width() < r.x() - _padding.left()) { if (w.x() - width() < r.x() - _padding.left()) {

View File

@ -19,6 +19,7 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
#include "platform/platform_specific.h"
namespace Ui { namespace Ui {
@ -45,17 +46,17 @@ AbstractTooltipShower::~AbstractTooltipShower() {
Tooltip::Tooltip() : TWidget(nullptr) { Tooltip::Tooltip() : TWidget(nullptr) {
TooltipInstance = this; TooltipInstance = this;
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint); setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::ToolTip);
setAttribute(Qt::WA_NoSystemBackground, true); setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true); setAttribute(Qt::WA_TranslucentBackground, true);
_showTimer.setSingleShot(true); _showTimer.setCallback([this] { performShow(); });
connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShow())); _hideByLeaveTimer.setCallback([this] { Hide(); });
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
} }
void Tooltip::onShow() { void Tooltip::performShow() {
if (_shower) { if (_shower) {
auto text = _shower->tooltipWindowActive() ? _shower->tooltipText() : QString(); auto text = _shower->tooltipWindowActive() ? _shower->tooltipText() : QString();
if (text.isEmpty()) { if (text.isEmpty()) {
@ -74,9 +75,9 @@ void Tooltip::onWndActiveChanged() {
bool Tooltip::eventFilter(QObject *o, QEvent *e) { bool Tooltip::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::Leave) { if (e->type() == QEvent::Leave) {
_hideByLeaveTimer.start(10); _hideByLeaveTimer.callOnce(10);
} else if (e->type() == QEvent::Enter) { } else if (e->type() == QEvent::Enter) {
_hideByLeaveTimer.stop(); _hideByLeaveTimer.cancel();
} else if (e->type() == QEvent::MouseMove) { } else if (e->type() == QEvent::MouseMove) {
if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) { if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) {
Hide(); Hide();
@ -85,10 +86,6 @@ bool Tooltip::eventFilter(QObject *o, QEvent *e) {
return TWidget::eventFilter(o, e); return TWidget::eventFilter(o, e);
} }
void Tooltip::onHideByLeave() {
Hide();
}
Tooltip::~Tooltip() { Tooltip::~Tooltip() {
if (TooltipInstance == this) { if (TooltipInstance == this) {
TooltipInstance = 0; TooltipInstance = 0;
@ -96,10 +93,7 @@ Tooltip::~Tooltip() {
} }
void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) { void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) {
if (!_hideByLeaveTimer.isSingleShot()) { if (!_isEventFilter) {
_hideByLeaveTimer.setSingleShot(true);
connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave()));
QCoreApplication::instance()->installEventFilter(this); QCoreApplication::instance()->installEventFilter(this);
} }
@ -107,7 +101,7 @@ void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *
_st = st; _st = st;
_text = Text(_st->textStyle, text, _textPlainOptions, _st->widthMax, true); _text = Text(_st->textStyle, text, _textPlainOptions, _st->widthMax, true);
_useTransparency = Platform::TransparentWindowsSupported(_point); _useTransparency = Platform::TranslucentWindowsSupported(_point);
setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);
int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right(); int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right();
@ -150,18 +144,16 @@ void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *
setGeometry(QRect(p, s)); setGeometry(QRect(p, s));
_hideByLeaveTimer.stop(); _hideByLeaveTimer.cancel();
show(); show();
} }
void Tooltip::paintEvent(QPaintEvent *e) { void Tooltip::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
#ifdef OS_MAC_OLD if (_useTransparency) {
p.setCompositionMode(QPainter::CompositionMode_Source); Platform::StartTranslucentPaint(p, e);
p.fillRect(e->rect(), Qt::transparent); }
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
#endif // OS_MAC_OLD
if (_useTransparency) { if (_useTransparency) {
p.setPen(_st->textBorder); p.setPen(_st->textBorder);
@ -194,17 +186,17 @@ void Tooltip::Show(int32 delay, const AbstractTooltipShower *shower) {
} }
TooltipInstance->_shower = shower; TooltipInstance->_shower = shower;
if (delay >= 0) { if (delay >= 0) {
TooltipInstance->_showTimer.start(delay); TooltipInstance->_showTimer.callOnce(delay);
} else { } else {
TooltipInstance->onShow(); TooltipInstance->performShow();
} }
} }
void Tooltip::Hide() { void Tooltip::Hide() {
if (auto instance = TooltipInstance) { if (auto instance = TooltipInstance) {
TooltipInstance = nullptr; TooltipInstance = nullptr;
instance->_showTimer.stop(); instance->_showTimer.cancel();
instance->_hideByLeaveTimer.stop(); instance->_hideByLeaveTimer.cancel();
instance->hide(); instance->hide();
instance->deleteLater(); instance->deleteLater();
} }

View File

@ -17,6 +17,8 @@
*/ */
#pragma once #pragma once
#include "base/timer.h"
namespace style { namespace style {
struct Tooltip; struct Tooltip;
} // namespace style } // namespace style
@ -41,9 +43,7 @@ public:
static void Hide(); static void Hide();
private slots: private slots:
void onShow();
void onWndActiveChanged(); void onWndActiveChanged();
void onHideByLeave();
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
@ -52,6 +52,8 @@ protected:
bool eventFilter(QObject *o, QEvent *e) override; bool eventFilter(QObject *o, QEvent *e) override;
private: private:
void performShow();
Tooltip(); Tooltip();
~Tooltip(); ~Tooltip();
@ -59,14 +61,15 @@ private:
friend class AbstractTooltipShower; friend class AbstractTooltipShower;
const AbstractTooltipShower *_shower = nullptr; const AbstractTooltipShower *_shower = nullptr;
QTimer _showTimer; base::Timer _showTimer;
Text _text; Text _text;
QPoint _point; QPoint _point;
const style::Tooltip *_st = nullptr; const style::Tooltip *_st = nullptr;
QTimer _hideByLeaveTimer; base::Timer _hideByLeaveTimer;
bool _isEventFilter = false;
bool _useTransparency = true; bool _useTransparency = true;
}; };