Add basic FileSystem view.
This commit is contained in:
parent
b62dce631f
commit
bdf942371c
@ -6,7 +6,7 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cxx = "1.0.95"
|
cxx = "1.0.95"
|
||||||
cxx-qt = "0.7"
|
cxx-qt = "0.7"
|
||||||
cxx-qt-lib = { version="0.7", features = ["qt_full"] }
|
cxx-qt-lib = { version="0.7", features = ["qt_full", "qt_gui"] }
|
||||||
log = { version = "0.4.27", features = [] }
|
log = { version = "0.4.27", features = [] }
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ CLIDE is an IDE written in Rust that supports both full and headless Linux envir
|
|||||||
The following packages must be installed before the application will build.
|
The following packages must be installed before the application will build.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install qt6-base-dev qt6-declarative-dev qt6-tools-dev qml6-module-qtquick-controls qml6-module-qtquick-layouts qml6-module-qtquick-window qml6-module-qtqml-workerscript qml6-module-qtquick-templates qml6-module-qtquick qml6-module-qtquick-dialogs
|
sudo apt install qt6-base-dev qt6-declarative-dev qt6-tools-dev qml6-module-qtquick-controls qml6-module-qtquick-layouts qml6-module-qtquick-window qml6-module-qtqml-workerscript qml6-module-qtquick-templates qml6-module-qtquick qml6-module-qtquick-dialogs qt6-svg-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
And of course, [Rust](https://www.rust-lang.org/tools/install).
|
And of course, [Rust](https://www.rust-lang.org/tools/install).
|
||||||
|
|||||||
3
build.rs
3
build.rs
@ -8,6 +8,9 @@ fn main() {
|
|||||||
// - Qt Qml is linked by enabling the qt_qml Cargo feature of cxx-qt-lib.
|
// - Qt Qml is linked by enabling the qt_qml Cargo feature of cxx-qt-lib.
|
||||||
// - Qt Qml requires linking Qt Network on macOS
|
// - Qt Qml requires linking Qt Network on macOS
|
||||||
.qt_module("Network")
|
.qt_module("Network")
|
||||||
|
.qt_module("Gui")
|
||||||
|
.qt_module("Svg")
|
||||||
|
.qt_module("Xml")
|
||||||
.qml_module(QmlModule {
|
.qml_module(QmlModule {
|
||||||
uri: "clide.module",
|
uri: "clide.module",
|
||||||
rust_files: &["src/colors.rs", "src/filesystem.rs"],
|
rust_files: &["src/colors.rs", "src/filesystem.rs"],
|
||||||
|
|||||||
@ -67,6 +67,7 @@ MenuBar {
|
|||||||
}
|
}
|
||||||
ClideMenuItem {
|
ClideMenuItem {
|
||||||
action: actionOpen
|
action: actionOpen
|
||||||
|
onTriggered: FileSystem.setDirectory(FileSystem.filePath)
|
||||||
}
|
}
|
||||||
ClideMenuItem {
|
ClideMenuItem {
|
||||||
action: actionSave
|
action: actionSave
|
||||||
|
|||||||
@ -8,7 +8,7 @@ SplitView {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
// Path to the file selected in the tree view.
|
// Path to the file selected in the tree view.
|
||||||
property string selectedFilePath;
|
default property string selectedFilePath: FileSystem.filePath;
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@ -38,23 +38,14 @@ SplitView {
|
|||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
// Shows the help text.
|
|
||||||
TextArea {
|
|
||||||
placeholderText: qsTr("File system view placeholder")
|
|
||||||
placeholderTextColor: "white"
|
|
||||||
readOnly: true
|
|
||||||
wrapMode: TextArea.Wrap
|
|
||||||
}
|
|
||||||
|
|
||||||
ClideTreeView {
|
ClideTreeView {
|
||||||
id: clideTreeView
|
id: clideTreeView
|
||||||
onFileClicked: path => root.currentFilePath = path
|
onFileClicked: path => root.selectedFilePath = path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClideEditor {
|
ClideEditor {
|
||||||
// Initialize using the Default trait in Rust QML singleton FileSystem.
|
// Initialize using the Default trait in Rust QML singleton FileSystem.
|
||||||
filePath: FileSystem.filePath
|
filePath: root.selectedFilePath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,32 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import clide.module 1.0
|
import clide.module 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
signal fileClicked(string filePath)
|
signal fileClicked(string filePath)
|
||||||
|
|
||||||
|
color: RustColors.explorer_background
|
||||||
|
|
||||||
TreeView {
|
TreeView {
|
||||||
id: fileSystemTreeView
|
id: fileSystemTreeView
|
||||||
|
|
||||||
// rootIndex: FileSystem.rootIndex
|
// rootIndex: FileSystem.rootIndex
|
||||||
property int lastIndex: -1
|
property int lastIndex: -1
|
||||||
|
|
||||||
// model: FileSystem
|
model: FileSystem
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
boundsMovement: Flickable.StopAtBounds
|
boundsMovement: Flickable.StopAtBounds
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Component.onCompleted: fileSystemTreeView.toggleExpanded(0)
|
Component.onCompleted: {
|
||||||
|
FileSystem.setDirectory(FileSystem.filePath)
|
||||||
|
fileSystemTreeView.expandRecursively(0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
// The delegate represents a single entry in the filesystem.
|
// The delegate represents a single entry in the filesystem.
|
||||||
delegate: TreeViewDelegate {
|
delegate: TreeViewDelegate {
|
||||||
@ -28,9 +35,6 @@ Rectangle {
|
|||||||
implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250
|
implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250
|
||||||
implicitHeight: 25
|
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 int index
|
||||||
required property url filePath
|
required property url filePath
|
||||||
required property string fileName
|
required property string fileName
|
||||||
@ -40,11 +44,16 @@ Rectangle {
|
|||||||
|
|
||||||
x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation)
|
x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation)
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
source: treeDelegate.hasChildren ? (treeDelegate.expanded
|
source: {
|
||||||
? "../icons/folder_open.svg" : "../icons/folder_closed.svg")
|
// If the item has children, it's a directory.
|
||||||
: "../icons/generic_file.svg"
|
if (treeDelegate.hasChildren) {
|
||||||
sourceSize.width: 20
|
return treeDelegate.expanded ?
|
||||||
sourceSize.height: 20
|
"../icons/folder-open-solid.svg" : "../icons/folder-solid.svg";
|
||||||
|
}
|
||||||
|
return "../icons/file-solid.svg"
|
||||||
|
}
|
||||||
|
sourceSize.width: 15
|
||||||
|
sourceSize.height: 15
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
smooth: true
|
smooth: true
|
||||||
@ -54,37 +63,23 @@ Rectangle {
|
|||||||
|
|
||||||
contentItem: Text {
|
contentItem: Text {
|
||||||
text: treeDelegate.fileName
|
text: treeDelegate.fileName
|
||||||
color: RustColors.editor_text
|
color: RustColors.explorer_text
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
|
// TODO: Fix flickering from color transition on states here.
|
||||||
color: (treeDelegate.index === fileSystemTreeView.lastIndex)
|
color: (treeDelegate.index === fileSystemTreeView.lastIndex)
|
||||||
? RustColors.editor_highlight
|
? RustColors.explorer_text_selected
|
||||||
: (hoverHandler.hovered ? RustColors.active : "transparent")
|
: (hoverHandler.hovered ? RustColors.explorer_hovered : "transparent")
|
||||||
}
|
radius: 2.5
|
||||||
|
opacity: hoverHandler.hovered ? 0.75 : 1.0
|
||||||
|
|
||||||
// We color the directory icons with this MultiEffect, where we overlay
|
Behavior on color {
|
||||||
// the colorization color ontop of the SVG icons.
|
ColorAnimation {
|
||||||
// MultiEffect {
|
duration: 300
|
||||||
// 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 {
|
HoverHandler {
|
||||||
id: hoverHandler
|
id: hoverHandler
|
||||||
@ -115,12 +110,15 @@ Rectangle {
|
|||||||
Action {
|
Action {
|
||||||
text: qsTr("Set as root index")
|
text: qsTr("Set as root index")
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
// fileSystemTreeView.rootIndex = fileSystemTreeView.index(treeDelegate.row, 0)
|
console.log("Setting directory: " + treeDelegate.filePath)
|
||||||
|
FileSystem.setDirectory(treeDelegate.filePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action {
|
Action {
|
||||||
text: qsTr("Reset root index")
|
text: qsTr("Reset root index")
|
||||||
// onTriggered: fileSystemTreeView.rootIndex = undefined
|
onTriggered: {
|
||||||
|
FileSystem.setDirectory("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,9 @@ pub mod qobject {
|
|||||||
#[qproperty(QColor, editor_highlighted_text)]
|
#[qproperty(QColor, editor_highlighted_text)]
|
||||||
#[qproperty(QColor, editor_highlight)]
|
#[qproperty(QColor, editor_highlight)]
|
||||||
#[qproperty(QColor, gutter)]
|
#[qproperty(QColor, gutter)]
|
||||||
|
#[qproperty(QColor, explorer_hovered)]
|
||||||
|
#[qproperty(QColor, explorer_text)]
|
||||||
|
#[qproperty(QColor, explorer_text_selected)]
|
||||||
#[qproperty(QColor, explorer_background)]
|
#[qproperty(QColor, explorer_background)]
|
||||||
#[qproperty(QColor, explorer_folder)]
|
#[qproperty(QColor, explorer_folder)]
|
||||||
#[qproperty(QColor, explorer_folder_open)]
|
#[qproperty(QColor, explorer_folder_open)]
|
||||||
@ -51,6 +54,9 @@ pub struct RustColorsImpl {
|
|||||||
editor_highlighted_text: QColor,
|
editor_highlighted_text: QColor,
|
||||||
editor_highlight: QColor,
|
editor_highlight: QColor,
|
||||||
gutter: QColor,
|
gutter: QColor,
|
||||||
|
explorer_hovered: QColor,
|
||||||
|
explorer_text: QColor,
|
||||||
|
explorer_text_selected: QColor,
|
||||||
explorer_background: QColor,
|
explorer_background: QColor,
|
||||||
explorer_folder: QColor,
|
explorer_folder: QColor,
|
||||||
explorer_folder_open: QColor,
|
explorer_folder_open: QColor,
|
||||||
@ -75,7 +81,10 @@ impl Default for RustColorsImpl {
|
|||||||
editor_highlighted_text: QColor::try_from("#ccced3").unwrap(),
|
editor_highlighted_text: QColor::try_from("#ccced3").unwrap(),
|
||||||
editor_highlight: QColor::try_from("#ccced3").unwrap(),
|
editor_highlight: QColor::try_from("#ccced3").unwrap(),
|
||||||
gutter: QColor::try_from("#1e1f22").unwrap(),
|
gutter: QColor::try_from("#1e1f22").unwrap(),
|
||||||
explorer_background: QColor::try_from("#3c3f41").unwrap(),
|
explorer_hovered: QColor::try_from("#4c5053").unwrap(),
|
||||||
|
explorer_text: QColor::try_from("#3b3b3b").unwrap(),
|
||||||
|
explorer_text_selected: QColor::try_from("#8b8b8b").unwrap(),
|
||||||
|
explorer_background: QColor::try_from("#676c70").unwrap(),
|
||||||
explorer_folder: QColor::try_from("#FFF").unwrap(),
|
explorer_folder: QColor::try_from("#FFF").unwrap(),
|
||||||
explorer_folder_open: QColor::try_from("#FFF").unwrap(),
|
explorer_folder_open: QColor::try_from("#FFF").unwrap(),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,29 +6,36 @@ pub mod qobject {
|
|||||||
type QString = cxx_qt_lib::QString;
|
type QString = cxx_qt_lib::QString;
|
||||||
include!("cxx-qt-lib/qmodelindex.h");
|
include!("cxx-qt-lib/qmodelindex.h");
|
||||||
type QModelIndex = cxx_qt_lib::QModelIndex;
|
type QModelIndex = cxx_qt_lib::QModelIndex;
|
||||||
|
include!(<QtGui/QFileSystemModel>);
|
||||||
|
type QFileSystemModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "RustQt" {
|
unsafe extern "RustQt" {
|
||||||
// Export QML Types from Rust
|
// Export QML Types from Rust
|
||||||
#[qobject]
|
#[qobject]
|
||||||
|
#[base = QFileSystemModel]
|
||||||
#[qml_element]
|
#[qml_element]
|
||||||
#[qml_singleton]
|
#[qml_singleton]
|
||||||
#[qproperty(QString, file_path, cxx_name = "filePath")]
|
#[qproperty(QString, file_path, cxx_name = "filePath")]
|
||||||
#[qproperty(QModelIndex, root_index, cxx_name = "rootIndex")]
|
#[qproperty(QModelIndex, root_index, cxx_name = "rootIndex")]
|
||||||
type FileSystem = super::FileSystemImpl;
|
type FileSystem = super::FileSystemImpl;
|
||||||
|
|
||||||
|
#[inherit]
|
||||||
|
#[cxx_name = "setRootPath"]
|
||||||
|
fn set_root_path(self: Pin<&mut FileSystem>, path: &QString) -> QModelIndex;
|
||||||
|
|
||||||
#[qinvokable]
|
#[qinvokable]
|
||||||
|
#[cxx_override]
|
||||||
#[cxx_name = "columnCount"]
|
#[cxx_name = "columnCount"]
|
||||||
pub fn column_count(self: &FileSystem, index: &QModelIndex) -> i32;
|
fn column_count(self: &FileSystem, _index: &QModelIndex) -> i32;
|
||||||
|
|
||||||
#[qinvokable]
|
#[qinvokable]
|
||||||
#[cxx_name = "readFile"]
|
#[cxx_name = "readFile"]
|
||||||
fn read_file(self: &FileSystem, path: &QString) -> QString;
|
fn read_file(self: &FileSystem, path: &QString) -> QString;
|
||||||
|
|
||||||
// TODO: Remove if unused in QML.
|
|
||||||
#[qinvokable]
|
#[qinvokable]
|
||||||
#[cxx_name = "setInitialDirectory"]
|
#[cxx_name = "setDirectory"]
|
||||||
fn set_initial_directory(self: &FileSystem, path: &QString);
|
fn set_directory(self: Pin<&mut FileSystem>, path: &QString) -> QModelIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +43,7 @@ use cxx_qt_lib::{QModelIndex, QString};
|
|||||||
use dirs;
|
use dirs;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
// TODO: Impleent a provider for QFileSystemModel::setIconProvider for icons.
|
||||||
pub struct FileSystemImpl {
|
pub struct FileSystemImpl {
|
||||||
file_path: QString,
|
file_path: QString,
|
||||||
root_index: QModelIndex,
|
root_index: QModelIndex,
|
||||||
@ -64,21 +72,27 @@ impl qobject::FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// There will never be more than one column.
|
// There will never be more than one column.
|
||||||
pub fn column_count(&self, _index: &QModelIndex) -> i32 {
|
fn column_count(&self, _index: &QModelIndex) -> i32 {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_initial_directory(&self, path: &QString) {
|
fn set_directory(self: std::pin::Pin<&mut Self>, path: &QString) -> QModelIndex {
|
||||||
if !path.is_empty()
|
if !path.is_empty()
|
||||||
&& fs::metadata(path.to_string())
|
&& fs::metadata(path.to_string())
|
||||||
.expect(format!("Failed to get metadata for file {}", path).as_str())
|
.expect(format!("Failed to get metadata for path {}", path).as_str())
|
||||||
.is_file()
|
.is_dir()
|
||||||
{
|
{
|
||||||
// Open the file
|
self.set_root_path(path)
|
||||||
// setRootPa
|
|
||||||
} else {
|
} else {
|
||||||
// If the initial directory can't be opened, attempt to find the home directory.
|
// If the initial directory can't be opened, attempt to find the home directory.
|
||||||
// dirs::home_dir()
|
self.set_root_path(&QString::from(
|
||||||
|
dirs::home_dir()
|
||||||
|
.expect("Failed to get home directory")
|
||||||
|
.as_path()
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user