Skip to content

Commit de7734c

Browse files
committed
Support opening PCBoard files from macOS Finder
1 parent 2c7d4ca commit de7734c

3 files changed

Lines changed: 90 additions & 30 deletions

File tree

‎Cargo.lock‎

Lines changed: 30 additions & 30 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎crates/icy_draw/build/mac/Info.plist‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,20 @@
2828
<array>
2929
<string>MacOSX</string>
3030
</array>
31+
<key>CFBundleDocumentTypes</key>
32+
<array>
33+
<dict>
34+
<key>CFBundleTypeExtensions</key>
35+
<array>
36+
<string>pcb</string>
37+
</array>
38+
<key>CFBundleTypeName</key>
39+
<string>PCBoard Document</string>
40+
<key>CFBundleTypeRole</key>
41+
<string>Editor</string>
42+
<key>LSHandlerRank</key>
43+
<string>Alternate</string>
44+
</dict>
45+
</array>
3146
</dict>
3247
</plist>

‎crates/icy_draw/src/window_manager.rs‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,19 @@ fn extract_paths_from_drag_drop(format: &str, data: &[u8]) -> Vec<PathBuf> {
152152
paths
153153
}
154154

155+
#[cfg(test)]
156+
mod tests {
157+
use super::file_uri_to_pathbuf;
158+
use std::path::PathBuf;
159+
160+
#[test]
161+
fn converts_macos_document_urls_to_paths() {
162+
assert_eq!(file_uri_to_pathbuf("file:///Users/test/My%20File.pcb"), Some(PathBuf::from("/Users/test/My File.pcb")));
163+
assert_eq!(file_uri_to_pathbuf("file://localhost/Users/test/foo.pcb"), Some(PathBuf::from("/Users/test/foo.pcb")));
164+
assert_eq!(file_uri_to_pathbuf("https://example.com/foo.pcb"), None);
165+
}
166+
}
167+
155168
#[derive(Clone, Debug)]
156169
pub enum WindowManagerMessage {
157170
OpenWindow,
@@ -185,6 +198,8 @@ pub enum WindowManagerMessage {
185198
McpCommand(Arc<McpCommand>),
186199
/// File was dropped onto the window (with Ctrl state for new window)
187200
FileDropped(window::Id, PathBuf, bool),
201+
/// Document URL received from Finder or Launch Services on macOS.
202+
OpenDocumentUrl(String),
188203
}
189204

190205
fn start_session_save_worker() -> mpsc::Sender<SaveRequest> {
@@ -934,6 +949,35 @@ impl WindowManager {
934949
// No unsaved changes - open directly
935950
return Task::done(WindowManagerMessage::WindowMessage(window_id, crate::ui::Message::FileOpened(path)));
936951
}
952+
953+
WindowManagerMessage::OpenDocumentUrl(url) => {
954+
let Some(path) = file_uri_to_pathbuf(&url) else {
955+
return Task::batch([mcp_task]);
956+
};
957+
958+
// Reuse the initial empty window when the application has just
959+
// been launched by Finder. Otherwise, preserve the current
960+
// document and open the requested file in a new window.
961+
if self.windows.len() == 1 {
962+
let (&window_id, window) = self.windows.first_key_value().expect("one window");
963+
if window.file_path().is_none() && !window.is_modified() {
964+
return Task::done(WindowManagerMessage::WindowMessage(
965+
window_id,
966+
crate::ui::Message::FileOpened(path),
967+
));
968+
}
969+
}
970+
971+
self.pending_restores.push(WindowRestoreInfo {
972+
original_path: Some(path),
973+
load_path: None,
974+
mark_dirty: false,
975+
position: None,
976+
size: (DEFAULT_SIZE.width, DEFAULT_SIZE.height),
977+
session_data_path: None,
978+
});
979+
return Task::done(WindowManagerMessage::OpenWindow);
980+
}
937981
};
938982

939983
Task::batch([main_task, mcp_task])
@@ -1017,6 +1061,7 @@ impl WindowManager {
10171061
_ => None,
10181062
}
10191063
}),
1064+
icy_ui::event::listen_url().map(WindowManagerMessage::OpenDocumentUrl),
10201065
// Autosave tick - check every second
10211066
icy_ui::time::every(std::time::Duration::from_secs(1)).map(|_| WindowManagerMessage::AutosaveTick),
10221067
];

0 commit comments

Comments
 (0)