Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macOS: drag-n-drop folder into the app icon #6456

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions assets/macos/WezTerm.app/Contents/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@
<string>public.unix-executable</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>public.directory</string>
<string>com.apple.bundle</string>
<string>com.apple.resolvable</string>
</array>
</dict>
</array>
</dict>
</plist>
41 changes: 41 additions & 0 deletions wezterm-gui/src/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,47 @@ impl GuiFrontEnd {
})
.detach();
}
ApplicationEvent::OpenDirectory(path) => {
promise::spawn::spawn(async move {
use config::keyassignment::SpawnTabDomain;
use wezterm_term::TerminalSize;

// We send the script to execute to the shell on stdin, rather than ask the
// shell to execute it directly, so that we start the shell and read in the
// user's rc files before running the script. Without this, wezterm on macOS
// is launched with a default and very anemic path, and that is frustrating for
// users.

let mux = Mux::get();
let window_id = None;
let pane_id = None;
let cmd = None;
let cwd = Some(path.clone());
let workspace = mux.active_workspace();

match mux
.spawn_tab_or_window(
window_id,
SpawnTabDomain::DomainName("local".to_string()),
cmd,
cwd,
TerminalSize::default(),
pane_id,
workspace,
None, // optional position
)
.await
{
Ok((_tab, pane, _window_id)) => {
log::trace!("Spawned {path} as pane_id {}", pane.pane_id());
}
Err(err) => {
log::error!("Failed to spawn {path}: {err:#?}");
}
};
})
.detach();
}
ApplicationEvent::PerformKeyAssignment(action) => {
// We should only get here when there are no windows open
// and the user picks an action from the menubar.
Expand Down
1 change: 1 addition & 0 deletions window/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub fn shutdown() {
pub enum ApplicationEvent {
/// The system wants to open a command in the terminal
OpenCommandScript(String),
OpenDirectory(String),
PerformKeyAssignment(KeyAssignment),
}

Expand Down
52 changes: 27 additions & 25 deletions window/src/os/macos/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::menu::{Menu, MenuItem};
use crate::{ApplicationEvent, Connection};
use cocoa::appkit::NSApplicationTerminateReply;
use cocoa::base::id;
use cocoa::foundation::NSArray;
use cocoa::foundation::NSInteger;
use config::keyassignment::KeyAssignment;
use config::WindowCloseConfirmation;
Expand Down Expand Up @@ -68,27 +69,27 @@ extern "C" fn application_will_finish_launching(
log::debug!("application_will_finish_launching");
}

extern "C" fn application_did_finish_launching(this: &mut Object, _sel: Sel, _notif: *mut Object) {
extern "C" fn application_did_finish_launching(_self: &mut Object, _sel: Sel, _notif: *mut Object) {
log::debug!("application_did_finish_launching");
unsafe {
(*this).set_ivar("launched", YES);
}
}

extern "C" fn application_open_untitled_file(
this: &mut Object,
_self: &mut Object,
_sel: Sel,
_app: *mut Object,
app: *mut Object,
) -> BOOL {
let launched: BOOL = unsafe { *this.get_ivar("launched") };
log::debug!("application_open_untitled_file launched={launched}");
if let Some(conn) = Connection::get() {
if launched == YES {
conn.dispatch_app_event(ApplicationEvent::PerformKeyAssignment(
KeyAssignment::SpawnWindow,
));
log::debug!("application_open_untitled_file");
unsafe {
let windows: id = msg_send![app, windows];
let windows_count = windows.count();
if windows_count == 0 {
if let Some(conn) = Connection::get() {
conn.dispatch_app_event(ApplicationEvent::PerformKeyAssignment(
KeyAssignment::SpawnWindow,
));
return YES;
}
}
return YES;
}
NO
}
Expand All @@ -113,19 +114,23 @@ extern "C" fn wezterm_perform_key_assignment(
}

extern "C" fn application_open_file(
this: &mut Object,
_self: &mut Object,
_sel: Sel,
_app: *mut Object,
file_name: *mut Object,
) {
let launched: BOOL = unsafe { *this.get_ivar("launched") };
if launched == YES {
let file_name = unsafe { nsstring_to_str(file_name) }.to_string();
if let Some(conn) = Connection::get() {
log::debug!("application_open_file {file_name}");
) -> BOOL {
let file_name = unsafe { nsstring_to_str(file_name) }.to_string();
let path = std::path::Path::new(&file_name);
if let Some(conn) = Connection::get() {
log::debug!("application_open_file {file_name}");
if path.is_dir() {
conn.dispatch_app_event(ApplicationEvent::OpenDirectory(file_name));
} else {
conn.dispatch_app_event(ApplicationEvent::OpenCommandScript(file_name));
}
return YES;
}
NO
}

extern "C" fn application_dock_menu(
Expand All @@ -147,8 +152,6 @@ fn get_class() -> &'static Class {
let mut cls = ClassDecl::new(CLS_NAME, class!(NSWindow))
.expect("Unable to register application class");

cls.add_ivar::<BOOL>("launched");

unsafe {
cls.add_method(
sel!(applicationShouldTerminate:),
Expand All @@ -164,7 +167,7 @@ fn get_class() -> &'static Class {
);
cls.add_method(
sel!(application:openFile:),
application_open_file as extern "C" fn(&mut Object, Sel, *mut Object, *mut Object),
application_open_file as extern "C" fn(&mut Object, Sel, *mut Object, *mut Object) -> BOOL,
);
cls.add_method(
sel!(applicationDockMenu:),
Expand All @@ -191,7 +194,6 @@ pub fn create_app_delegate() -> StrongPtr {
unsafe {
let delegate: *mut Object = msg_send![cls, alloc];
let delegate: *mut Object = msg_send![delegate, init];
(*delegate).set_ivar("launched", NO);
StrongPtr::new(delegate)
}
}