Change menu icon on macOS when disabled. Fix #432

This commit is contained in:
Federico Terzi 2020-09-22 22:23:39 +02:00
parent ff13da9a85
commit b23763d4de
9 changed files with 92 additions and 14 deletions

View File

@ -29,5 +29,7 @@
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification; - (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
- (IBAction) statusIconClick: (id) sender; - (IBAction) statusIconClick: (id) sender;
- (IBAction) contextMenuClick: (id) sender; - (IBAction) contextMenuClick: (id) sender;
- (void) updateIcon: (char *)iconPath;
- (void) setIcon: (char *)iconPath;
@end @end

View File

@ -27,14 +27,7 @@
if (show_icon) { if (show_icon) {
myStatusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain]; myStatusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
NSString *nsIconPath = [NSString stringWithUTF8String:icon_path]; [self setIcon: icon_path];
NSImage *statusImage = [[NSImage alloc] initWithContentsOfFile:nsIconPath];
[statusImage setTemplate:YES];
[myStatusItem.button setImage:statusImage];
[myStatusItem setHighlightMode:YES];
[myStatusItem.button setAction:@selector(statusIconClick:)];
[myStatusItem.button setTarget:self];
} }
// Setup key listener // Setup key listener
@ -66,6 +59,29 @@
}]; }];
} }
- (void) updateIcon: (char *)iconPath {
if (show_icon) {
[myStatusItem release];
[self setIcon: iconPath];
}
}
- (void) setIcon: (char *)iconPath {
if (show_icon) {
myStatusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
NSString *nsIconPath = [NSString stringWithUTF8String:iconPath];
NSImage *statusImage = [[NSImage alloc] initWithContentsOfFile:nsIconPath];
[statusImage setTemplate:YES];
[myStatusItem.button setImage:statusImage];
[myStatusItem setHighlightMode:YES];
[myStatusItem.button setAction:@selector(statusIconClick:)];
[myStatusItem.button setTarget:self];
}
}
- (IBAction) statusIconClick: (id) sender { - (IBAction) statusIconClick: (id) sender {
icon_click_callback(context_instance); icon_click_callback(context_instance);
} }

View File

