feat: Add 'mouse-enter' and 'mouse-leave' Tray events for Windows. (#40072)

This commit is contained in:
Tomasz 2023-10-10 01:56:38 +02:00 committed by GitHub
parent a31deea1ba
commit 925e4f7d74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 125 additions and 2 deletions

View file

@ -187,7 +187,7 @@ Returns:
Emitted when the mouse clicks the tray icon.
#### Event: 'mouse-enter' _macOS_
#### Event: 'mouse-enter' _macOS_ _Windows_
Returns:
@ -196,7 +196,7 @@ Returns:
Emitted when the mouse enters the tray icon.
#### Event: 'mouse-leave' _macOS_
#### Event: 'mouse-leave' _macOS_ _Windows_
Returns:

View file

@ -102,6 +102,16 @@ void NotifyIcon::HandleMouseMoveEvent(int modifiers) {
NotifyMouseMoved(cursorPos, modifiers);
}
void NotifyIcon::HandleMouseEntered(int modifiers) {
gfx::Point cursor_pos = display::Screen::GetScreen()->GetCursorScreenPoint();
NotifyMouseEntered(cursor_pos, modifiers);
}
void NotifyIcon::HandleMouseExited(int modifiers) {
gfx::Point cursor_pos = display::Screen::GetScreen()->GetCursorScreenPoint();
NotifyMouseExited(cursor_pos, modifiers);
}
void NotifyIcon::ResetIcon() {
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);

View file

@ -50,6 +50,8 @@ class NotifyIcon : public TrayIcon {
// Handles a mouse move event from the user.
void HandleMouseMoveEvent(int modifiers);
void HandleMouseEntered(int modifiers);
void HandleMouseExited(int modifiers);
// Re-creates the status tray icon now after the taskbar has been created.
void ResetIcon();

View file

@ -11,11 +11,13 @@
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/stl_util.h"
#include "base/timer/timer.h"
#include "base/win/win_util.h"
#include "base/win/windows_types.h"
#include "base/win/wrapped_window_proc.h"
#include "content/public/browser/browser_thread.h"
#include "shell/browser/ui/win/notify_icon.h"
#include "ui/display/screen.h"
#include "ui/events/event_constants.h"
#include "ui/events/win/system_event_state_lookup.h"
#include "ui/gfx/win/hwnd_util.h"
@ -31,6 +33,8 @@ const UINT kBaseIconId = 2;
const wchar_t kNotifyIconHostWindowClass[] = L"Electron_NotifyIconHostWindow";
constexpr unsigned int kMouseLeaveCheckFrequency = 250;
bool IsWinPressed() {
return ((::GetKeyState(VK_LWIN) & 0x8000) == 0x8000) ||
((::GetKeyState(VK_RWIN) & 0x8000) == 0x8000);
@ -51,6 +55,103 @@ int GetKeyboardModifiers() {
} // namespace
// Helper class used to detect mouse entered and mouse exited events based on
// mouse move event.
class NotifyIconHost::MouseEnteredExitedDetector {
public:
MouseEnteredExitedDetector() = default;
~MouseEnteredExitedDetector() = default;
// disallow copy
MouseEnteredExitedDetector(const MouseEnteredExitedDetector&) = delete;
MouseEnteredExitedDetector& operator=(const MouseEnteredExitedDetector&) =
delete;
// disallow move
MouseEnteredExitedDetector(MouseEnteredExitedDetector&&) = delete;
MouseEnteredExitedDetector& operator=(MouseEnteredExitedDetector&&) = delete;
void MouseMoveEvent(raw_ptr<NotifyIcon> icon) {
if (!icon)
return;
// If cursor is out of icon then skip this move event.
if (!IsCursorOverIcon(icon))
return;
// If user moved cursor to other icon then send mouse exited event for
// old icon.
if (current_mouse_entered_ &&
current_mouse_entered_->icon_id() != icon->icon_id()) {
SendExitedEvent();
}
// If timer is runnig then cursor is arelady over icon and
// CheckCursorPositionOverIcon will be repeadly checking when to send
// mouse exited event.
if (mouse_exit_timer_.IsRunning())
return;
SendEnteredEvent(icon);
// Start repeadly checking when to send mouse exited event.
StartObservingIcon(icon);
}
void IconRemoved(raw_ptr<NotifyIcon> icon) {
if (current_mouse_entered_ &&
current_mouse_entered_->icon_id() == icon->icon_id()) {
SendExitedEvent();
}
}
private:
void SendEnteredEvent(raw_ptr<NotifyIcon> icon) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&NotifyIcon::HandleMouseEntered,
icon->GetWeakPtr(), GetKeyboardModifiers()));
}
void SendExitedEvent() {
mouse_exit_timer_.Stop();
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&NotifyIcon::HandleMouseExited,
std::move(current_mouse_entered_),
GetKeyboardModifiers()));
}
bool IsCursorOverIcon(raw_ptr<NotifyIcon> icon) {
gfx::Point cursor_pos =
display::Screen::GetScreen()->GetCursorScreenPoint();
return icon->GetBounds().Contains(cursor_pos);
}
void CheckCursorPositionOverIcon() {
if (!current_mouse_entered_ ||
IsCursorOverIcon(current_mouse_entered_.get()))
return;
SendExitedEvent();
}
void StartObservingIcon(raw_ptr<NotifyIcon> icon) {
current_mouse_entered_ = icon->GetWeakPtr();
mouse_exit_timer_.Start(
FROM_HERE, base::Milliseconds(kMouseLeaveCheckFrequency),
base::BindRepeating(
&MouseEnteredExitedDetector::CheckCursorPositionOverIcon,
weak_factory_.GetWeakPtr()));
}
// Timer used to check if cursor is still over the icon.
base::MetronomeTimer mouse_exit_timer_;
// Weak pointer to icon over which cursor is hovering.
base::WeakPtr<NotifyIcon> current_mouse_entered_;
base::WeakPtrFactory<MouseEnteredExitedDetector> weak_factory_{this};
};
NotifyIconHost::NotifyIconHost() {
// Register our window class
WNDCLASSEX window_class;
@ -74,6 +175,9 @@ NotifyIconHost::NotifyIconHost() {
instance_, 0);
gfx::CheckWindowCreated(window_, ::GetLastError());
gfx::SetWindowUserData(window_, this);
mouse_entered_exited_detector_ =
std::make_unique<MouseEnteredExitedDetector>();
}
NotifyIconHost::~NotifyIconHost() {
@ -116,6 +220,8 @@ void NotifyIconHost::Remove(NotifyIcon* icon) {
return;
}
mouse_entered_exited_detector_->IconRemoved(*i);
notify_icons_.erase(i);
}
@ -208,6 +314,8 @@ LRESULT CALLBACK NotifyIconHost::WndProc(HWND hwnd,
return TRUE;
case WM_MOUSEMOVE:
mouse_entered_exited_detector_->MouseMoveEvent(win_icon);
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&NotifyIcon::HandleMouseMoveEvent,
win_icon_weak, GetKeyboardModifiers()));

View file

@ -64,6 +64,9 @@ class NotifyIconHost {
// The message ID of the "TaskbarCreated" message, sent to us when we need to
// reset our status icons.
UINT taskbar_created_message_ = 0;
class MouseEnteredExitedDetector;
std::unique_ptr<MouseEnteredExitedDetector> mouse_entered_exited_detector_;
};
} // namespace electron