Add clipboard-∞ tray icon, fix tray click, and Settings gear.
Ship a macOS template menu-bar glyph, only toggle on left mouse-down so press/release no longer opens-then-closes, and put Settings next to Search so it is reachable without the tray menu.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 804 B After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 996 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 567 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 908 B After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 890 B After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 674 B After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" fill="none">
|
||||
<!-- Template glyph: black on transparent. macOS tints this in the menu bar. -->
|
||||
<g stroke="#000000" stroke-width="1.55" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4.2" y="4.2" width="13.6" height="15.6" rx="2"/>
|
||||
<rect x="8.2" y="1.9" width="5.6" height="2.6" rx="1.3"/>
|
||||
<path d="M6.6 13
|
||||
C 6.6 10.7, 8.4 10.7, 11 13
|
||||
C 13.6 15.3, 15.4 15.3, 15.4 13
|
||||
C 15.4 10.7, 13.6 10.7, 11 13
|
||||
C 8.4 15.3, 6.6 15.3, 6.6 13"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 579 B |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 361 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 486 B |
|
After Width: | Height: | Size: 873 B |
@@ -6,11 +6,13 @@ mod redact;
|
||||
|
||||
use commands::{DbState, LastHashState, PausedState};
|
||||
use db::Database;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use tauri::{
|
||||
image::Image,
|
||||
menu::{CheckMenuItem, Menu, MenuItem, PredefinedMenuItem},
|
||||
tray::TrayIconBuilder,
|
||||
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
||||
Emitter, Manager, WindowEvent,
|
||||
};
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
@@ -161,6 +163,26 @@ fn toggle_window(app: &tauri::AppHandle) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tray `Click` fires on both mouse-down and mouse-up. Only the down edge
|
||||
/// should toggle — otherwise press opens and release immediately closes.
|
||||
fn is_tray_primary_down(event: &TrayIconEvent) -> bool {
|
||||
matches!(
|
||||
event,
|
||||
TrayIconEvent::Click {
|
||||
button: MouseButton::Left,
|
||||
button_state: MouseButtonState::Down,
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn now_millis() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|d| d.as_millis() as u64)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn position_window(window: &tauri::WebviewWindow, position: &str) {
|
||||
let get_monitor_info =
|
||||
|w: &tauri::WebviewWindow| -> Option<(f64, f64, f64)> {
|
||||
@@ -271,6 +293,7 @@ pub fn run() {
|
||||
commands::set_hotkey,
|
||||
])
|
||||
.setup(move |app| {
|
||||
let ignore_blur_until = Arc::new(AtomicU64::new(0));
|
||||
let hotkey_str = db
|
||||
.get_settings()
|
||||
.map(|s| s.hotkey)
|
||||
@@ -290,8 +313,11 @@ pub fn run() {
|
||||
let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
|
||||
let menu = Menu::with_items(app, &[&show_i, &pause_i, &sep, &settings_i, &quit_i])?;
|
||||
|
||||
let tray_icon = Image::from_bytes(include_bytes!("../icons/menubarTemplate@2x.png"))
|
||||
.expect("embedded menu-bar template");
|
||||
let _tray = TrayIconBuilder::with_id("macopy-tray")
|
||||
.icon(app.default_window_icon().unwrap().clone())
|
||||
.icon(tray_icon)
|
||||
.icon_as_template(true)
|
||||
.menu(&menu)
|
||||
.show_menu_on_left_click(false)
|
||||
.tooltip("maCopy — Clipboard Manager")
|
||||
@@ -316,8 +342,15 @@ pub fn run() {
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.on_tray_icon_event(|tray, event| {
|
||||
if let tauri::tray::TrayIconEvent::Click { .. } = event {
|
||||
.on_tray_icon_event({
|
||||
let ignore_blur_until = ignore_blur_until.clone();
|
||||
move |tray, event| {
|
||||
if !is_tray_primary_down(&event) {
|
||||
return;
|
||||
}
|
||||
// Mouse-up lands on the menu bar and steals focus; don't
|
||||
// treat that as "clicked outside".
|
||||
ignore_blur_until.store(now_millis() + 500, Ordering::Relaxed);
|
||||
toggle_window(tray.app_handle());
|
||||
}
|
||||
})
|
||||
@@ -328,13 +361,17 @@ pub fn run() {
|
||||
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let w = window.clone();
|
||||
let ignore_blur_until = ignore_blur_until.clone();
|
||||
window.on_window_event(move |event| {
|
||||
if let WindowEvent::Focused(false) = event {
|
||||
if now_millis() < ignore_blur_until.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
// Delay hide so left-clicks inside the list finish before
|
||||
// macOS/webview focus churn can cancel them.
|
||||
let w2 = w.clone();
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(std::time::Duration::from_millis(180));
|
||||
std::thread::sleep(Duration::from_millis(180));
|
||||
if !w2.is_focused().unwrap_or(false) {
|
||||
let _ = w2.hide();
|
||||
}
|
||||
@@ -423,4 +460,32 @@ mod tests {
|
||||
assert!(parse_hotkey("cmd+banana").is_none());
|
||||
assert!(parse_hotkey("cmd+v+x").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tray_toggle_is_left_mouse_down_only() {
|
||||
let down = TrayIconEvent::Click {
|
||||
id: tauri::tray::TrayIconId::new("t"),
|
||||
position: tauri::PhysicalPosition::new(0.0, 0.0),
|
||||
rect: tauri::Rect::default(),
|
||||
button: MouseButton::Left,
|
||||
button_state: MouseButtonState::Down,
|
||||
};
|
||||
let up = TrayIconEvent::Click {
|
||||
id: tauri::tray::TrayIconId::new("t"),
|
||||
position: tauri::PhysicalPosition::new(0.0, 0.0),
|
||||
rect: tauri::Rect::default(),
|
||||
button: MouseButton::Left,
|
||||
button_state: MouseButtonState::Up,
|
||||
};
|
||||
let right = TrayIconEvent::Click {
|
||||
id: tauri::tray::TrayIconId::new("t"),
|
||||
position: tauri::PhysicalPosition::new(0.0, 0.0),
|
||||
rect: tauri::Rect::default(),
|
||||
button: MouseButton::Right,
|
||||
button_state: MouseButtonState::Down,
|
||||
};
|
||||
assert!(is_tray_primary_down(&down));
|
||||
assert!(!is_tray_primary_down(&up));
|
||||
assert!(!is_tray_primary_down(&right));
|
||||
}
|
||||
}
|
||||
|
||||