diff --git a/Cargo.lock b/Cargo.lock
index 3574567..55532cf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8,6 +8,12 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+[[package]]
+name = "bitflags"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+
[[package]]
name = "cc"
version = "1.2.16"
@@ -19,13 +25,19 @@ dependencies = [
"shlex",
]
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
[[package]]
name = "clang-format"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "696283b40e1a39d208ee614b92e5f6521d16962edeb47c48372585ec92419943"
dependencies = [
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -62,6 +74,7 @@ dependencies = [
"cxx-qt",
"cxx-qt-build",
"cxx-qt-lib",
+ "dirs",
"log",
]
@@ -121,7 +134,7 @@ dependencies = [
"cxx-qt-macro",
"qt-build-utils",
"static_assertions",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -210,6 +223,27 @@ dependencies = [
"syn",
]
+[[package]]
+name = "dirs"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys",
+]
+
[[package]]
name = "either"
version = "1.15.0"
@@ -222,6 +256,17 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
[[package]]
name = "indoc"
version = "2.0.6"
@@ -258,6 +303,16 @@ version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags",
+ "libc",
+]
+
[[package]]
name = "link-cplusplus"
version = "1.0.10"
@@ -295,6 +350,12 @@ dependencies = [
"minimal-lexical",
]
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
[[package]]
name = "proc-macro2"
version = "1.0.94"
@@ -311,7 +372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efb239fdd8c036fabb95364320041ef68197cd4ab971bb3b4ca3ea0b7b93d12c"
dependencies = [
"cc",
- "thiserror",
+ "thiserror 1.0.69",
"versions",
]
@@ -324,6 +385,17 @@ dependencies = [
"proc-macro2",
]
+[[package]]
+name = "redox_users"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror 2.0.12",
+]
+
[[package]]
name = "rustversion"
version = "1.0.20"
@@ -412,7 +484,16 @@ version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
- "thiserror-impl",
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl 2.0.12",
]
[[package]]
@@ -426,6 +507,17 @@ dependencies = [
"syn",
]
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "unicode-ident"
version = "1.0.18"
@@ -460,6 +552,12 @@ dependencies = [
"nom",
]
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
[[package]]
name = "winapi-util"
version = "0.1.9"
diff --git a/Cargo.toml b/Cargo.toml
index 8b7c81c..c039058 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,7 @@ cxx = "1.0.95"
cxx-qt = "0.7"
cxx-qt-lib = { version="0.7", features = ["qt_full"] }
log = { version = "0.4.27", features = [] }
+dirs = "6.0.0"
[build-dependencies]
# The link_qt_object_files feature is required for statically linking Qt 6.
diff --git a/README.md b/README.md
index 6172640..0caa5c2 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,25 @@ Using Qt Assistant is recommended. It comes with Qt6 when installed. Run the fol
nohup $HOME/Qt/6.8.3/gcc_64/bin/assistant > /dev/null 2>&1 &
```
+If you are looking for an include path from Qt
+
+```bash
+find /usr/include/x86_64-linux-gnu/qt6/ -name QFile*
+
+/usr/include/x86_64-linux-gnu/qt6/QtWidgets/QFileIconProvider
+/usr/include/x86_64-linux-gnu/qt6/QtWidgets/QFileDialog
+/usr/include/x86_64-linux-gnu/qt6/QtGui/QFileSystemModel
+/usr/include/x86_64-linux-gnu/qt6/QtGui/QFileOpenEvent
+/usr/include/x86_64-linux-gnu/qt6/QtCore/QFile
+/usr/include/x86_64-linux-gnu/qt6/QtCore/QFileDevice
+/usr/include/x86_64-linux-gnu/qt6/QtCore/QFileSystemWatcher
+/usr/include/x86_64-linux-gnu/qt6/QtCore/QFileInfoList
+/usr/include/x86_64-linux-gnu/qt6/QtCore/QFileInfo
+/usr/include/x86_64-linux-gnu/qt6/QtCore/QFileSelector
+```
+
+This helped find that QFileSystemModel is in QtGui and not QtCore.
+
### Resources
Some helpful links for reading up on QML if you're just getting started.
diff --git a/build.rs b/build.rs
index 07367f3..69a103e 100644
--- a/build.rs
+++ b/build.rs
@@ -14,6 +14,7 @@ fn main() {
qml_files: &[
"qml/main.qml",
"qml/ClideAboutWindow.qml",
+ "qml/ClideTreeView.qml",
"qml/ClideProjectView.qml",
"qml/ClideEditor.qml",
"qml/ClideMenuBar.qml",
diff --git a/icons/folder_closed.svg b/icons/folder_closed.svg
new file mode 100644
index 0000000..281be32
--- /dev/null
+++ b/icons/folder_closed.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/icons/folder_open.svg b/icons/folder_open.svg
new file mode 100644
index 0000000..09f7615
--- /dev/null
+++ b/icons/folder_open.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/icons/generic_file.svg b/icons/generic_file.svg
new file mode 100644
index 0000000..e0423f2
--- /dev/null
+++ b/icons/generic_file.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/qml/ClideProjectView.qml b/qml/ClideProjectView.qml
index eead520..569a038 100644
--- a/qml/ClideProjectView.qml
+++ b/qml/ClideProjectView.qml
@@ -5,6 +5,11 @@ import QtQuick.Layouts
import clide.module 1.0
SplitView {
+ id: root
+
+ // Path to the file selected in the tree view.
+ property string selectedFilePath;
+
Layout.fillHeight: true
Layout.fillWidth: true
anchors.fill: parent
@@ -42,12 +47,10 @@ SplitView {
wrapMode: TextArea.Wrap
}
- // TODO: Shows the files on the file system.
- // ClideTreeView {
- // id: fileSystemView
- // color: Colors.surface1
- // onFileClicked: path => root.currentFilePath = path
- // }
+ ClideTreeView {
+ id: clideTreeView
+ onFileClicked: path => root.currentFilePath = path
+ }
}
}
ClideEditor {
diff --git a/qml/ClideTreeView.qml b/qml/ClideTreeView.qml
new file mode 100644
index 0000000..a2cf773
--- /dev/null
+++ b/qml/ClideTreeView.qml
@@ -0,0 +1,148 @@
+import QtQuick
+import QtQuick.Controls
+
+import clide.module 1.0
+
+Rectangle {
+ id: root
+ signal fileClicked(string filePath)
+
+ TreeView {
+ id: fileSystemTreeView
+
+ // rootIndex: FileSystem.rootIndex
+ property int lastIndex: -1
+
+ // model: FileSystem
+ anchors.fill: parent
+ boundsBehavior: Flickable.StopAtBounds
+ boundsMovement: Flickable.StopAtBounds
+ clip: true
+
+ Component.onCompleted: fileSystemTreeView.toggleExpanded(0)
+
+ // The delegate represents a single entry in the filesystem.
+ delegate: TreeViewDelegate {
+ id: treeDelegate
+ indentation: 8
+ implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250
+ implicitHeight: 25
+
+ // Since we have the 'ComponentBehavior Bound' pragma, we need to
+ // require these properties from our model. This is a convenient way
+ // to bind the properties provided by the model's role names.
+ required property int index
+ required property url filePath
+ required property string fileName
+
+ indicator: Image {
+ id: directoryIcon
+
+ x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation)
+ anchors.verticalCenter: parent.verticalCenter
+ source: treeDelegate.hasChildren ? (treeDelegate.expanded
+ ? "../icons/folder_open.svg" : "../icons/folder_closed.svg")
+ : "../icons/generic_file.svg"
+ sourceSize.width: 20
+ sourceSize.height: 20
+ fillMode: Image.PreserveAspectFit
+
+ smooth: true
+ antialiasing: true
+ asynchronous: true
+ }
+
+ contentItem: Text {
+ text: treeDelegate.fileName
+ color: RustColors.editor_text
+ }
+
+ background: Rectangle {
+ color: (treeDelegate.index === fileSystemTreeView.lastIndex)
+ ? RustColors.editor_highlight
+ : (hoverHandler.hovered ? RustColors.active : "transparent")
+ }
+
+ // We color the directory icons with this MultiEffect, where we overlay
+ // the colorization color ontop of the SVG icons.
+ // MultiEffect {
+ // id: iconOverlay
+ //
+ // anchors.fill: directoryIcon
+ // source: directoryIcon
+ // colorization: 1.0
+ // brightness: 1.0
+ // colorizationColor: {
+ // const isFile = treeDelegate.index === fileSystemTreeView.lastIndex
+ // && !treeDelegate.hasChildren;
+ // if (isFile)
+ // return Qt.lighter(RustColors.explorer_folder, 3)
+ //
+ // const isExpandedFolder = treeDelegate.expanded && treeDelegate.hasChildren;
+ // if (isExpandedFolder)
+ // return RustColors.explorer_forder_open
+ // else
+ // return RustColors.explorer_folder
+ // }
+ // }
+
+ HoverHandler {
+ id: hoverHandler
+ }
+
+ TapHandler {
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onSingleTapped: (eventPoint, button) => {
+ switch (button) {
+ case Qt.LeftButton:
+ fileSystemTreeView.toggleExpanded(treeDelegate.row)
+ fileSystemTreeView.lastIndex = treeDelegate.index
+ // If this model item doesn't have children, it means it's
+ // representing a file.
+ if (!treeDelegate.hasChildren)
+ root.fileClicked(treeDelegate.filePath)
+ break;
+ case Qt.RightButton:
+ if (treeDelegate.hasChildren)
+ contextMenu.popup();
+ break;
+ }
+ }
+ }
+
+ Menu {
+ id: contextMenu
+ Action {
+ text: qsTr("Set as root index")
+ onTriggered: {
+ // fileSystemTreeView.rootIndex = fileSystemTreeView.index(treeDelegate.row, 0)
+ }
+ }
+ Action {
+ text: qsTr("Reset root index")
+ // onTriggered: fileSystemTreeView.rootIndex = undefined
+ }
+ }
+ }
+
+ // Provide our own custom ScrollIndicator for the TreeView.
+ ScrollIndicator.vertical: ScrollIndicator {
+ active: true
+ implicitWidth: 15
+
+ contentItem: Rectangle {
+ implicitWidth: 6
+ implicitHeight: 6
+
+ color: RustColors.scrollbar
+ opacity: fileSystemTreeView.movingVertically ? 0.5 : 0.0
+
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 500
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/colors.rs b/src/colors.rs
index 1b699b5..7d1f08f 100644
--- a/src/colors.rs
+++ b/src/colors.rs
@@ -26,6 +26,8 @@ pub mod qobject {
#[qproperty(QColor, editor_highlight)]
#[qproperty(QColor, gutter)]
#[qproperty(QColor, explorer_background)]
+ #[qproperty(QColor, explorer_folder)]
+ #[qproperty(QColor, explorer_folder_open)]
type RustColors = super::RustColorsImpl;
}
}
@@ -50,6 +52,8 @@ pub struct RustColorsImpl {
editor_highlight: QColor,
gutter: QColor,
explorer_background: QColor,
+ explorer_folder: QColor,
+ explorer_folder_open: QColor,
}
impl Default for RustColorsImpl {
@@ -72,6 +76,8 @@ impl Default for RustColorsImpl {
editor_highlight: QColor::try_from("#ccced3").unwrap(),
gutter: QColor::try_from("#1e1f22").unwrap(),
explorer_background: QColor::try_from("#3c3f41").unwrap(),
+ explorer_folder: QColor::try_from("#FFF").unwrap(),
+ explorer_folder_open: QColor::try_from("#FFF").unwrap(),
}
}
}
diff --git a/src/filesystem.rs b/src/filesystem.rs
index ecc9fcd..0f97344 100644
--- a/src/filesystem.rs
+++ b/src/filesystem.rs
@@ -14,19 +14,31 @@ pub mod qobject {
#[qml_element]
#[qml_singleton]
#[qproperty(QString, file_path, cxx_name = "filePath")]
+ #[qproperty(QModelIndex, root_index, cxx_name = "rootIndex")]
type FileSystem = super::FileSystemImpl;
+ #[qinvokable]
+ #[cxx_name = "columnCount"]
+ pub fn column_count(self: &FileSystem, index: &QModelIndex) -> i32;
+
#[qinvokable]
#[cxx_name = "readFile"]
fn read_file(self: &FileSystem, path: &QString) -> QString;
+
+ // TODO: Remove if unused in QML.
+ #[qinvokable]
+ #[cxx_name = "setInitialDirectory"]
+ fn set_initial_directory(self: &FileSystem, path: &QString);
}
}
use cxx_qt_lib::{QModelIndex, QString};
+use dirs;
use std::fs;
+
pub struct FileSystemImpl {
file_path: QString,
- model_index: QModelIndex,
+ root_index: QModelIndex,
}
// Default is explicit to make the editor open this source file initially.
@@ -34,7 +46,7 @@ impl Default for FileSystemImpl {
fn default() -> Self {
Self {
file_path: QString::from(file!()),
- model_index: Default::default(),
+ root_index: Default::default(),
}
}
}
@@ -50,4 +62,23 @@ impl qobject::FileSystem {
.expect(format!("Failed to read file {}", path).as_str()),
)
}
+
+ // There will never be more than one column.
+ pub fn column_count(&self, _index: &QModelIndex) -> i32 {
+ 1
+ }
+
+ fn set_initial_directory(&self, path: &QString) {
+ if !path.is_empty()
+ && fs::metadata(path.to_string())
+ .expect(format!("Failed to get metadata for file {}", path).as_str())
+ .is_file()
+ {
+ // Open the file
+ // setRootPa
+ } else {
+ // If the initial directory can't be opened, attempt to find the home directory.
+ // dirs::home_dir()
+ }
+ }
}