From bea36918f4920ffd665be23f3c2bbe09c0f0b3ad Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Fri, 16 Feb 2024 13:34:54 -0500 Subject: [PATCH] Linux: file dialogs (#7852) This PR implements linux file dialogs and open/reveal actions. | Open folder | Reveal path | | --- | --- | | ![Screenshot from 2024-02-15 16-50-49](https://github.com/zed-industries/zed/assets/1423607/b4260574-d841-4ded-821d-521f507916d1) | ![Screenshot from 2024-02-15 16-51-36](https://github.com/zed-industries/zed/assets/1423607/1f32f451-7def-423a-9d69-de2876285b60) | --------- Co-authored-by: Mikayla Maki --- Cargo.lock | 404 +++++++++++++++++++-- crates/gpui/Cargo.toml | 2 + crates/gpui/src/platform/linux/platform.rs | 69 +++- 3 files changed, 435 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 064192a69f..137d0c4c7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,6 +300,24 @@ dependencies = [ "raw-window-metal", ] +[[package]] +name = "ashpd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01992ad7774250d5b7fe214e2676cb99bf92564436d8135ab44fe815e71769a9" +dependencies = [ + "async-fs 2.1.1", + "async-net 2.0.0", + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "serde", + "serde_repr", + "url", + "zbus", +] + [[package]] name = "assets" version = "0.1.0" @@ -356,11 +374,21 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d26004fe83b2d1cd3a97609b21e39f9a31535822210fe83205d2ce48866ea61" dependencies = [ - "event-listener", + "event-listener 2.5.3", "futures-core", "parking_lot 0.12.1", ] +[[package]] +name = "async-broadcast" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +dependencies = [ + "event-listener 2.5.3", + "futures-core", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -368,7 +396,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 2.5.3", "futures-core", ] @@ -404,11 +432,11 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ - "async-lock", + "async-lock 2.8.0", "async-task", "concurrent-queue", "fastrand 1.9.0", - "futures-lite", + "futures-lite 1.13.0", "slab", ] @@ -418,10 +446,21 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" dependencies = [ - "async-lock", + "async-lock 2.8.0", "autocfg", "blocking", - "futures-lite", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-fs" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" +dependencies = [ + "async-lock 3.3.0", + "blocking", + "futures-lite 2.0.0", ] [[package]] @@ -432,10 +471,10 @@ checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ "async-channel", "async-executor", - "async-io", - "async-lock", + "async-io 1.13.0", + "async-lock 2.8.0", "blocking", - "futures-lite", + "futures-lite 1.13.0", "once_cell", ] @@ -445,11 +484,11 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "async-lock", + "async-lock 2.8.0", "autocfg", "cfg-if 1.0.0", "concurrent-queue", - "futures-lite", + "futures-lite 1.13.0", "log", "parking", "polling 2.8.0", @@ -459,13 +498,43 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "async-io" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" +dependencies = [ + "async-lock 3.3.0", + "cfg-if 1.0.0", + "concurrent-queue", + "futures-io", + "futures-lite 2.0.0", + "parking", + "polling 3.3.2", + "rustix 0.38.30", + "slab", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "async-lock" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "event-listener", + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy", + "pin-project-lite 0.2.13", ] [[package]] @@ -486,10 +555,21 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" dependencies = [ - "async-io", + "async-io 1.13.0", "autocfg", "blocking", - "futures-lite", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io 2.3.1", + "blocking", + "futures-lite 2.0.0", ] [[package]] @@ -507,13 +587,13 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" dependencies = [ - "async-io", - "async-lock", + "async-io 1.13.0", + "async-lock 2.8.0", "autocfg", "blocking", "cfg-if 1.0.0", - "event-listener", - "futures-lite", + "event-listener 2.5.3", + "futures-lite 1.13.0", "rustix 0.37.23", "signal-hook", "windows-sys 0.48.0", @@ -549,13 +629,13 @@ checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-channel", "async-global-executor", - "async-io", - "async-lock", + "async-io 1.13.0", + "async-lock 2.8.0", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite", + "futures-lite 1.13.0", "gloo-timers", "kv-log-macro", "log", @@ -1384,11 +1464,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" dependencies = [ "async-channel", - "async-lock", + "async-lock 2.8.0", "async-task", "atomic-waker", "fastrand 1.9.0", - "futures-lite", + "futures-lite 1.13.0", "log", ] @@ -1562,7 +1642,7 @@ name = "call" version = "0.1.0" dependencies = [ "anyhow", - "async-broadcast", + "async-broadcast 0.4.1", "audio", "client", "collections", @@ -3058,6 +3138,27 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "enumflags2" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -3141,6 +3242,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.13", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite 0.2.13", +] + [[package]] name = "extension" version = "0.1.0" @@ -3691,6 +3813,21 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-lite" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1155db57329dca6d018b61e76b1488ce9a2e5e44028cac420a5898f4fcef63" +dependencies = [ + "fastrand 2.0.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite 0.2.13", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.30" @@ -3935,6 +4072,7 @@ version = "0.1.0" dependencies = [ "anyhow", "as-raw-xcb-connection", + "ashpd", "async-task", "backtrace", "bindgen 0.65.1", @@ -3969,6 +4107,7 @@ dependencies = [ "metal 0.21.0", "num_cpus", "objc", + "open", "ordered-float 2.10.0", "parking", "parking_lot 0.11.2", @@ -4531,6 +4670,25 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "isahc" version = "1.7.2" @@ -4543,8 +4701,8 @@ dependencies = [ "curl", "curl-sys", "encoding_rs", - "event-listener", - "futures-lite", + "event-listener 2.5.3", + "futures-lite 1.13.0", "http 0.2.9", "log", "mime", @@ -4730,7 +4888,7 @@ name = "language" version = "0.1.0" dependencies = [ "anyhow", - "async-broadcast", + "async-broadcast 0.4.1", "async-trait", "client", "clock", @@ -4987,7 +5145,7 @@ name = "live_kit_client" version = "0.1.0" dependencies = [ "anyhow", - "async-broadcast", + "async-broadcast 0.4.1", "async-trait", "block", "byteorder", @@ -5239,6 +5397,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -5606,6 +5773,18 @@ dependencies = [ "libc", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", + "memoffset 0.7.1", +] + [[package]] name = "nix" version = "0.27.1" @@ -5978,6 +6157,17 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "open" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "openssl" version = "0.10.57" @@ -6040,6 +6230,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite 0.2.13", +] + [[package]] name = "os_str_bytes" version = "6.5.1" @@ -6232,6 +6432,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pathfinder_color" version = "0.5.0" @@ -7499,7 +7705,7 @@ name = "rpc" version = "0.1.0" dependencies = [ "anyhow", - "async-lock", + "async-lock 2.8.0", "async-tungstenite", "base64 0.13.1", "clock", @@ -8469,13 +8675,13 @@ checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" dependencies = [ "async-channel", "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-net", + "async-fs 1.6.0", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-net 1.7.0", "async-process", "blocking", - "futures-lite", + "futures-lite 1.13.0", ] [[package]] @@ -8484,7 +8690,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "847d777e2c6c166bad26264479e80a9820f3d364fcb4a0e23cd57bbfa8e94961" dependencies = [ - "async-io", + "async-io 1.13.0", "pin-project-lite 0.1.12", ] @@ -8636,7 +8842,7 @@ dependencies = [ "crossbeam-queue", "dotenvy", "either", - "event-listener", + "event-listener 2.5.3", "futures-channel", "futures-core", "futures-intrusive", @@ -10287,6 +10493,17 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset 0.9.0", + "tempfile", + "winapi 0.3.9", +] + [[package]] name = "ui" version = "0.1.0" @@ -11480,6 +11697,16 @@ dependencies = [ "quick-xml 0.30.0", ] +[[package]] +name = "xdg-home" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "xkbcommon" version = "0.7.0" @@ -11534,6 +11761,72 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "zbus" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c45d06ae3b0f9ba1fb2671268b975557d8f5a84bb5ec6e43964f87e763d8bca8" +dependencies = [ + "async-broadcast 0.5.1", + "async-executor", + "async-fs 1.6.0", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-process", + "async-recursion 1.0.5", + "async-task", + "async-trait", + "blocking", + "byteorder", + "derivative", + "enumflags2", + "event-listener 2.5.3", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.26.4", + "once_cell", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "winapi 0.3.9", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a1ba45ed0ad344b85a2bb5a1fe9830aed23d67812ea39a586e7d0136439c7d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zed" version = "0.124.0" @@ -11770,3 +12063,42 @@ dependencies = [ "libc", "pkg-config", ] + +[[package]] +name = "zvariant" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 7679761291..3421f3388e 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -96,6 +96,8 @@ objc = "0.2" [target.'cfg(target_os = "linux")'.dependencies] flume = "0.11" +open = "5.0.1" +ashpd = "0.7.0" # todo!(linux) - Technically do not use `randr`, but it doesn't compile otherwise xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr", "xkb"] } wayland-client= { version = "0.31.2" } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 330cc84aa2..e6f9dc4894 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -8,6 +8,7 @@ use std::{ time::Duration, }; +use ashpd::desktop::file_chooser::{OpenFileRequest, SaveFileRequest}; use async_task::Runnable; use flume::{Receiver, Sender}; use futures::channel::oneshot; @@ -214,7 +215,7 @@ impl Platform for LinuxPlatform { } fn open_url(&self, url: &str) { - unimplemented!() + open::that(url); } fn on_open_urls(&self, callback: Box)>) { @@ -225,15 +226,75 @@ impl Platform for LinuxPlatform { &self, options: PathPromptOptions, ) -> oneshot::Receiver>> { - unimplemented!() + let (done_tx, done_rx) = oneshot::channel(); + self.foreground_executor() + .spawn(async move { + let title = if options.multiple { + if !options.files { + "Open folders" + } else { + "Open files" + } + } else { + if !options.files { + "Open folder" + } else { + "Open file" + } + }; + + let result = OpenFileRequest::default() + .modal(true) + .title(title) + .accept_label("Select") + .multiple(options.multiple) + .directory(options.directories) + .send() + .await + .ok() + .and_then(|request| request.response().ok()) + .and_then(|response| { + response + .uris() + .iter() + .map(|uri| uri.to_file_path().ok()) + .collect() + }); + + done_tx.send(result); + }) + .detach(); + done_rx } fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver> { - unimplemented!() + let (done_tx, done_rx) = oneshot::channel(); + let directory = directory.to_owned(); + self.foreground_executor() + .spawn(async move { + let result = SaveFileRequest::default() + .modal(true) + .title("Select new path") + .accept_label("Accept") + .send() + .await + .ok() + .and_then(|request| request.response().ok()) + .and_then(|response| { + response + .uris() + .first() + .and_then(|uri| uri.to_file_path().ok()) + }); + + done_tx.send(result); + }) + .detach(); + done_rx } fn reveal_path(&self, path: &Path) { - unimplemented!() + open::that(path); } fn on_become_active(&self, callback: Box) {