Add workaround for macOS leaveEvent() bugs.

On macOS sometimes when mouse leaves the window we don't receive leaveEvent()
calls in the nested widgets, like buttons, only for the window itself.
This commit is contained in:
John Preston 2017-11-21 14:27:37 +04:00
parent d93c1ccbaa
commit 44e94bfbf5
9 changed files with 66 additions and 4 deletions

View File

@ -272,7 +272,6 @@ public:
using reference = pair_type&; using reference = pair_type&;
using const_reference = const pair_type&; using const_reference = const pair_type&;
class const_iterator;
class iterator : public iterator_base { class iterator : public iterator_base {
public: public:
using iterator_base::iterator_base; using iterator_base::iterator_base;
@ -292,7 +291,6 @@ public:
} }
}; };
class const_reverse_iterator;
class reverse_iterator : public reverse_iterator_base { class reverse_iterator : public reverse_iterator_base {
public: public:
using reverse_iterator_base::reverse_iterator_base; using reverse_iterator_base::reverse_iterator_base;

View File

@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "chat_helpers/tabbed_selector.h" #include "chat_helpers/tabbed_selector.h"
#include "window/window_controller.h" #include "window/window_controller.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "messenger.h"
namespace ChatHelpers { namespace ChatHelpers {
namespace { namespace {
@ -178,6 +179,7 @@ void TabbedPanel::moveByBottom() {
} }
void TabbedPanel::enterEventHook(QEvent *e) { void TabbedPanel::enterEventHook(QEvent *e) {
Messenger::Instance().registerLeaveSubscription(this);
showAnimated(); showAnimated();
} }
@ -189,6 +191,7 @@ bool TabbedPanel::preventAutoHide() const {
} }
void TabbedPanel::leaveEventHook(QEvent *e) { void TabbedPanel::leaveEventHook(QEvent *e) {
Messenger::Instance().unregisterLeaveSubscription(this);
if (preventAutoHide()) { if (preventAutoHide()) {
return; return;
} }

View File

@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/ */
#include "messenger.h" #include "messenger.h"
#include <rpl/complete.h>
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "base/timer.h" #include "base/timer.h"
@ -995,6 +996,36 @@ QPoint Messenger::getPointForCallPanelCenter() const {
return QApplication::desktop()->screenGeometry().center(); return QApplication::desktop()->screenGeometry().center();
} }
// macOS Qt bug workaround, sometimes no leaveEvent() gets to the nested widgets.
void Messenger::registerLeaveSubscription(QWidget *widget) {
#ifdef Q_OS_MAC
if (auto topLevel = widget->window()) {
if (topLevel == _window.get()) {
auto guarded = weak(widget);
auto subscription = _window->leaveEvents()
| rpl::start_with_next([guarded] {
if (auto w = guarded.data()) {
QEvent ev(QEvent::Leave);
QGuiApplication::sendEvent(w, &ev);
}
});
_leaveSubscriptions.emplace_back(guarded, std::move(subscription));
}
}
#endif // Q_OS_MAC
}
void Messenger::unregisterLeaveSubscription(QWidget *widget) {
#ifdef Q_OS_MAC
_leaveSubscriptions = std::move(
_leaveSubscriptions
) | ranges::action::remove_if([&](const LeaveSubscription &subscription) {
auto pointer = subscription.pointer.data();
return !pointer || (pointer == widget);
});
#endif // Q_OS_MAC
}
void Messenger::QuitAttempt() { void Messenger::QuitAttempt() {
auto prevents = false; auto prevents = false;
if (!Sandbox::isSavingSession() && AuthSession::Exists()) { if (!Sandbox::isSavingSession() && AuthSession::Exists()) {

View File

@ -174,6 +174,9 @@ public:
return _passcodedChanged; return _passcodedChanged;
} }
void registerLeaveSubscription(QWidget *widget);
void unregisterLeaveSubscription(QWidget *widget);
void quitPreventFinished(); void quitPreventFinished();
void handleAppActivated(); void handleAppActivated();
@ -246,4 +249,14 @@ private:
base::DelayedCallTimer _callDelayedTimer; base::DelayedCallTimer _callDelayedTimer;
struct LeaveSubscription {
LeaveSubscription(QPointer<QWidget> pointer, rpl::lifetime &&subscription)
: pointer(pointer), subscription(std::move(subscription)) {
}
QPointer<QWidget> pointer;
rpl::lifetime subscription;
};
std::vector<LeaveSubscription> _leaveSubscriptions;
}; };

View File

@ -135,7 +135,7 @@ template <typename Value, typename Error>
inline void type_erased_handlers<Value, Error>::terminate() { inline void type_erased_handlers<Value, Error>::terminate() {
if (!_terminated) { if (!_terminated) {
_terminated = true; _terminated = true;
details::take(_lifetime).destroy(); _lifetime.destroy();
} }
} }

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/filter.h> #include <rpl/filter.h>
#include <rpl/mappers.h> #include <rpl/mappers.h>
#include "messenger.h"
namespace Ui { namespace Ui {
@ -109,10 +110,12 @@ void AbstractButton::setOver(bool over, StateChangeSource source) {
if (over && !(_state & StateFlag::Over)) { if (over && !(_state & StateFlag::Over)) {
auto was = _state; auto was = _state;
_state |= StateFlag::Over; _state |= StateFlag::Over;
Messenger::Instance().registerLeaveSubscription(this);
onStateChanged(was, source); onStateChanged(was, source);
} else if (!over && (_state & StateFlag::Over)) { } else if (!over && (_state & StateFlag::Over)) {
auto was = _state; auto was = _state;
_state &= ~State(StateFlag::Over); _state &= ~State(StateFlag::Over);
Messenger::Instance().unregisterLeaveSubscription(this);
onStateChanged(was, source); onStateChanged(was, source);
} }
updateCursor(); updateCursor();

View File

@ -169,7 +169,8 @@ void HistoryDownButton::setUnreadCount(int unreadCount) {
} }
} }
EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : RippleButton(parent, st.ripple) EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st)
: RippleButton(parent, st.ripple)
, _st(st) , _st(st)
, _a_loading(animation(this, &EmojiButton::step_loading)) { , _a_loading(animation(this, &EmojiButton::step_loading)) {
resize(_st.width, _st.height); resize(_st.width, _st.height);

View File

@ -261,6 +261,14 @@ void MainWindow::resizeEvent(QResizeEvent *e) {
updateControlsGeometry(); updateControlsGeometry();
} }
rpl::producer<> MainWindow::leaveEvents() const {
return _leaveEvents.events();
}
void MainWindow::leaveEvent(QEvent *e) {
_leaveEvents.fire({});
}
void MainWindow::updateControlsGeometry() { void MainWindow::updateControlsGeometry() {
auto bodyTop = 0; auto bodyTop = 0;
auto bodyWidth = width(); auto bodyWidth = width();

View File

@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include <rpl/event_stream.h>
#include "window/window_title.h" #include "window/window_title.h"
#include "base/timer.h" #include "base/timer.h"
@ -96,6 +97,8 @@ public:
return _dragFinished; return _dragFinished;
} }
rpl::producer<> leaveEvents() const;
public slots: public slots:
bool minimizeToTray(); bool minimizeToTray();
void updateGlobalMenu() { void updateGlobalMenu() {
@ -104,6 +107,7 @@ public slots:
protected: protected:
void resizeEvent(QResizeEvent *e) override; void resizeEvent(QResizeEvent *e) override;
void leaveEvent(QEvent *e) override;
void savePosition(Qt::WindowState state = Qt::WindowActive); void savePosition(Qt::WindowState state = Qt::WindowActive);
void handleStateChanged(Qt::WindowState state); void handleStateChanged(Qt::WindowState state);
@ -187,6 +191,7 @@ private:
base::Timer _inactivePressTimer; base::Timer _inactivePressTimer;
base::Observable<void> _dragFinished; base::Observable<void> _dragFinished;
rpl::event_stream<> _leaveEvents;
}; };