diff --git a/qml/ClideProjectView.qml b/qml/ClideProjectView.qml index 1c56c53..3cd08b4 100644 --- a/qml/ClideProjectView.qml +++ b/qml/ClideProjectView.qml @@ -43,11 +43,26 @@ SplitView { SplitView.preferredWidth: 200 SplitView.maximumWidth: 250 - StackLayout { - anchors.fill: parent + ColumnLayout { + spacing: 2 + Rectangle { + width: navigationView.width + height: breadCrumb.height + 5 + + color: RustColors.explorer_text + Text { + id: breadCrumb + text: clideTreeView.rootDirectory + elide: Text.ElideLeft + horizontalAlignment: Text.AlignHCenter + } + } + ClideTreeView { id: clideTreeView onFileClicked: path => root.projectDir = path + width: navigationView.width + height: navigationView.height // Path to the directory opened in the file explorer. rootDirectory: root.projectDir diff --git a/qml/ClideTreeView.qml b/qml/ClideTreeView.qml index 17d7e24..9a58e0b 100644 --- a/qml/ClideTreeView.qml +++ b/qml/ClideTreeView.qml @@ -3,153 +3,170 @@ // SPDX-License-Identifier: GNU General Public License v3.0 or later import QtQuick +import QtQuick.Effects import QtQuick.Controls import clide.module 1.0 -Rectangle { - id: root - color: RustColors.explorer_background +TreeView { + id: fileSystemTreeView + model: FileSystem + + property int lastIndex: -1 required property string rootDirectory signal fileClicked(string filePath) - TreeView { - id: fileSystemTreeView - anchors.margins: 15 - property int lastIndex: -1 + rootIndex: FileSystem.rootIndex + leftMargin: 5 + boundsBehavior: Flickable.StopAtBounds + boundsMovement: Flickable.StopAtBounds + clip: true - model: FileSystem - anchors.fill: parent - boundsBehavior: Flickable.StopAtBounds - boundsMovement: Flickable.StopAtBounds - clip: true - rootIndex: FileSystem.rootIndex + Component.onCompleted: { + FileSystem.rootIndex = FileSystem.setDirectory(fileSystemTreeView.rootDirectory) + } - Component.onCompleted: { - FileSystem.setDirectory(root.rootDirectory) - fileSystemTreeView.expandRecursively(0, -1) + // The delegate represents a single entry in the filesystem. + delegate: TreeViewDelegate { + id: treeDelegate + indentation: 6 + implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250 + implicitHeight: 25 + + required property int index + required property url filePath + required property string fileName + + indicator: Image { + id: directoryIcon + + function setSourceImage() { + let folderOpen = "data:image/svg+xml;utf8,"; + let folderClosed = "data:image/svg+xml;utf8,"; + let file = "data:image/svg+xml;utf8,"; + // If the item has children, it's a directory. + if (treeDelegate.hasChildren) { + return treeDelegate.expanded ? + folderOpen : folderClosed; + } else { + return file + } + } + + x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation) + anchors.verticalCenter: parent.verticalCenter + source: setSourceImage() + sourceSize.width: 15 + sourceSize.height: 15 + fillMode: Image.PreserveAspectFit + + smooth: true + antialiasing: true + asynchronous: true } - // The delegate represents a single entry in the filesystem. - delegate: TreeViewDelegate { - id: treeDelegate - indentation: 8 - implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250 - implicitHeight: 25 + contentItem: Text { + text: treeDelegate.fileName + color: RustColors.explorer_text + } - required property int index - required property url filePath - required property string fileName + background: Rectangle { + // TODO: Fix flickering from color transition on states here. + color: (treeDelegate.index === fileSystemTreeView.lastIndex) + ? RustColors.explorer_text_selected + : (hoverHandler.hovered ? RustColors.explorer_hovered : "transparent") + radius: 2.5 + opacity: hoverHandler.hovered ? 0.75 : 1.0 - indicator: Image { - id: directoryIcon - - function setSourceImage() { - let folderOpen = "data:image/svg+xml;utf8,"; - let folderClosed = "data:image/svg+xml;utf8,"; - let file = "data:image/svg+xml;utf8,"; - // If the item has children, it's a directory. - if (treeDelegate.hasChildren) { - return treeDelegate.expanded ? - folderOpen : folderClosed; - } else { - return file - } - } - - x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation) - anchors.verticalCenter: parent.verticalCenter - source: setSourceImage() - sourceSize.width: 15 - sourceSize.height: 15 - fillMode: Image.PreserveAspectFit - - smooth: true - antialiasing: true - asynchronous: true - } - - contentItem: Text { - text: treeDelegate.fileName - color: RustColors.explorer_text - } - - background: Rectangle { - // TODO: Fix flickering from color transition on states here. - color: (treeDelegate.index === fileSystemTreeView.lastIndex) - ? RustColors.explorer_text_selected - : (hoverHandler.hovered ? RustColors.explorer_hovered : "transparent") - radius: 2.5 - opacity: hoverHandler.hovered ? 0.75 : 1.0 - - Behavior on color { - ColorAnimation { - duration: 300 - } - } - } - - 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: { - console.log("Setting directory: " + treeDelegate.filePath) - FileSystem.rootIndex = FileSystem.setDirectory(treeDelegate.filePath) - } - } - Action { - text: qsTr("Reset root index") - onTriggered: { - FileSystem.rootIndex = FileSystem.setDirectory(root.rootDirectory) - } + Behavior on color { + ColorAnimation { + duration: 300 } } } - // Provide our own custom ScrollIndicator for the TreeView. - ScrollIndicator.vertical: ScrollIndicator { - active: true - implicitWidth: 15 + MultiEffect { + id: iconOverlay - contentItem: Rectangle { - implicitWidth: 6 - implicitHeight: 6 + anchors.fill: directoryIcon + source: directoryIcon + colorization: 1.0 + brightness: 1.0 + colorizationColor: { + const isFile = !treeDelegate.hasChildren; + if (isFile) + return Qt.lighter(RustColors.explorer_folder, 2) - color: RustColors.scrollbar - opacity: fileSystemTreeView.movingVertically ? 0.5 : 0.0 + const isExpandedFolder = treeDelegate.expanded && treeDelegate.hasChildren; + if (isExpandedFolder) + return Qt.darker(RustColors.explorer_folder, 2) + else + return RustColors.explorer_folder + } + } - Behavior on opacity { - OpacityAnimator { - duration: 500 - } + HoverHandler { + id: hoverHandler + acceptedDevices: PointerDevice.Mouse + } + + 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: { + console.log("Setting directory: " + treeDelegate.filePath) + FileSystem.rootIndex = FileSystem.setDirectory(treeDelegate.filePath) + } + } + Action { + text: qsTr("Reset root index") + onTriggered: { + console.log("Reset root index") + FileSystem.rootIndex = FileSystem.setDirectory(fileSystemTreeView.rootDirectory) + } + } + } + } + + // 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/gui/colors.rs b/src/gui/colors.rs index 0d2b17f..fea8ea1 100644 --- a/src/gui/colors.rs +++ b/src/gui/colors.rs @@ -88,10 +88,10 @@ impl Default for RustColorsImpl { gutter: QColor::try_from("#1e1f22").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_text_selected: QColor::try_from("#262626").unwrap(), + explorer_background: QColor::try_from("#1E1F22").unwrap(), explorer_folder: QColor::try_from("#54585b").unwrap(), - explorer_folder_open: QColor::try_from("#FFF").unwrap(), + explorer_folder_open: QColor::try_from("#2b2b2b").unwrap(), } } }