diff --git a/Cargo.lock b/Cargo.lock index a961a24..0567db1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,28 @@ version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" +[[package]] +name = "async-recursion" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -20,6 +42,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + [[package]] name = "cairo-rs" version = "0.14.7" @@ -62,10 +90,19 @@ dependencies = [ "smallvec", ] +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "echidna" version = "0.1.0" dependencies = [ + "async-recursion", + "async-trait", + "futures", "gdk4", "gio", "glib", @@ -75,6 +112,7 @@ dependencies = [ "serde", "serde_json", "sourceview5", + "tokio", ] [[package]] @@ -94,25 +132,41 @@ dependencies = [ ] [[package]] -name = "futures-channel" -version = "0.3.17" +name = "futures" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +checksum = "8cd0210d8c325c245ff06fd95a3b13689a1a276ac8cfa8e8720cb840bfb84b9e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" +checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445" [[package]] name = "futures-executor" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" +checksum = "7b808bf53348a36cab739d7e04755909b9fcaaa69b7d7e588b37b6ec62704c97" dependencies = [ "futures-core", "futures-task", @@ -121,25 +175,46 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" +checksum = "e481354db6b5c353246ccf6a728b0c5511d752c08da7260546fc0933869daa11" + +[[package]] +name = "futures-macro" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89f17b21645bc4ed773c69af9c9a0effd4a3f1a3876eadd453469f8854e7fdd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "996c6442437b62d21a32cd9906f9c41e7dc1e19a9579843fad948696769305af" [[package]] name = "futures-task" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" +checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12" [[package]] name = "futures-util" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e" dependencies = [ - "autocfg", + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -410,6 +485,24 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "itertools" version = "0.10.1" @@ -431,6 +524,30 @@ version = "0.2.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103" +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + [[package]] name = "memoffset" version = "0.6.4" @@ -440,6 +557,47 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "once_cell" version = "1.8.0" @@ -471,6 +629,31 @@ dependencies = [ "system-deps 3.2.0", ] +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + [[package]] name = "pest" version = "2.1.3" @@ -550,6 +733,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + [[package]] name = "relative-path" version = "1.5.0" @@ -571,6 +763,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "semver" version = "0.11.0" @@ -620,6 +818,15 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.4" @@ -767,6 +974,37 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml" version = "0.5.8" diff --git a/Cargo.toml b/Cargo.toml index 3265340..bdc71c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,8 @@ serde = { version = "^1.0.130", features = ["derive"] } relative-path = "^1.5.0" gdk = { version = "^0.3.0", package = "gdk4"} sourceview = { package = "sourceview5", version = "^0.1.0", git = "https://github.com/EchidnaHQ/sourceview" } -once_cell = "1" \ No newline at end of file +once_cell = "1" +async-recursion = "0.3.2" +async-trait = "0.1.7" +futures = "0.3" +tokio = { version = "1", features = ["full"]} \ No newline at end of file diff --git a/src/components/mod.rs b/src/components/mod.rs index 5353e5a..234d52a 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -15,5 +15,5 @@ pub use tab_label::TabLabel; pub use window::EchidnaWindow; pub mod prelude { - pub use super::window::{file::*, menubar::*}; + pub use super::window::{file::*, menubar::*, workspace::*}; } diff --git a/src/components/sidebar/imp.rs b/src/components/sidebar/imp.rs index cc41ea5..2bc2ad4 100644 --- a/src/components/sidebar/imp.rs +++ b/src/components/sidebar/imp.rs @@ -8,7 +8,10 @@ use gtk::CompositeTemplate; #[derive(Default, CompositeTemplate)] #[template(file = "./sidebar.ui")] -pub struct EchidnaSidebar {} +pub struct EchidnaSidebar { + #[template_child] + pub explorer: TemplateChild, +} #[glib::object_subclass] impl ObjectSubclass for EchidnaSidebar { diff --git a/src/components/sidebar/mod.rs b/src/components/sidebar/mod.rs index 7aa464a..b9c623e 100644 --- a/src/components/sidebar/mod.rs +++ b/src/components/sidebar/mod.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ pub mod imp; +use gtk::subclass::prelude::*; glib::wrapper! { pub struct EchidnaSidebar(ObjectSubclass) @@ -14,4 +15,8 @@ impl EchidnaSidebar { pub fn new() -> Self { glib::Object::new(&[]).expect("Failed to create 'EchidnaSidebar' component.") } + + pub fn to_imp(&self) -> &imp::EchidnaSidebar { + imp::EchidnaSidebar::from_instance(self) + } } diff --git a/src/components/sidebar/sidebar.ui b/src/components/sidebar/sidebar.ui index 372a406..dd5b36d 100644 --- a/src/components/sidebar/sidebar.ui +++ b/src/components/sidebar/sidebar.ui @@ -17,18 +17,14 @@ 1 170 - + explorer Explorer GTK_ORIENTATION_VERTICAL - - - Explorer - - + diff --git a/src/components/window/menubar.rs b/src/components/window/menubar.rs index f51f47f..9e6fc79 100644 --- a/src/components/window/menubar.rs +++ b/src/components/window/menubar.rs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use super::file::FileImplementedEditor; use super::EchidnaWindow; +use crate::prelude::*; use gio::{MenuModel, SimpleAction}; use glib::clone; use gtk::prelude::*; @@ -122,5 +122,15 @@ impl MenubarImplementedEditor for EchidnaWindow { window.action_save_file_as(); })); } + { + let action_open_workspace = SimpleAction::new("open-workspace", None); + + self.add_action(&action_open_workspace); + + action_open_workspace.connect_activate(clone!(@weak self as window => + move |_action, _variant| { + window.action_open_workspace(); + })); + } } } diff --git a/src/components/window/mod.rs b/src/components/window/mod.rs index 40ed731..d07b5cb 100644 --- a/src/components/window/mod.rs +++ b/src/components/window/mod.rs @@ -5,7 +5,7 @@ pub mod file; pub mod imp; pub mod menubar; - +pub mod workspace; use glib::object::IsA; use gtk::subclass::prelude::*; diff --git a/src/components/window/workspace.rs b/src/components/window/workspace.rs deleted file mode 100644 index 7df8a13..0000000 --- a/src/components/window/workspace.rs +++ /dev/null @@ -1,172 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use super::imp::EchidnaEditor; -use gio::Cancellable; -use gio::{File, FileQueryInfoFlags, FileType, SimpleAction}; -use glib::clone; -use glib::subclass::types::ObjectSubclassExt; -use glib::types::Type; -use gtk::prelude::*; -use gtk::{ApplicationWindow, FileChooserAction, FileChooserNative, ResponseType, TreeStore}; -use relative_path::RelativePath; -use serde::{Deserialize, Serialize}; -use std::path::Path; - -#[derive(Deserialize, Serialize)] -struct MonacoFolder { - path: String, -} - -#[derive(Deserialize, Serialize)] -struct MonacoWorkspace { - folders: Vec, -} - -trait WorkspaceImplementedEditor { - fn action_open_workspace( - &self, - window: ApplicationWindow, - app: super::EchidnaEditor, - _action: &SimpleAction, - _variant: Option<&glib::Variant>, - ); - - fn open_workspace(&self, file: File); - fn recursive_add_files_into_tree_store(&self, parent_file: File, tree: &TreeStore); - fn open_folder(&self, file: File); -} - -impl WorkspaceImplementedEditor for EchidnaEditor { - fn action_open_workspace( - &self, - window: ApplicationWindow, - app: super::EchidnaEditor, - _action: &SimpleAction, - _variant: Option<&glib::Variant>, - ) { - let dialog = FileChooserNative::new( - Some("Open a file"), - Some(&window), - FileChooserAction::Open, - Some("Open"), - Some("Cancel"), - ); - dialog.set_visible(true); - dialog.connect_response(clone!(@weak window, @weak app => - move |dialog, response| { - if response == ResponseType::Accept { - let file_option = dialog.file(); - match file_option { - Some(file) => { - dialog.destroy(); - Self::from_instance(&app).open_workspace(file); - }, - None => {} - } - } else if response == ResponseType::Cancel { - dialog.destroy(); - } - } - )); - } - /** - * __Open Workspace__ - * - * Basically, this is just the same as Open Folder, but it's many folders. - * - * - Open a FileChooserNative, set to only view .code-workspace files. - * - If the user pressed cancel, destroy the dialog. If the user opened a .code-workspace file: - * - Get the workspace file, load and parse its content, .code-workspace files are in JSON with comments. But JSON only should be fine, for now. - * - Iterate over folders listed in the workspace file. - * - Find the absolute path of the folder: Relative to the workspace file. - * - Create a GFile instance of that path, and call open_folder(file: File) and pass the GFile instance to it. - * - */ - fn open_workspace(&self, file: File) { - let cancellable = Cancellable::new(); - let filepath_raw = &file - .path() - .expect("Could not get the file path of the file."); - let filepath = Path::new(&filepath_raw); - let info = file - .query_info("*", gio::FileQueryInfoFlags::NONE, Some(&cancellable)) - .expect(format!( - "Could not retrieve file information for {:?}", - filepath - )); - let content_type = info - .content_type() - .expect(format!("Found no content type for {:?}", filepath)); - println!( - "Opened {} and found its content type is {}.", - "file", - content_type.to_string() - ); - let content_cancellable = Cancellable::new(); - let content = file - .load_contents(Some(&content_cancellable)) - .expect("Could not load the file contents for {:?}", filepath); - - let (int_vec, _byte_string) = content; - let workspace = serde_json::from_slice::(&int_vec).expect(format!( - "Could not parse the workspace file of {:?}", - filepath - )); - - for folder in workspace.folders { - let path = RelativePath::new(&folder.path); - let folder = File::for_path(path.to_path(filepath)); - - // Do something with the folder, perhaps lists its child and . - self.open_folder(folder); - } - } - - /** - * - * - */ - fn recursive_add_files_into_tree_store(&self, parent_file: File, tree: &TreeStore) { - let child_enumerate_cancellable = Cancellable::new(); - let child_files = parent_file - .enumerate_children( - "*", - FileQueryInfoFlags::NONE, - Some(&child_enumerate_cancellable), - ) - .expect( - format!( - "Could not look up the children files of {:?} because:\n{:#?}", - filepath - ) - .as_str(), - ); - let filepath = &parent_file - .path() - .expect("Could not get the file path of the file."); - - for file_iter in files { - let file_info = file_iter.expect(); - let file = parent_file.child(file_info.name()); - let tree_iter = tree.append(None); - tree.set_value(&tree_iter, 2, &file_info.name().to_str().to_value()); - - if file_info.file_type() == FileType::Directory { - self.recursive_add_files_into_tree_store(file, tree); - } - } - } - - /* - Loads a folder into the tree view. - - - Create a new tree - - Enumerate over child files of 'file'. (PS: In the Unix family of OS-es, directories are files too) - */ - fn open_folder(&self, file: File) { - let tree = TreeStore::new(&[gdk::Texture::static_type(), Type::STRING]); - self.recursive_add_files_into_tree_store(file, &tree); - } -} diff --git a/src/components/window/workspace/mod.rs b/src/components/window/workspace/mod.rs new file mode 100644 index 0000000..ed638b5 --- /dev/null +++ b/src/components/window/workspace/mod.rs @@ -0,0 +1,136 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +mod open_folder; + +pub use open_folder::*; +mod recursive_add_files; +use super::EchidnaWindow; +use core::pin::Pin; +use futures::stream::FuturesUnordered; +use gio::{Cancellable, File}; +use glib::clone; +use gtk::{prelude::*, FileChooserAction, FileChooserNative, ResponseType}; +use relative_path::RelativePath; +use serde::{Deserialize, Serialize}; +use std::path::Path; +use tokio::runtime; + +#[derive(Deserialize, Serialize)] +struct MonacoFolder { + path: String, +} + +#[derive(Deserialize, Serialize)] +struct MonacoWorkspace { + folders: Vec, +} + +pub trait WorkspaceImplementedEditor { + fn action_open_workspace(&self); + + fn open_workspace(&self, file: File); +} + +impl WorkspaceImplementedEditor for EchidnaWindow { + fn action_open_workspace(&self) { + let dialog: FileChooserNative = FileChooserNative::new( + Some("Open a file"), + Some(self), + FileChooserAction::Open, + Some("Open"), + Some("Cancel"), + ); + dialog.set_visible(true); + dialog.connect_response(clone!(@weak self as win => + move |dialog, response| { + if response == ResponseType::Accept { + let file_option = dialog.file(); + match file_option { + Some(file) => { + dialog.destroy(); + win.open_workspace(file); + }, + None => {} + } + } else if response == ResponseType::Cancel { + dialog.destroy(); + } + } + )); + } + /** + * __Open Workspace__ + * + * Basically, this is just the same as Open Folder, but it's many folders. + * + * - Open a FileChooserNative, set to only view .code-workspace files. + * - If the user pressed cancel, destroy the dialog. If the user opened a .code-workspace file: + * - Get the workspace file, load and parse its content, .code-workspace files are - - Create a GFile instance of that path, and call open_folder(file: File) and pass the GFile instance to it. + * + */ + fn open_workspace(&self, file: File) { + let cancellable = Cancellable::new(); + let filepath_raw = &file + .path() + .expect("Could not get the file path of the file."); + let filepath = Path::new(&filepath_raw); + let info = file + .query_info("*", gio::FileQueryInfoFlags::NONE, Some(&cancellable)) + .expect(format!("Could not retrieve file information for {:?}", filepath).as_str()); + let content_type = info + .content_type() + .expect(format!("Found no content type for {:?}", filepath).as_str()); + println!( + "Opened {} and found its content type is {}.", + "file", + content_type.to_string() + ); + let content_cancellable = Cancellable::new(); + let (content, _) = file + .load_contents(Some(&content_cancellable)) + .expect(format!("Could not load the file contents for {:?}", filepath).as_str()); + + let workspace = serde_json::from_slice::(&content) + .expect(format!("Could not parse the workspace file of {:?}", filepath).as_str()); + let explorer_box: >k::Box = &self + .to_imp() + .sidebar + .to_imp() + .explorer + .child() + .downcast() + .expect("Could not downcast the Explorer activity tab's child widget to GtkBox."); + let folder_futures_unordered: FuturesUnordered<_> = workspace.folders.iter().map(|folder_path|{ + let path = RelativePath::new(&folder_path.path); + let folder = File::for_path( + path.to_path( + filepath + .parent() + .expect("Could not get the parent of 'filepath'. 'filepath' terminates in a root or prefix.") + ) + ); + + println!("Pushing {:?} into the futures for opening.", folder.path()); + + // Do something with the folder, perhaps lists its child and . + open_folder(&explorer_box, folder) + }).collect(); + + let rt = runtime::Runtime::new().unwrap(); + + for future in Pin::new(&folder_futures_unordered).iter_pin_ref() { + + /* + TODO: Implement a connection between the main thread and the Tokio runtime. + + GTK4 is not thread-safe thus it does not implement Sync on its objects, which is a requirement for tokio Runtime::spawn(). + + Or maybe using Tokio is stupid for this and we should just use another runtime that doesn't require Send, which idk if there are any atm. + */ + + // rt.spawn(future); + } + } +} diff --git a/src/components/window/workspace/open_folder.rs b/src/components/window/workspace/open_folder.rs new file mode 100644 index 0000000..f437657 --- /dev/null +++ b/src/components/window/workspace/open_folder.rs @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use super::recursive_add_files::*; +use gio::File; +use glib::Type; +use gtk::{prelude::*, TreeStore, TreeView}; + +pub async fn open_folder(explorer_box: >k::Box, file: File) { + let tree = TreeStore::new(&[gdk::Texture::static_type(), Type::STRING]); + println!("Opening folder {:?}", file.path()); + recursive_add_files_into_tree_store(&file, &tree, None).await; + + let tree_view = TreeView::new(); + + tree_view.set_model(Some(&tree)); + + explorer_box.prepend(&tree_view); +} diff --git a/src/components/window/workspace/recursive_add_files.rs b/src/components/window/workspace/recursive_add_files.rs new file mode 100644 index 0000000..0db23ee --- /dev/null +++ b/src/components/window/workspace/recursive_add_files.rs @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use async_recursion::async_recursion; +use gio::{Cancellable, File, FileQueryInfoFlags, FileType}; +use gtk::{prelude::*, TreeIter, TreeStore}; +#[async_recursion(?Send)] +pub async fn recursive_add_files_into_tree_store( + parent_file: &File, + tree: &TreeStore, + parent_iter: Option<&'async_recursion TreeIter>, +) { + let child_enumerate_cancellable = Cancellable::new(); + + println!( + "Adding file {:?} and if it's a folder, with its children, to the tree store.", + parent_file.path() + ); + let child_files = parent_file + .enumerate_children( + "*", + FileQueryInfoFlags::NONE, + Some(&child_enumerate_cancellable), + ) + .expect( + format!( + "Could not look up the children files of {:?} because:", + parent_file.path().expect("No path for the parent file") + ) + .as_str(), + ); + for file_iter in child_files { + let file_info = file_iter.expect("Could not get the file info"); + let file = parent_file.child(file_info.name()); + + println!( + "Found child {:?} of {:?}", + file_info.name(), + parent_file.path() + ); + + let tree_iter = tree.append(parent_iter); + tree.set_value(&tree_iter, 1, &file_info.name().to_str().to_value()); + if file_info.file_type() == FileType::Directory { + recursive_add_files_into_tree_store(&file, tree, Some(&tree_iter)).await; + } + } +}