@ -26,12 +26,13 @@ extern "C" {
extern void * context_instance; extern void * context_instance;
extern char * icon_path; extern char * icon_path;
extern char * disabled_icon_path;
extern int32_t show_icon; extern int32_t show_icon;
/* /*
* Initialize the AppDelegate and check for accessibility permissions * Initialize the AppDelegate and check for accessibility permissions
*/ */
int32_t initialize(void * context, const char * icon_path, int32_t show_icon); int32_t initialize(void * context, const char * icon_path, const char * disabled_icon_path, int32_t show_icon);
/* /*
* Start the event loop indefinitely. Blocking call. * Start the event loop indefinitely. Blocking call.
@ -117,6 +118,11 @@ typedef void (*ContextMenuClickCallback)(void * self, int32_t id);
extern ContextMenuClickCallback context_menu_click_callback; extern ContextMenuClickCallback context_menu_click_callback;
extern "C" void register_context_menu_click_callback(ContextMenuClickCallback callback); extern "C" void register_context_menu_click_callback(ContextMenuClickCallback callback);
/*
* Update the tray icon status
*/
extern "C" void update_tray_icon(int32_t enabled);
// SYSTEM // SYSTEM
/* /*

View File

@ -33,6 +33,7 @@ extern "C" {
void * context_instance; void * context_instance;
char * icon_path; char * icon_path;
char * disabled_icon_path;
int32_t show_icon; int32_t show_icon;
AppDelegate * delegate_ptr; AppDelegate * delegate_ptr;
@ -40,9 +41,10 @@ KeypressCallback keypress_callback;
IconClickCallback icon_click_callback; IconClickCallback icon_click_callback;
ContextMenuClickCallback context_menu_click_callback; ContextMenuClickCallback context_menu_click_callback;
int32_t initialize(void * context, const char * _icon_path, int32_t _show_icon) { int32_t initialize(void * context, const char * _icon_path, const char * _disabled_icon_path, int32_t _show_icon) {
context_instance = context; context_instance = context;
icon_path = strdup(_icon_path); icon_path = strdup(_icon_path);
disabled_icon_path = strdup(_disabled_icon_path);
show_icon = _show_icon; show_icon = _show_icon;
AppDelegate *delegate = [[AppDelegate alloc] init]; AppDelegate *delegate = [[AppDelegate alloc] init];
@ -74,6 +76,18 @@ int32_t headless_eventloop() {
return 0; return 0;
} }
void update_tray_icon(int32_t enabled) {
dispatch_async(dispatch_get_main_queue(), ^(void) {
NSApplication * application = [NSApplication sharedApplication];
char * iconPath = icon_path;
if (!enabled) {
iconPath = disabled_icon_path;
}
[[application delegate] updateIcon: iconPath];
});
}
void send_string(const char * string) { void send_string(const char * string) {
char * stringCopy = strdup(string); char * stringCopy = strdup(string);
dispatch_async(dispatch_get_main_queue(), ^(void) { dispatch_async(dispatch_get_main_queue(), ^(void) {

View File

@ -29,7 +29,12 @@ pub struct MacMenuItem {
#[allow(improper_ctypes)] #[allow(improper_ctypes)]
#[link(name = "macbridge", kind = "static")] #[link(name = "macbridge", kind = "static")]
extern "C" { extern "C" {
pub fn initialize(s: *const c_void, icon_path: *const c_char, show_icon: i32); pub fn initialize(
s: *const c_void,
icon_path: *const c_char,
disabled_icon_path: *const c_char,
show_icon: i32,
);
pub fn eventloop(); pub fn eventloop();
pub fn headless_eventloop(); pub fn headless_eventloop();
@ -51,6 +56,7 @@ extern "C" {
pub fn register_icon_click_callback(cb: extern "C" fn(_self: *mut c_void)); pub fn register_icon_click_callback(cb: extern "C" fn(_self: *mut c_void));
pub fn show_context_menu(items: *const MacMenuItem, count: i32) -> i32; pub fn show_context_menu(items: *const MacMenuItem, count: i32) -> i32;
pub fn register_context_menu_click_callback(cb: extern "C" fn(_self: *mut c_void, id: i32)); pub fn register_context_menu_click_callback(cb: extern "C" fn(_self: *mut c_void, id: i32));
pub fn update_tray_icon(enabled: i32);
// Keyboard // Keyboard
pub fn register_keypress_callback( pub fn register_keypress_callback(

View File

@ -34,6 +34,7 @@ use std::sync::Arc;
use std::{fs, thread}; use std::{fs, thread};
const STATUS_ICON_BINARY: &[u8] = include_bytes!("../res/mac/icon.png"); const STATUS_ICON_BINARY: &[u8] = include_bytes!("../res/mac/icon.png");
const DISABLED_STATUS_ICON_BINARY: &[u8] = include_bytes!("../res/mac/icondisabled.png");
pub struct MacContext { pub struct MacContext {
pub send_channel: Sender<Event>, pub send_channel: Sender<Event>,
@ -72,6 +73,7 @@ impl MacContext {
// Initialize the status icon path // Initialize the status icon path
let espanso_dir = super::get_data_dir(); let espanso_dir = super::get_data_dir();
let status_icon_target = espanso_dir.join("icon.png"); let status_icon_target = espanso_dir.join("icon.png");
let disabled_status_icon_target = espanso_dir.join("icondisabled.png");
if status_icon_target.exists() { if status_icon_target.exists() {
info!("Status icon already initialized, skipping."); info!("Status icon already initialized, skipping.");
@ -84,6 +86,19 @@ impl MacContext {
}); });
} }
if disabled_status_icon_target.exists() {
info!("Status icon (disabled) already initialized, skipping.");
} else {
fs::write(&disabled_status_icon_target, DISABLED_STATUS_ICON_BINARY).unwrap_or_else(
|e| {
error!(
"Error copying the Status Icon (disabled) to the espanso data directory: {}",
e
);
},
);
}
unsafe { unsafe {
let context_ptr = &*context as *const MacContext as *const c_void; let context_ptr = &*context as *const MacContext as *const c_void;
@ -93,9 +108,17 @@ impl MacContext {
let status_icon_path = let status_icon_path =
CString::new(status_icon_target.to_str().unwrap_or_default()).unwrap_or_default(); CString::new(status_icon_target.to_str().unwrap_or_default()).unwrap_or_default();
let disabled_status_icon_path =
CString::new(disabled_status_icon_target.to_str().unwrap_or_default())
.unwrap_or_default();
let show_icon = if config.show_icon { 1 } else { 0 }; let show_icon = if config.show_icon { 1 } else { 0 };
initialize(context_ptr, status_icon_path.as_ptr(), show_icon); initialize(
context_ptr,
status_icon_path.as_ptr(),
disabled_status_icon_path.as_ptr(),
show_icon,
);
} }
context context
@ -146,6 +169,12 @@ impl MacContext {
} }
} }
pub fn update_icon(enabled: bool) {
unsafe {
crate::bridge::macos::update_tray_icon(if enabled { 1 } else { 0 });
}
}
impl super::Context for MacContext { impl super::Context for MacContext {
fn eventloop(&self) { fn eventloop(&self) {
// Start the SecureInput watcher thread // Start the SecureInput watcher thread

View File

@ -50,7 +50,7 @@ pub fn new(
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn update_icon(enabled: bool) { pub fn update_icon(enabled: bool) {
// TODO: add update icon on macOS macos::update_icon(enabled);
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]

View File

@ -498,9 +498,14 @@ impl<
if config.secure_input_notification && config.show_notifications { if config.secure_input_notification && config.show_notifications {
self.ui_manager.notify_delay(&format!("{} has activated SecureInput. Espanso won't work until you disable it.", app_name), 5000); self.ui_manager.notify_delay(&format!("{} has activated SecureInput. Espanso won't work until you disable it.", app_name), 5000);
} }
crate::context::update_icon(false);
} }
SystemEvent::SecureInputDisabled => { SystemEvent::SecureInputDisabled => {
info!("SecureInput has been disabled."); info!("SecureInput has been disabled.");
let is_enabled = self.enabled.borrow();
crate::context::update_icon(*is_enabled);
} }
SystemEvent::NotifyRequest(message) => { SystemEvent::NotifyRequest(message) => {
let config = self.config_manager.default_config(); let config = self.config_manager.default_config();

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB