Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: simplify TrayIconEvent in JS by tagging it with type field #11121

Merged
merged 12 commits into from
Sep 26, 2024
5 changes: 0 additions & 5 deletions .changes/api-tray-doubleClick-event.md

This file was deleted.

7 changes: 7 additions & 0 deletions .changes/api-tray-event-refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"tauri": "patch:breaking"
"@tauri-apps/api": "patch:breaking"
---

Simplified emitted tray event JS value and updated `TrayIconEvent` type definition to match it.

6 changes: 0 additions & 6 deletions .changes/api-tray-icon-event-value-mismatch-type.md

This file was deleted.

4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ specta = { version = "^2.0.0-rc.16", optional = true, default-features = false,

[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
muda = { version = "0.15", default-features = false, features = ["serde"] }
tray-icon = { version = "0.18", default-features = false, features = [
tray-icon = { version = "0.19", default-features = false, features = [
"serde",
], optional = true }

Expand Down
2 changes: 1 addition & 1 deletion crates/tauri/scripts/bundle.global.js

Large diffs are not rendered by default.

51 changes: 50 additions & 1 deletion crates/tauri/src/tray/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl From<tray_icon::MouseButton> for MouseButton {
/// - **Linux**: Unsupported. The event is not emmited even though the icon is shown
/// and will still show a context menu on right click.
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "type")]
#[non_exhaustive]
pub enum TrayIconEvent {
/// A click happened on the tray icon.
Expand Down Expand Up @@ -563,3 +563,52 @@ impl<R: Runtime> Resource for TrayIcon<R> {
self.app_handle.remove_tray_by_id(&self.id);
}
}

#[cfg(test)]
mod tests {
#[test]
fn tray_event_json_serialization() {
// NOTE: if this test is ever changed, you probably need to change `TrayIconEvent` in JS as well

use super::*;
let event = TrayIconEvent::Click {
button: MouseButton::Left,
button_state: MouseButtonState::Down,
id: TrayIconId::new("id"),
position: crate::PhysicalPosition::default(),
rect: crate::Rect {
position: tray_icon::Rect::default().position.into(),
size: tray_icon::Rect::default().size.into(),
},
};

let value = serde_json::to_value(&event).unwrap();
assert_eq!(
value,
serde_json::json!({
"type": "Click",
"button": "Left",
"buttonState": "Down",
"id": "id",
"position": {
"x": 0.0,
"y": 0.0,
},
"rect": {
"size": {
"Physical": {
"width": 0,
"height": 0,
}
},
"position": {
"Physical": {
"x": 0,
"y": 0,
}
},
}
})
);
}
}
159 changes: 47 additions & 112 deletions packages/api/src/tray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,86 +9,32 @@ import { PhysicalPosition, PhysicalSize } from './dpi'

export type MouseButtonState = 'Up' | 'Down'
export type MouseButton = 'Left' | 'Right' | 'Middle'
export type TrayIconEventType =
| 'Click'
| 'DoubleClick'
| 'Enter'
| 'Move'
| 'Leave'

/** A click happened on the tray icon. */
export interface TrayIconClickEvent {
export type TrayIconEventBase<T extends TrayIconEventType> = {
/** The tray icon event type */
type: T
/** Id of the tray icon which triggered this event. */
id: string
/** Physical X Position of the click the triggered this event. */
x: number
/** Physical Y Position of the click the triggered this event. */
y: number
/** Physical position of the click the triggered this event. */
position: PhysicalPosition
/** Position and size of the tray icon. */
rect: {
position: PhysicalPosition
size: PhysicalSize
}
/** Mouse button that triggered this event. */
button: MouseButton
/** Mouse button state when this event was triggered. */
buttonState: MouseButtonState
}

/** A double click happened on the tray icon. **Windows Only** */
export interface TrayIconDoubleClickEvent {
/** Id of the tray icon which triggered this event. */
id: string
/** Physical X Position of the click the triggered this event. */
x: number
/** Physical Y Position of the click the triggered this event. */
y: number
/** Position and size of the tray icon. */
rect: {
position: PhysicalPosition
size: PhysicalSize
}
export type TrayIconClickEvent = {
/** Mouse button that triggered this event. */
button: MouseButton
}

/** The mouse entered the tray icon region. */
export interface TrayIconEnterEvent {
/** Id of the tray icon which triggered this event. */
id: string
/** Physical X Position of the click the triggered this event. */
x: number
/** Physical Y Position of the click the triggered this event. */
y: number
/** Position and size of the tray icon. */
rect: {
position: PhysicalPosition
size: PhysicalSize
}
}

/** The mouse moved over the tray icon region. */
export interface TrayIconMoveEvent {
/** Id of the tray icon which triggered this event. */
id: string
/** Physical X Position of the click the triggered this event. */
x: number
/** Physical Y Position of the click the triggered this event. */
y: number
/** Position and size of the tray icon. */
rect: {
position: PhysicalPosition
size: PhysicalSize
}
}

/** The mouse left the tray icon region. */
export interface TrayIconLeaveEvent {
/** Id of the tray icon which triggered this event. */
id: string
/** Physical X Position of the click the triggered this event. */
x: number
/** Physical Y Position of the click the triggered this event. */
y: number
/** Position and size of the tray icon. */
rect: {
position: PhysicalPosition
size: PhysicalSize
}
/** Mouse button state when this event was triggered. */
buttonState: MouseButtonState
}

/**
Expand All @@ -100,11 +46,22 @@ export interface TrayIconLeaveEvent {
* the icon will still show a context menu on right click.
*/
export type TrayIconEvent =
| { click: TrayIconClickEvent }
| { doubleClick: TrayIconDoubleClickEvent }
| { enter: TrayIconEnterEvent }
| { move: TrayIconMoveEvent }
| { leave: TrayIconLeaveEvent }
| (TrayIconEventBase<'Click'> & TrayIconClickEvent)
| (TrayIconEventBase<'DoubleClick'> & Omit<TrayIconClickEvent, 'buttonState'>)
| TrayIconEventBase<'Enter'>
| TrayIconEventBase<'Move'>
| TrayIconEventBase<'Leave'>

type RustTrayIconEvent = Omit<TrayIconEvent, 'rect'> & {
rect: {
position: {
Physical: { x: number; y: number }
}
size: {
Physical: { width: number; height: number }
}
}
}

/**
* Tray icon types and utilities.
Expand Down Expand Up @@ -223,38 +180,10 @@ export class TrayIcon extends Resource {
options.icon = transformImage(options.icon)
}

const handler = new Channel<TrayIconEvent>()
const handler = new Channel<RustTrayIconEvent>()
if (options?.action) {
const action = options.action
handler.onmessage = (e) => {
if ('click' in e) {
// @ts-expect-error `TrayIconEvent` doesn't quite match the value yet so we reconstruct the incorrect fields
e.click.rect.position = mapPosition(e.click.rect.position)
// @ts-expect-error `TrayIconEvent` doesn't quite match the value yet so we reconstruct the incorrect fields
e.click.rect.size = mapSize(e.click.rect.size)
} else if ('doubleClick' in e) {
// @ts-expect-error `TrayIconEvent` doesn't quite match the value yet so we reconstruct the incorrect fields
e.doubleClick.rect.position = mapPosition(e.doubleClick.rect.position)
// @ts-expect-error `TrayIconEvent` doesn't quite match the value yet so we reconstruct the incorrect fields
e.doubleClick.rect.size = mapSize(e.doubleClick.rect.size)
} else if ('enter' in e) {
// @ts-expect-error `TrayIconEvent` doesn't quite match the value yet so we reconstruct the incorrect fields
e.enter.rect.position = mapPosition(e.enter.rect.position)
// @ts-expect-error `TrayIconEvent` doesn't quite match the value yet so we reconstruct the incorrect fields
e.enter.rect.size = mapSize(e.enter.rect.size)
} else if ('move' in e) {
// @ts-expect-error `TrayIconEvent` doesn't quite match the value yet so we reconstruct the incorrect fields
e.move.rect.position = mapPosition(e.move.rect.position)
// @ts-expect-error `TrayIconEvent` doesn't quite match the value yet so we reconstruct the incorrect fields
e.move.rect.size = mapSize(e.move.rect.size)
} else if ('leave' in e) {
// @ts-expect-error `TrayIconEvent` doesn't quite match the value yet so we reconstruct the incorrect fields
e.leave.rect.position = mapPosition(e.leave.rect.position)
// @ts-expect-error `TrayIconEvent` doesn't quite match the value yet so we reconstruct the incorrect fields
e.leave.rect.size = mapSize(e.leave.rect.size)
}
action(e)
}
handler.onmessage = (e) => action(mapEvent(e))
delete options.action
}

Expand Down Expand Up @@ -358,13 +287,19 @@ export class TrayIcon extends Resource {
}
}

function mapPosition(pos: {
Physical: { x: number; y: number }
}): PhysicalPosition {
return new PhysicalPosition(pos.Physical.x, pos.Physical.y)
}
function mapSize(pos: {
Physical: { width: number; height: number }
}): PhysicalSize {
return new PhysicalSize(pos.Physical.width, pos.Physical.height)
function mapEvent(e: RustTrayIconEvent): TrayIconEvent {
const out = e as unknown as TrayIconEvent

out.position = new PhysicalPosition(e.position.x, e.position.y)

out.rect.position = new PhysicalPosition(
e.rect.position.Physical.x,
e.rect.position.Physical.y
)
out.rect.size = new PhysicalSize(
e.rect.size.Physical.width,
e.rect.size.Physical.height
)

return out
}