feat: finalizations for M0-M2
This commit is contained in:
@@ -3,11 +3,11 @@ use iced::Task;
|
||||
use crate::app::Message;
|
||||
|
||||
/// Pick a folder using the native file dialog.
|
||||
pub fn pick_folder() -> Task<Message> {
|
||||
pub fn pick_folder(title: String) -> Task<Message> {
|
||||
Task::perform(
|
||||
async {
|
||||
async move {
|
||||
rfd::AsyncFileDialog::new()
|
||||
.set_title("Select Folder")
|
||||
.set_title(&title)
|
||||
.pick_folder()
|
||||
.await
|
||||
.map(|h| h.path().to_path_buf())
|
||||
@@ -17,13 +17,13 @@ pub fn pick_folder() -> Task<Message> {
|
||||
}
|
||||
|
||||
/// Pick one or more image/media files using the native file dialog.
|
||||
pub fn pick_media_files() -> Task<Message> {
|
||||
pub fn pick_media_files(title: String, filter_label: String) -> Task<Message> {
|
||||
Task::perform(
|
||||
async {
|
||||
async move {
|
||||
rfd::AsyncFileDialog::new()
|
||||
.set_title("Import Media")
|
||||
.set_title(&title)
|
||||
.add_filter(
|
||||
"Images",
|
||||
&filter_label,
|
||||
&["jpg", "jpeg", "png", "gif", "webp", "svg", "tiff", "bmp"],
|
||||
)
|
||||
.pick_files()
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc;
|
||||
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::ProtocolObject;
|
||||
use objc2::{define_class, msg_send, DefinedClass, MainThreadMarker, MainThreadOnly};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationDelegate};
|
||||
use objc2_foundation::{NSArray, NSNotification, NSObject, NSObjectProtocol, NSString, NSURL};
|
||||
|
||||
use crate::app::Message;
|
||||
|
||||
/// Events that arrive from macOS lifecycle hooks (openFile, openURLs).
|
||||
@@ -10,11 +16,75 @@ pub enum LifecycleEvent {
|
||||
UrlOpen(String),
|
||||
}
|
||||
|
||||
/// Create the macOS lifecycle channel (sender goes to objc hooks, receiver polled by subscription).
|
||||
/// Ivars for the delegate — holds the sender side of the lifecycle channel.
|
||||
#[derive(Debug)]
|
||||
pub struct DelegateIvars {
|
||||
tx: mpsc::Sender<LifecycleEvent>,
|
||||
}
|
||||
|
||||
define_class!(
|
||||
// SAFETY: NSObject has no subclassing requirements. BdsAppDelegate does not impl Drop.
|
||||
#[unsafe(super(NSObject))]
|
||||
#[thread_kind = MainThreadOnly]
|
||||
#[name = "BdsAppDelegate"]
|
||||
#[ivars = DelegateIvars]
|
||||
pub struct BdsAppDelegate;
|
||||
|
||||
unsafe impl NSObjectProtocol for BdsAppDelegate {}
|
||||
|
||||
unsafe impl NSApplicationDelegate for BdsAppDelegate {
|
||||
#[unsafe(method(applicationDidFinishLaunching:))]
|
||||
fn did_finish_launching(&self, _notification: &NSNotification) {
|
||||
// App is already running via Iced; nothing to do here.
|
||||
}
|
||||
|
||||
#[unsafe(method(application:openFile:))]
|
||||
fn application_open_file(&self, _sender: &NSApplication, filename: &NSString) -> bool {
|
||||
let path = PathBuf::from(filename.to_string());
|
||||
let _ = self.ivars().tx.send(LifecycleEvent::FileOpen(path));
|
||||
true
|
||||
}
|
||||
|
||||
#[unsafe(method(application:openURLs:))]
|
||||
fn application_open_urls(&self, _sender: &NSApplication, urls: &NSArray<NSURL>) {
|
||||
for i in 0..urls.len() {
|
||||
if let Some(url) = urls.objectAtIndex(i).absoluteString() {
|
||||
let _ = self.ivars().tx.send(LifecycleEvent::UrlOpen(url.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl BdsAppDelegate {
|
||||
fn new(mtm: MainThreadMarker, tx: mpsc::Sender<LifecycleEvent>) -> Retained<Self> {
|
||||
let this = Self::alloc(mtm);
|
||||
let this = this.set_ivars(DelegateIvars { tx });
|
||||
unsafe { msg_send![super(this), init] }
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the macOS lifecycle channel and wire the native delegate.
|
||||
///
|
||||
/// Returns `(Retained<BdsAppDelegate>, Receiver)`. The delegate must be kept alive
|
||||
/// for the lifetime of the application (drop it and the callbacks stop).
|
||||
pub fn lifecycle_channel() -> (mpsc::Sender<LifecycleEvent>, mpsc::Receiver<LifecycleEvent>) {
|
||||
mpsc::channel()
|
||||
}
|
||||
|
||||
/// Install the native Objective-C delegate on NSApplication, wiring it to the given sender.
|
||||
///
|
||||
/// Must be called from the main thread after the Iced application is running.
|
||||
/// Returns the retained delegate (caller must keep it alive).
|
||||
pub fn install_delegate(tx: mpsc::Sender<LifecycleEvent>) -> Option<Retained<BdsAppDelegate>> {
|
||||
let mtm = MainThreadMarker::new()?;
|
||||
let delegate = BdsAppDelegate::new(mtm, tx);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
let object = ProtocolObject::from_ref(&*delegate);
|
||||
app.setDelegate(Some(object));
|
||||
Some(delegate)
|
||||
}
|
||||
|
||||
/// Poll the macOS lifecycle receiver for the next event and map to a Message.
|
||||
pub fn poll_lifecycle(
|
||||
receiver: &mpsc::Receiver<LifecycleEvent>,
|
||||
|
||||
Reference in New Issue
Block a user