Skip to content

Commit

Permalink
feat: add TrayIcon::rect() (#147)
Browse files Browse the repository at this point in the history
* Add TrayIcon::get_frame_rect()

* add windows implementation

* share logic

* fix linux impl

* fix macos impl

* macos: refactor

---------

Co-authored-by: amrbashir <[email protected]>
  • Loading branch information
pronebird and amrbashir authored Apr 30, 2024
1 parent 768d5e5 commit 599bb8f
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changes/tray-icon-rect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tray-icon": "patch"
---

Add `TrayIcon::rect` method to retrieve the tray icon rectangle on Windows and macOS.
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,15 @@ impl TrayIcon {
#[cfg(not(target_os = "macos"))]
let _ = enable;
}

/// Get tray icon rect.
///
/// ## Platform-specific:
///
/// - **Linux**: Unsupported.
pub fn rect(&self) -> Option<Rect> {
self.tray.borrow().rect()
}
}

/// Describes a tray event emitted when a tray icon is clicked
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/gtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ impl TrayIcon {
pub fn set_temp_dir_path<P: AsRef<Path>>(&mut self, path: Option<P>) {
self.temp_dir_path = path.map(|p| p.as_ref().to_path_buf());
}

pub fn rect(&self) -> Option<crate::Rect> {
None
}
}

impl Drop for TrayIcon {
Expand Down
45 changes: 32 additions & 13 deletions src/platform_impl/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ mod icon;
use std::sync::Once;

use cocoa::{
appkit::{NSButton, NSImage, NSStatusBar, NSStatusItem, NSVariableStatusItemLength, NSWindow},
appkit::{
NSButton, NSEvent, NSImage, NSStatusBar, NSStatusItem, NSVariableStatusItemLength, NSWindow,
},
base::{id, nil},
foundation::{NSData, NSInteger, NSPoint, NSRect, NSSize, NSString},
};
Expand Down Expand Up @@ -217,6 +219,19 @@ impl TrayIcon {
}
self.attrs.menu_on_left_click = enable;
}

pub fn rect(&self) -> Option<Rect> {
let ns_status_item = self.ns_status_item?;
unsafe {
let button = ns_status_item.button();
let window: id = button.window();
if window.is_null() {
None
} else {
Some(get_tray_rect(window))
}
}
}
}

