feat: Add 'mouse-enter' and 'mouse-leave' Tray events for Windows. (#40072)
This commit is contained in:
parent
a31deea1ba
commit
925e4f7d74
5 changed files with 125 additions and 2 deletions
|
@ -187,7 +187,7 @@ Returns:
|
||||||
|
|
||||||
Emitted when the mouse clicks the tray icon.
|
Emitted when the mouse clicks the tray icon.
|
||||||
|
|
||||||
#### Event: 'mouse-enter' _macOS_
|
#### Event: 'mouse-enter' _macOS_ _Windows_
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ Returns:
|
||||||
|
|
||||||
Emitted when the mouse enters the tray icon.
|
Emitted when the mouse enters the tray icon.
|
||||||
|
|
||||||
#### Event: 'mouse-leave' _macOS_
|
#### Event: 'mouse-leave' _macOS_ _Windows_
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,16 @@ void NotifyIcon::HandleMouseMoveEvent(int modifiers) {
|
||||||
NotifyMouseMoved(cursorPos, 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() {
|
void NotifyIcon::ResetIcon() {
|
||||||
NOTIFYICONDATA icon_data;
|
NOTIFYICONDATA icon_data;
|
||||||
InitIconData(&icon_data);
|
InitIconData(&icon_data);
|
||||||
|
|
|
@ -50,6 +50,8 @@ class NotifyIcon : public TrayIcon {
|
||||||
|
|
||||||
// Handles a mouse move event from the user.
|
// Handles a mouse move event from the user.
|
||||||
void HandleMouseMoveEvent(int modifiers);
|
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.
|
// Re-creates the status tray icon now after the taskbar has been created.
|
||||||
void ResetIcon();
|
void ResetIcon();
|
||||||
|
|
|
@ -11,11 +11,13 @@
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/memory/weak_ptr.h"
|
#include "base/memory/weak_ptr.h"
|
||||||
#include "base/stl_util.h"
|
#include "base/stl_util.h"
|
||||||
|
#include "base/timer/timer.h"
|
||||||
#include "base/win/win_util.h"
|
#include "base/win/win_util.h"
|
||||||
#include "base/win/windows_types.h"
|
#include "base/win/windows_types.h"
|
||||||
#include "base/win/wrapped_window_proc.h"
|
#include "base/win/wrapped_window_proc.h"
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
#include "shell/browser/ui/win/notify_icon.h"
|
#include "shell/browser/ui/win/notify_icon.h"
|
||||||
|
#include "ui/display/screen.h"
|
||||||
#include "ui/events/event_constants.h"
|
#include "ui/events/event_constants.h"
|
||||||
#include "ui/events/win/system_event_state_lookup.h"
|
#include "ui/events/win/system_event_state_lookup.h"
|
||||||
#include "ui/gfx/win/hwnd_util.h"
|
#include "ui/gfx/win/hwnd_util.h"
|
||||||
|
@ -31,6 +33,8 @@ const UINT kBaseIconId = 2;
|
||||||
|
|
||||||
const wchar_t kNotifyIconHostWindowClass[] = L"Electron_NotifyIconHostWindow";
|
const wchar_t kNotifyIconHostWindowClass[] = L"Electron_NotifyIconHostWindow";
|
||||||
|
|
||||||
|
constexpr unsigned int kMouseLeaveCheckFrequency = 250;
|
||||||
|
|
||||||
bool IsWinPressed() {
|
bool IsWinPressed() {
|
||||||
return ((::GetKeyState(VK_LWIN) & 0x8000) == 0x8000) ||
|
return ((::GetKeyState(VK_LWIN) & 0x8000) == 0x8000) ||
|
||||||
((::GetKeyState(VK_RWIN) & 0x8000) == 0x8000);
|
((::GetKeyState(VK_RWIN) & 0x8000) == 0x8000);
|
||||||
|
@ -51,6 +55,103 @@ int GetKeyboardModifiers() {
|
||||||
|
|
||||||
} // namespace
|
} // 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() {
|
NotifyIconHost::NotifyIconHost() {
|
||||||
// Register our window class
|
// Register our window class
|
||||||
WNDCLASSEX window_class;
|
WNDCLASSEX window_class;
|
||||||
|
@ -74,6 +175,9 @@ NotifyIconHost::NotifyIconHost() {
|
||||||
instance_, 0);
|
instance_, 0);
|
||||||
gfx::CheckWindowCreated(window_, ::GetLastError());
|
gfx::CheckWindowCreated(window_, ::GetLastError());
|
||||||
gfx::SetWindowUserData(window_, this);
|
gfx::SetWindowUserData(window_, this);
|
||||||
|
|
||||||
|
mouse_entered_exited_detector_ =
|
||||||
|
std::make_unique<MouseEnteredExitedDetector>();
|
||||||
}
|
}
|
||||||
|
|
||||||
NotifyIconHost::~NotifyIconHost() {
|
NotifyIconHost::~NotifyIconHost() {
|
||||||
|
@ -116,6 +220,8 @@ void NotifyIconHost::Remove(NotifyIcon* icon) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mouse_entered_exited_detector_->IconRemoved(*i);
|
||||||
|
|
||||||
notify_icons_.erase(i);
|
notify_icons_.erase(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +314,8 @@ LRESULT CALLBACK NotifyIconHost::WndProc(HWND hwnd,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case WM_MOUSEMOVE:
|
case WM_MOUSEMOVE:
|
||||||
|
mouse_entered_exited_detector_->MouseMoveEvent(win_icon);
|
||||||
|
|
||||||
content::GetUIThreadTaskRunner({})->PostTask(
|
content::GetUIThreadTaskRunner({})->PostTask(
|
||||||
FROM_HERE, base::BindOnce(&NotifyIcon::HandleMouseMoveEvent,
|
FROM_HERE, base::BindOnce(&NotifyIcon::HandleMouseMoveEvent,
|
||||||
win_icon_weak, GetKeyboardModifiers()));
|
win_icon_weak, GetKeyboardModifiers()));
|
||||||
|
|
|
@ -64,6 +64,9 @@ class NotifyIconHost {
|
||||||
// The message ID of the "TaskbarCreated" message, sent to us when we need to
|
// The message ID of the "TaskbarCreated" message, sent to us when we need to
|
||||||
// reset our status icons.
|
// reset our status icons.
|
||||||
UINT taskbar_created_message_ = 0;
|
UINT taskbar_created_message_ = 0;
|
||||||
|
|
||||||
|
class MouseEnteredExitedDetector;
|
||||||
|
std::unique_ptr<MouseEnteredExitedDetector> mouse_entered_exited_detector_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue