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 const_reference = const pair_type&;
class const_iterator;
class iterator : public iterator_base {
public:
using iterator_base::iterator_base;
@ -292,7 +291,6 @@ public:
}
};
class const_reverse_iterator;
class reverse_iterator : public reverse_iterator_base {
public:
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 "window/window_controller.h"
#include "mainwindow.h"
#include "messenger.h"
namespace ChatHelpers {
namespace {
@ -178,6 +179,7 @@ void TabbedPanel::moveByBottom() {
}
void TabbedPanel::enterEventHook(QEvent *e) {
Messenger::Instance().registerLeaveSubscription(this);
showAnimated();
}
@ -189,6 +191,7 @@ bool TabbedPanel::preventAutoHide() const {
}
void TabbedPanel::leaveEventHook(QEvent *e) {
Messenger::Instance().unregisterLeaveSubscription(this);
if (preventAutoHide()) {
return;
}

View file

@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "messenger.h"
#include <rpl/complete.h>
#include "data/data_photo.h"
#include "data/data_document.h"
#include "base/timer.h"
@ -995,6 +996,36 @@ QPoint Messenger::getPointForCallPanelCenter() const {
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() {
auto prevents = false;
if (!Sandbox::isSavingSession() && AuthSession::Exists()) {

View file

@ -174,6 +174,9 @@ public:
return _passcodedChanged;
}
void registerLeaveSubscription(QWidget *widget);
void unregisterLeaveSubscription(QWidget *widget);
void quitPreventFinished();
void handleAppActivated();
@ -246,4 +249,14 @@ private:
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() {
if (!_terminated) {
_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/mappers.h>
#include "messenger.h"
namespace Ui {
@ -109,10 +110,12 @@ void AbstractButton::setOver(bool over, StateChangeSource source) {
if (over && !(_state & StateFlag::Over)) {
auto was = _state;
_state |= StateFlag::Over;
Messenger::Instance().registerLeaveSubscription(this);
onStateChanged(was, source);
} else if (!over && (_state & StateFlag::Over)) {
auto was = _state;
_state &= ~State(StateFlag::Over);
Messenger::Instance().unregisterLeaveSubscription(this);
onStateChanged(was, source);
}
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)
, _a_loading(animation(this, &EmojiButton::step_loading)) {
resize(_st.width, _st.height);

View file

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

View file

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