clide/qml/ClideTreeView.qml

170 lines
6.7 KiB
QML
Raw Normal View History

2026-01-31 08:02:16 -05:00
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
//
// SPDX-License-Identifier: GNU General Public License v3.0 or later
2026-01-25 20:57:36 +00:00
import QtQuick
2026-02-01 15:35:40 -05:00
import QtQuick.Effects
2026-01-25 20:57:36 +00:00
import QtQuick.Controls
import clide.module 1.0
2026-02-01 15:35:40 -05:00
TreeView {
id: fileSystemTreeView
model: FileSystem
property int lastIndex: -1
2026-01-25 20:57:36 +00:00
required property string originalRootDirectory
property string rootDirectory
2026-01-31 04:25:14 +00:00
2026-01-25 20:57:36 +00:00
signal fileClicked(string filePath)
rootIndex: FileSystem.setDirectory(fileSystemTreeView.rootDirectory)
2026-02-01 15:35:40 -05:00
leftMargin: 5
boundsBehavior: Flickable.StopAtBounds
boundsMovement: Flickable.StopAtBounds
clip: true
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
// The delegate represents a single entry in the filesystem.
delegate: TreeViewDelegate {
id: treeDelegate
indentation: 12
2026-02-01 15:35:40 -05:00
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,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d=\"M88.7 223.8L0 375.8 0 96C0 60.7 28.7 32 64 32l117.5 0c17 0 33.3 6.7 45.3 18.7l26.5 26.5c12 12 28.3 18.7 45.3 18.7L416 96c35.3 0 64 28.7 64 64l0 32-336 0c-22.8 0-43.8 12.1-55.3 31.8zm27.6 16.1C122.1 230 132.6 224 144 224l400 0c11.5 0 22 6.1 27.7 16.1s5.7 22.2-.1 32.1l-112 192C453.9 474 443.4 480 432 480L32 480c-11.5 0-22-6.1-27.7-16.1s-5.7-22.2 .1-32.1l112-192z\"/></svg>";
let folderClosed = "data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d=\"M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z\"/></svg>";
let file = "data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 384 512\"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d=\"M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 288c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128z\"/></svg>";
// If the item has children, it's a directory.
if (treeDelegate.hasChildren) {
return treeDelegate.expanded ?
folderOpen : folderClosed;
} else {
return file
2026-01-25 20:57:36 +00:00
}
2026-02-01 15:35:40 -05:00
}
2026-01-31 04:25:14 +00:00
2026-02-01 15:35:40 -05:00
x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation)
anchors.verticalCenter: parent.verticalCenter
source: setSourceImage()
sourceSize.width: 15
sourceSize.height: 15
fillMode: Image.PreserveAspectFit
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
smooth: true
antialiasing: true
asynchronous: true
}
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
contentItem: Text {
text: treeDelegate.fileName
color: RustColors.explorer_text
}
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
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
2026-01-25 20:57:36 +00:00
}
}
2026-02-01 15:35:40 -05:00
}
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
MultiEffect {
id: iconOverlay
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)
const isExpandedFolder = treeDelegate.expanded && treeDelegate.hasChildren;
if (isExpandedFolder)
return Qt.darker(RustColors.explorer_folder, 2)
else
return RustColors.explorer_folder
2026-01-25 20:57:36 +00:00
}
2026-02-01 15:35:40 -05:00
}
HoverHandler {
id: hoverHandler
acceptedDevices: PointerDevice.Mouse
}
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
TapHandler {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onSingleTapped: (eventPoint, button) => {
switch (button) {
case Qt.LeftButton:
fileSystemTreeView.toggleExpanded(treeDelegate.row)
// If this model item doesn't have children, it means it's
// representing a file.
if (!treeDelegate.hasChildren)
fileSystemTreeView.fileClicked(treeDelegate.filePath)
2026-02-01 15:35:40 -05:00
break;
case Qt.RightButton:
contextMenu.popup();
2026-02-01 15:35:40 -05:00
break;
2026-01-25 20:57:36 +00:00
}
}
2026-02-01 15:35:40 -05:00
}
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
Menu {
id: contextMenu
Action {
text: qsTr("Set as root index")
enabled: treeDelegate.hasChildren
2026-02-01 15:35:40 -05:00
onTriggered: {
console.log("Setting new root directory: " + treeDelegate.filePath)
fileSystemTreeView.rootDirectory = treeDelegate.filePath
2026-01-25 20:57:36 +00:00
}
2026-02-01 15:35:40 -05:00
}
Action {
text: qsTr("Reset root index")
onTriggered: {
console.log("Resetting root directory: " + fileSystemTreeView.originalRootDirectory)
fileSystemTreeView.rootDirectory = fileSystemTreeView.originalRootDirectory
2026-01-25 20:57:36 +00:00
}
}
}
2026-02-01 15:35:40 -05:00
}
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
// Provide our own custom ScrollIndicator for the TreeView.
ScrollIndicator.vertical: ScrollIndicator {
active: true
implicitWidth: 15
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
contentItem: Rectangle {
implicitWidth: 6
implicitHeight: 6
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
color: RustColors.scrollbar
opacity: fileSystemTreeView.movingVertically ? 0.5 : 0.0
2026-01-25 20:57:36 +00:00
2026-02-01 15:35:40 -05:00
Behavior on opacity {
OpacityAnimator {
duration: 500
2026-01-25 20:57:36 +00:00
}
}
}
}
}