WIP: feat: implement workspaces support #12

Draft
fortressia wants to merge 12 commits from feat/workspaces into main
12 changed files with 486 additions and 197 deletions

266
Cargo.lock generated
View File

@ -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"

View File

@ -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"
once_cell = "1"
async-recursion = "0.3.2"
async-trait = "0.1.7"
futures = "0.3"
tokio = { version = "1", features = ["full"]}

View File

@ -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::*};
}

View File

@ -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<gtk::StackPage>,
}
#[glib::object_subclass]
impl ObjectSubclass for EchidnaSidebar {

View File

@ -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<imp::EchidnaSidebar>)
@ -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)
}
}

View File

@ -17,18 +17,14 @@
<property name="hhomogeneous">1</property>
<property name="width-request">170</property>
<child>
<object class="GtkStackPage">
<object class="GtkStackPage" id="explorer">
<!-- Explorer Tab -->
<property name="name">explorer</property>
<property name="title">Explorer</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<object class="GtkLabel">
<property name="label">Explorer</property>
</object>
</child>
</object>
</property>

View File

@ -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();
}));
}
}
}

View File

@ -5,7 +5,7 @@
pub mod file;
pub mod imp;
pub mod menubar;
pub mod workspace;
use glib::object::IsA;
use gtk::subclass::prelude::*;

View File

@ -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<MonacoFolder>,
}
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::<MonacoWorkspace>(&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);
}
}

View File

@ -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<MonacoFolder>,
}
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::<MonacoWorkspace>(&content)
.expect(format!("Could not parse the workspace file of {:?}", filepath).as_str());
let explorer_box: &gtk::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);
}
}
}

View File

@ -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: &gtk::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);
}

View File

@ -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;
}
}
}