feat: update dependencies and refactor macOS icon handling

This commit is contained in:
2026-07-31 17:07:25 -05:00
parent 62a3526f33
commit 075a051f74
12 changed files with 124 additions and 49 deletions
Generated
+85 -8
View File
@@ -1405,10 +1405,12 @@ version = "0.1.0"
dependencies = [
"arboard",
"base64",
"cocoa",
"env_logger",
"log",
"muda",
"objc2 0.6.4",
"objc2-app-kit 0.3.2",
"objc2-foundation 0.3.2",
"png 0.18.1",
"rfd",
"serde",
@@ -2556,10 +2558,10 @@ dependencies = [
"block2 0.5.1",
"libc",
"objc2 0.5.2",
"objc2-core-data",
"objc2-core-image",
"objc2-core-data 0.2.2",
"objc2-core-image 0.2.2",
"objc2-foundation 0.2.2",
"objc2-quartz-core",
"objc2-quartz-core 0.2.2",
]
[[package]]
@@ -2570,10 +2572,17 @@ checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
dependencies = [
"bitflags 2.13.1",
"block2 0.6.2",
"libc",
"objc2 0.6.4",
"objc2-cloud-kit 0.3.2",
"objc2-core-data 0.3.2",
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-core-image 0.3.2",
"objc2-core-text",
"objc2-core-video",
"objc2-foundation 0.3.2",
"objc2-quartz-core 0.3.2",
]
[[package]]
@@ -2589,6 +2598,17 @@ dependencies = [
"objc2-foundation 0.2.2",
]
[[package]]
name = "objc2-cloud-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-foundation 0.3.2",
]
[[package]]
name = "objc2-contacts"
version = "0.2.2"
@@ -2612,6 +2632,17 @@ dependencies = [
"objc2-foundation 0.2.2",
]
[[package]]
name = "objc2-core-data"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-foundation 0.3.2",
]
[[package]]
name = "objc2-core-foundation"
version = "0.3.2"
@@ -2648,6 +2679,16 @@ dependencies = [
"objc2-metal",
]
[[package]]
name = "objc2-core-image"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006"
dependencies = [
"objc2 0.6.4",
"objc2-foundation 0.3.2",
]
[[package]]
name = "objc2-core-location"
version = "0.2.2"
@@ -2660,6 +2701,31 @@ dependencies = [
"objc2-foundation 0.2.2",
]
[[package]]
name = "objc2-core-text"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-core-foundation",
"objc2-core-graphics",
]
[[package]]
name = "objc2-core-video"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-io-surface",
]
[[package]]
name = "objc2-encode"
version = "4.1.0"
@@ -2740,6 +2806,17 @@ dependencies = [
"objc2-metal",
]
[[package]]
name = "objc2-quartz-core"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-foundation 0.3.2",
]
[[package]]
name = "objc2-symbols"
version = "0.2.2"
@@ -2759,13 +2836,13 @@ dependencies = [
"bitflags 2.13.1",
"block2 0.5.1",
"objc2 0.5.2",
"objc2-cloud-kit",
"objc2-core-data",
"objc2-core-image",
"objc2-cloud-kit 0.2.2",
"objc2-core-data 0.2.2",
"objc2-core-image 0.2.2",
"objc2-core-location",
"objc2-foundation 0.2.2",
"objc2-link-presentation",
"objc2-quartz-core",
"objc2-quartz-core 0.2.2",
"objc2-symbols",
"objc2-uniform-type-identifiers",
"objc2-user-notifications",
+3 -1
View File
@@ -25,4 +25,6 @@ base64 = "0.22"
url = { workspace = true }
[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.26"
objc2 = "0.6"
objc2-app-kit = "0.3"
objc2-foundation = "0.3"
+21 -25
View File
@@ -951,24 +951,21 @@ fn find_app_icon_file(app_path: &std::path::Path) -> Option<PathBuf> {
fn apply_dock_icon(icon: &DecodedIcon) {
#[cfg(target_os = "macos")]
{
use cocoa::appkit::{NSApplication, NSImage};
use cocoa::base::{id, nil};
use cocoa::foundation::NSData;
use objc2::AnyThread;
use objc2_app_kit::{NSApplication, NSImage};
use objc2_foundation::{MainThreadMarker, NSData};
let img_bytes = if !icon.rgba.is_empty() {
rgba_to_png(&icon.rgba, icon.width, icon.height).unwrap_or_else(|| icon.raw.clone())
} else {
icon.raw.clone()
};
unsafe {
let data = NSData::dataWithBytes_length_(
nil,
img_bytes.as_ptr() as *const std::ffi::c_void,
img_bytes.len() as u64,
);
let image: id = NSImage::initWithData_(NSImage::alloc(nil), data);
if image != nil {
let app = NSApplication::sharedApplication(nil);
let _: () = app.setApplicationIconImage_(image);
if let Some(mtm) = MainThreadMarker::new() {
let data = NSData::with_bytes(&img_bytes);
if let Some(image) = NSImage::initWithData(NSImage::alloc(), &data) {
let app = NSApplication::sharedApplication(mtm);
unsafe {
app.setApplicationIconImage(Some(&image));
}
}
}
}
@@ -1035,15 +1032,16 @@ fn detect_and_apply_icon(app_path: &std::path::Path, st: &mut AppState) {
#[cfg(target_os = "macos")]
fn apply_dock_icon_from_path(path: &std::path::Path) {
use cocoa::appkit::{NSApplication, NSImage};
use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
unsafe {
let ns_path = NSString::alloc(nil).init_str(&path.display().to_string());
let image: id = NSImage::initWithContentsOfFile_(NSImage::alloc(nil), ns_path);
if image != nil {
let app = NSApplication::sharedApplication(nil);
let _: () = app.setApplicationIconImage_(image);
use objc2::AnyThread;
use objc2_app_kit::{NSApplication, NSImage};
use objc2_foundation::{MainThreadMarker, NSString};
if let Some(mtm) = MainThreadMarker::new() {
let ns_path = NSString::from_str(&path.display().to_string());
if let Some(image) = NSImage::initWithContentsOfFile(NSImage::alloc(), &ns_path) {
let app = NSApplication::sharedApplication(mtm);
unsafe {
app.setApplicationIconImage(Some(&image));
}
}
}
}
@@ -1291,7 +1289,6 @@ fn handle_to_rust(
if let Ok(mut c) = arboard::Clipboard::new() {
use base64::Engine;
if let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(&data) {
use std::io::Read;
let cursor = std::io::Cursor::new(&bytes[..]);
let decoder = png::Decoder::new(cursor);
if let Ok(mut reader) = decoder.read_info() {
@@ -1589,7 +1586,6 @@ fn handle_to_rust(
pair.window.set_maximized(true);
}
}
_ => {}
}
}
@@ -1625,7 +1621,7 @@ mod tests {
bmp[8..12].copy_from_slice(&(w as i32).to_le_bytes());
bmp[14..16].copy_from_slice(&32u16.to_le_bytes());
for y in (0..w).rev() {
for x in 0..w {
for _x in 0..w {
let p = pixels[y];
bmp.extend_from_slice(&[p[2], p[1], p[0], p[3]]);
}
+2 -6
View File
@@ -12,7 +12,6 @@ static IPC_LISTENERS: once_cell::sync::Lazy<Mutex<HashMap<String, Vec<IpcListene
#[derive(Clone, Debug)]
struct IpcListener {
id: u32,
channel: String,
}
static LISTENER_COUNTER: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(1);
@@ -51,7 +50,7 @@ pub fn ipc_main_on(channel: String, _callback: JsFunction) -> Result<i64> {
listeners
.entry(channel.clone())
.or_insert_with(Vec::new)
.push(IpcListener { id, channel });
.push(IpcListener { id });
Ok(id as i64)
}
@@ -146,10 +145,7 @@ pub fn ipc_renderer_on(channel: String, _callback: JsFunction) -> Result<i64> {
listeners
.entry(format!("renderer:{}", channel))
.or_insert_with(Vec::new)
.push(IpcListener {
id,
channel: channel.clone(),
});
.push(IpcListener { id });
log::debug!("ipcRenderer.on registered for channel: {}", &channel);
Ok(id as i64)
}
+1
View File
@@ -62,6 +62,7 @@ pub fn tray_set_pressed_image(_tray_id: u32, _image_json: Option<String>) -> Res
}
#[napi]
#[allow(non_snake_case)]
pub fn tray_setContextMenu(tray_id: u32, menu_id: Option<u32>) -> Result<()> {
log::info!("Tray {} context menu set to {:?}", tray_id, menu_id);
Ok(())
@@ -111,12 +111,14 @@ pub fn web_contents_get_session_id(window_id: u32) -> Result<String> {
}
#[napi]
#[allow(non_snake_case)]
pub fn web_contents_set_windowOpenHandler(window_id: u32) -> Result<()> {
log::debug!("Window {} set window open handler", window_id);
Ok(())
}
#[napi]
#[allow(non_snake_case)]
pub fn web_contents_set_permissionRequestHandler(window_id: u32) -> Result<()> {
log::debug!("Window {} set permission request handler", window_id);
Ok(())
@@ -146,6 +148,7 @@ pub fn web_contents_set_ignore_menu_shortcuts(window_id: u32, ignore: bool) -> R
}
#[napi]
#[allow(non_snake_case)]
pub fn web_contents_setAudioMuted(window_id: u32, muted: bool) -> Result<()> {
log::debug!("Window {} audio muted={}", window_id, muted);
Ok(())
+1 -1
View File
@@ -335,7 +335,7 @@ impl InnerWebView {
Ok(())
}
pub fn id(&self) -> crate::WebViewId {
pub fn id(&self) -> crate::WebViewId<'_> {
&self.id
}
+1 -1
View File
@@ -1558,7 +1558,7 @@ impl WebView {
}
/// Returns the id of this webview.
pub fn id(&self) -> WebViewId {
pub fn id(&self) -> WebViewId<'_> {
self.webview.id()
}
+1 -1
View File
@@ -568,7 +568,7 @@ impl InnerWebView {
is_inspector_open
}
pub fn id(&self) -> crate::WebViewId {
pub fn id(&self) -> crate::WebViewId<'_> {
&self.id
}
+1 -1
View File
@@ -1263,7 +1263,7 @@ impl InnerWebView {
/// Public APIs
impl InnerWebView {
pub fn id(&self) -> crate::WebViewId {
pub fn id(&self) -> crate::WebViewId<'_> {
&self.id
}
+2 -2
View File
@@ -87,13 +87,13 @@ pub(crate) fn download_did_finish(this: &WryDownloadDelegate, download: &WKDownl
pub(crate) fn download_did_fail(
this: &WryDownloadDelegate,
download: &WKDownload,
error: &NSError,
_error: &NSError,
_resume_data: &NSData,
) {
unsafe {
#[cfg(debug_assertions)]
{
let description = error.localizedDescription().to_string();
let description = _error.localizedDescription().to_string();
eprintln!("Download failed with error: {}", description);
}
+3 -3
View File
@@ -34,7 +34,7 @@ use dpi::{LogicalPosition, LogicalSize};
use objc2::runtime::Bool;
use objc2::{
rc::Retained,
runtime::{AnyObject, NSObject, ProtocolObject},
runtime::{AnyObject, ProtocolObject},
ClassType, DeclaredClass,
};
#[cfg(target_os = "macos")]
@@ -387,7 +387,7 @@ impl InnerWebView {
#[cfg(any(debug_assertions, feature = "devtools"))]
if attributes.devtools {
let has_inspectable_property: bool =
NSObject::respondsToSelector(&webview, objc2::sel!(setInspectable:));
objc2::runtime::NSObject::respondsToSelector(&webview, objc2::sel!(setInspectable:));
if has_inspectable_property {
webview.setInspectable(true);
}
@@ -538,7 +538,7 @@ r#"Object.defineProperty(window, 'ipc', {
}
}
pub fn id(&self) -> crate::WebViewId {
pub fn id(&self) -> crate::WebViewId<'_> {
&self.id
}