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.
|
||||
|
||||
#### 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:
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue