Change menu icon on macOS when disabled. Fix #432
This commit is contained in:
parent
ff13da9a85
commit
b23763d4de
|
@ -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
|
|
@ -26,15 +26,8 @@
|
||||||
// Setup status icon
|
// Setup status icon
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")]
|
||||||
|
|
|
@ -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();
|
||||||
|
|
BIN
src/res/mac/icondisabled.png
Normal file
BIN
src/res/mac/icondisabled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
Loading…
Reference in New Issue
Block a user