impl Drop for TrayIcon {
Expand Down Expand Up @@ -351,18 +366,7 @@ fn make_tray_target_class() -> *const Class {

// icon position & size
let window: id = msg_send![event, window];
let frame = NSWindow::frame(window);
let scale_factor = NSWindow::backingScaleFactor(window);

let icon_rect = Rect {
size: crate::dpi::LogicalSize::new(frame.size.width, frame.size.height)
.to_physical(scale_factor),
position: crate::dpi::LogicalPosition::new(
frame.origin.x,
flip_window_screen_coordinates(frame.origin.y),
)
.to_physical(scale_factor),
};
let icon_rect = get_tray_rect(window);

// cursor position
let mouse_location: NSPoint = msg_send![class!(NSEvent), mouseLocation];
Expand Down Expand Up @@ -409,6 +413,21 @@ fn make_tray_target_class() -> *const Class {
unsafe { TRAY_CLASS }
}

fn get_tray_rect(window: id) -> Rect {
let frame = unsafe { NSWindow::frame(window) };
let scale_factor = unsafe { NSWindow::backingScaleFactor(window) };

Rect {
size: crate::dpi::LogicalSize::new(frame.size.width, frame.size.height)
.to_physical(scale_factor),
position: crate::dpi::LogicalPosition::new(
frame.origin.x,
flip_window_screen_coordinates(frame.origin.y),
)
.to_physical(scale_factor),
}
}

/// Core graphics screen coordinates are relative to the top-left corner of
/// the so-called "main" display, with y increasing downwards - which is
/// exactly what we want in Winit.
Expand Down
61 changes: 38 additions & 23 deletions src/platform_impl/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ impl TrayIcon {

Ok(())
}

pub fn rect(&self) -> Option<Rect> {
let dpi = unsafe { util::hwnd_dpi(self.hwnd) };
let scale_factor = util::dpi_to_scale_factor(dpi);
Some(get_tray_rect(self.internal_id, self.hwnd, scale_factor))
}
}

impl Drop for TrayIcon {
Expand Down Expand Up @@ -326,20 +332,6 @@ unsafe extern "system" fn tray_subclass_proc(
WM_LBUTTONUP | WM_RBUTTONUP | WM_LBUTTONDBLCLK
) =>
{
let nid = NOTIFYICONIDENTIFIER {
hWnd: hwnd,
cbSize: std::mem::size_of::<NOTIFYICONIDENTIFIER>() as _,
uID: subclass_input.internal_id,
..std::mem::zeroed()
};
let mut icon_rect = RECT {
left: 0,
bottom: 0,
right: 0,
top: 0,
};
Shell_NotifyIconGetRect(&nid, &mut icon_rect);

let mut cursor = POINT { x: 0, y: 0 };
GetCursorPos(&mut cursor as _);

Expand All @@ -359,15 +351,7 @@ unsafe extern "system" fn tray_subclass_proc(
TrayIconEvent::send(crate::TrayIconEvent {
id: subclass_input.id.clone(),
position: crate::dpi::LogicalPosition::new(x, y).to_physical(scale_factor),
icon_rect: Rect {
position: crate::dpi::LogicalPosition::new(icon_rect.left, icon_rect.top)
.to_physical(scale_factor),
size: crate::dpi::LogicalSize::new(
icon_rect.right.saturating_sub(icon_rect.left),
icon_rect.bottom.saturating_sub(icon_rect.top),
)
.to_physical(scale_factor),
},
icon_rect: get_tray_rect(subclass_input.internal_id, hwnd, scale_factor),
click_type: event,
});

Expand All @@ -383,6 +367,7 @@ unsafe extern "system" fn tray_subclass_proc(
DefSubclassProc(hwnd, msg, wparam, lparam)
}

#[inline]
unsafe fn show_tray_menu(hwnd: HWND, menu: HMENU, x: i32, y: i32) {
// bring the hidden window to the foreground so the pop up menu
// would automatically hide on click outside
Expand All @@ -399,6 +384,7 @@ unsafe fn show_tray_menu(hwnd: HWND, menu: HMENU, x: i32, y: i32) {
);
}

#[inline]
unsafe fn register_tray_icon(
hwnd: HWND,
tray_id: u32,
Expand Down Expand Up @@ -436,6 +422,7 @@ unsafe fn register_tray_icon(
Shell_NotifyIconW(NIM_ADD, &mut nid as _) == TRUE
}

#[inline]
unsafe fn remove_tray_icon(hwnd: HWND, id: u32) {
let mut nid = NOTIFYICONDATAW {
uFlags: NIF_ICON,
Expand All @@ -448,3 +435,31 @@ unsafe fn remove_tray_icon(hwnd: HWND, id: u32) {
eprintln!("Error removing system tray icon");
}
}

#[inline]
fn get_tray_rect(id: u32, hwnd: HWND, scale_factor: f64) -> Rect {
let nid = NOTIFYICONIDENTIFIER {
hWnd: hwnd,
cbSize: std::mem::size_of::<NOTIFYICONIDENTIFIER>() as _,
uID: id,
..unsafe { std::mem::zeroed() }
};

let mut icon_rect = RECT {
left: 0,
bottom: 0,
right: 0,
top: 0,
};
unsafe { Shell_NotifyIconGetRect(&nid, &mut icon_rect) };

Rect {
position: crate::dpi::LogicalPosition::new(icon_rect.left, icon_rect.top)
.to_physical(scale_factor),
size: crate::dpi::LogicalSize::new(
icon_rect.right.saturating_sub(icon_rect.left),
icon_rect.bottom.saturating_sub(icon_rect.top),
)
.to_physical(scale_factor),
}
}

0 comments on commit 599bb8f

Please sign in to comment.