Clean up GUI.
This commit is contained in:
3
build.rs
3
build.rs
@@ -5,7 +5,8 @@ fn main() {
|
||||
"qml/main.qml",
|
||||
"qml/ClideAboutWindow.qml",
|
||||
"qml/ClideTreeView.qml",
|
||||
"qml/ClideProjectView.qml",
|
||||
"qml/ClideApplicationView.qml",
|
||||
"qml/ClideExplorerView.qml",
|
||||
"qml/ClideEditor.qml",
|
||||
"qml/ClideEditorView.qml",
|
||||
"qml/ClideMenuBar.qml",
|
||||
|
||||
46
qml/ClideApplicationView.qml
Normal file
46
qml/ClideApplicationView.qml
Normal file
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import clide.module 1.0
|
||||
import Logger 1.0
|
||||
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
// Path to the directory of the project opened in clide.
|
||||
required property string projectDir
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
// Customized handle to drag between the Navigation and the Editor.
|
||||
handle: ClideHandle {
|
||||
hovered: SplitHandle.hovered
|
||||
pressed: SplitHandle.pressed
|
||||
}
|
||||
|
||||
ClideExplorerView {
|
||||
SplitView.fillHeight: true
|
||||
SplitView.preferredWidth: 200
|
||||
projectDir: root.projectDir
|
||||
|
||||
// Open files when clicked in the explorer.
|
||||
onFileClicked: path => {
|
||||
Logger.trace("Setting editor path from ClideExplorerView signal: " + path)
|
||||
clideEditorView.filePath = path;
|
||||
}
|
||||
}
|
||||
ClideEditorView {
|
||||
id: clideEditorView
|
||||
|
||||
SplitView.fillHeight: true
|
||||
SplitView.fillWidth: true
|
||||
// Provide a path to the file currently open in the text editor.
|
||||
// Initialized using the Default trait in Rust QML singleton FileSystem.
|
||||
filePath: FileSystem.filePath
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,12 @@ import QtQuick.Layouts
|
||||
import clide.module 1.0
|
||||
import Logger 1.0
|
||||
|
||||
Rectangle {
|
||||
color: "transparent"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
// We use a flickable to synchronize the position of the editor and
|
||||
// the line numbers. This is necessary because the line numbers can
|
||||
// extend the available height.
|
||||
@@ -57,12 +62,8 @@ RowLayout {
|
||||
text: parent.index + 1
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
width: parent.width - indicator.width
|
||||
|
||||
background: Rectangle {
|
||||
color: RustColors.terminal_background
|
||||
}
|
||||
}
|
||||
// Draw edge along the right side of the line number.
|
||||
// Draw an edge along the right side of the line number.
|
||||
Rectangle {
|
||||
id: indicator
|
||||
|
||||
@@ -134,3 +135,4 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,22 +9,25 @@ import QtQuick.Layouts
|
||||
import clide.module 1.0
|
||||
import Logger 1.0
|
||||
|
||||
SplitView {
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
// The path to the file to show in the text editor.
|
||||
// This is updated by a signal caught within ClideProjectView.
|
||||
// Initialized by the Default trait for the Rust QML singleton FileSystem.
|
||||
// This is updated by a signal caught within ClideApplicationView.
|
||||
required property string filePath
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
color: "transparent"
|
||||
radius: 20
|
||||
|
||||
SplitView {
|
||||
anchors.fill: parent
|
||||
orientation: Qt.Vertical
|
||||
|
||||
// Customized handle to drag between the Editor and the Console.
|
||||
handle: ClideHandle {
|
||||
pressed: SplitHandle.pressed
|
||||
hovered: SplitHandle.hovered
|
||||
pressed: SplitHandle.pressed
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
@@ -37,10 +40,9 @@ SplitView {
|
||||
}
|
||||
|
||||
ClideEditor {
|
||||
id: clideEditor
|
||||
SplitView.preferredHeight: 650
|
||||
}
|
||||
ClideLogger {
|
||||
id: areaConsole
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
62
qml/ClideExplorerView.qml
Normal file
62
qml/ClideExplorerView.qml
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import clide.module 1.0
|
||||
import Logger 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
required property string projectDir
|
||||
|
||||
clip: true
|
||||
color: RustColors.explorer_background
|
||||
radius: 20
|
||||
|
||||
signal fileClicked(string path)
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 5
|
||||
|
||||
ClideBreadCrumbs {
|
||||
id: breadCrumb
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 15
|
||||
Layout.rightMargin: 15
|
||||
Layout.topMargin: 10
|
||||
path: clideTreeView.rootDirectory
|
||||
|
||||
onCrumbClicked: path => {
|
||||
Logger.trace("Crumb clicked: " + path);
|
||||
clideTreeView.rootDirectory = path;
|
||||
}
|
||||
onResetRoot: {
|
||||
clideTreeView.rootDirectory = clideTreeView.originalRootDirectory;
|
||||
}
|
||||
}
|
||||
ClideTreeView {
|
||||
id: clideTreeView
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Path to the directory opened in the file explorer.
|
||||
originalRootDirectory: root.projectDir
|
||||
rootDirectory: root.projectDir
|
||||
|
||||
// Pass the signal to the parent component using another signal.
|
||||
onFileClicked: path => root.fileClicked(path)
|
||||
onRootDirectoryChanged: {
|
||||
Logger.log("Setting root directory: " + clideTreeView.rootDirectory);
|
||||
breadCrumb.path = clideTreeView.rootDirectory;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,12 @@ import QtQuick.Controls
|
||||
import clide.module 1.0
|
||||
import Logger 1.0
|
||||
|
||||
Item {
|
||||
Rectangle {
|
||||
color: "#111"
|
||||
radius: 10
|
||||
|
||||
ListModel {
|
||||
id: model
|
||||
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#111"
|
||||
}
|
||||
ListView {
|
||||
id: listView
|
||||
@@ -38,7 +36,6 @@ Item {
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
model: model
|
||||
|
||||
delegate: Text {
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import clide.module 1.0
|
||||
import Logger 1.0
|
||||
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
// Path to the directory of the project opened in clide.
|
||||
required property string projectDir
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
// Customized handle to drag between the Navigation and the Editor.
|
||||
handle: ClideHandle {
|
||||
id: verticalSplitHandle
|
||||
|
||||
hovered: SplitHandle.hovered
|
||||
pressed: SplitHandle.pressed
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: navigationView
|
||||
|
||||
SplitView.fillHeight: true
|
||||
SplitView.maximumWidth: 250
|
||||
SplitView.minimumWidth: 0
|
||||
SplitView.preferredWidth: 200
|
||||
color: RustColors.explorer_background
|
||||
radius: 20
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 2
|
||||
|
||||
ClideBreadCrumbs {
|
||||
id: breadCrumb
|
||||
|
||||
Layout.bottomMargin: 5
|
||||
Layout.leftMargin: 15
|
||||
Layout.topMargin: 5
|
||||
path: clideTreeView.rootDirectory
|
||||
|
||||
onCrumbClicked: path => {
|
||||
Logger.trace("Crumb clicked: " + path);
|
||||
clideTreeView.rootDirectory = path;
|
||||
}
|
||||
}
|
||||
ClideTreeView {
|
||||
id: clideTreeView
|
||||
|
||||
height: navigationView.height
|
||||
|
||||
// Path to the directory opened in the file explorer.
|
||||
originalRootDirectory: root.projectDir
|
||||
rootDirectory: root.projectDir
|
||||
width: navigationView.width
|
||||
|
||||
onFileClicked: path => clideEditor.filePath = path
|
||||
onRootDirectoryChanged: {
|
||||
Logger.log("Setting root directory: " + clideTreeView.rootDirectory);
|
||||
breadCrumb.path = clideTreeView.rootDirectory;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ClideEditorView {
|
||||
id: clideEditor
|
||||
|
||||
SplitView.fillWidth: true
|
||||
|
||||
// Provide a path to the file currently open in the text editor.
|
||||
// Initialized using the Default trait in Rust QML singleton FileSystem.
|
||||
filePath: FileSystem.filePath
|
||||
}
|
||||
}
|
||||
@@ -10,38 +10,30 @@ import clide.module 1.0
|
||||
import Logger 1.0
|
||||
|
||||
TreeView {
|
||||
id: fileSystemTreeView
|
||||
id: root
|
||||
|
||||
property int lastIndex: -1
|
||||
required property string originalRootDirectory
|
||||
property string rootDirectory
|
||||
property int rootIndent: 25
|
||||
|
||||
signal fileClicked(string filePath)
|
||||
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
boundsMovement: Flickable.StopAtBounds
|
||||
clip: true
|
||||
leftMargin: 25
|
||||
|
||||
// The model is implemented in filesystem.rs
|
||||
model: FileSystem
|
||||
rootIndex: FileSystem.setDirectory(fileSystemTreeView.rootDirectory)
|
||||
// Set the root directory on the Rust model, returning the QModeIndex to use for the root of the tree view widget.
|
||||
rootIndex: FileSystem.setDirectory(root.rootDirectory)
|
||||
|
||||
// Provide our own custom ScrollIndicator for the TreeView.
|
||||
ScrollIndicator.vertical: ScrollIndicator {
|
||||
active: true
|
||||
implicitWidth: 15
|
||||
|
||||
contentItem: Rectangle {
|
||||
color: RustColors.scrollbar
|
||||
implicitHeight: 6
|
||||
implicitWidth: 6
|
||||
opacity: fileSystemTreeView.movingVertically ? 0.5 : 0.0
|
||||
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: 500
|
||||
}
|
||||
}
|
||||
ScrollBar.horizontal: ClideScrollBar {
|
||||
sizeModifier: 3
|
||||
}
|
||||
ScrollBar.vertical: ClideScrollBar {
|
||||
sizeModifier: 3
|
||||
}
|
||||
|
||||
// The delegate represents a single entry in the filesystem.
|
||||
@@ -53,38 +45,40 @@ TreeView {
|
||||
required property int index
|
||||
|
||||
implicitHeight: 25
|
||||
implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250
|
||||
implicitWidth: root.width
|
||||
indentation: 12
|
||||
|
||||
background: Rectangle {
|
||||
color: current ? RustColors.explorer_folder_open : "transparent"
|
||||
radius: 2.5
|
||||
radius: 20
|
||||
width: root.width - 15
|
||||
}
|
||||
// Item name.
|
||||
contentItem: Text {
|
||||
anchors.left: directoryIcon.right
|
||||
anchors.left: itemIcon.right
|
||||
anchors.leftMargin: 5
|
||||
color: RustColors.explorer_text
|
||||
text: treeDelegate.fileName
|
||||
}
|
||||
// Item Icon.
|
||||
indicator: Label {
|
||||
id: directoryIcon
|
||||
id: itemIcon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
antialiasing: true
|
||||
enabled: false
|
||||
focus: false
|
||||
font.family: localFont.font.family
|
||||
font.pixelSize: 18
|
||||
smooth: true
|
||||
text: fileSystemTreeView.model.icon(filePath)
|
||||
x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation) + (indicator.visible ? indicator.width : 0)
|
||||
// Get the icon from Rust implementation.
|
||||
text: root.model.icon(filePath)
|
||||
x: root.rootIndent + (treeDelegate.depth * treeDelegate.indentation) + (carrotIndicator.visible ? carrotIndicator.width : 0)
|
||||
}
|
||||
|
||||
FontLoader {
|
||||
id: localFont
|
||||
|
||||
source: "qrc:/fonts/saucecodepro-xlight.ttf"
|
||||
}
|
||||
// Directory carrot indicator.
|
||||
Label {
|
||||
id: indicator
|
||||
id: carrotIndicator
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.family: localFont.font.family
|
||||
@@ -92,12 +86,11 @@ TreeView {
|
||||
font.weight: localFont.font.weight
|
||||
text: expanded ? "⮟" : "⮞"
|
||||
visible: isTreeNode && hasChildren
|
||||
x: padding + (depth * indentation)
|
||||
x: (root.rootIndent - implicitWidth) + (depth * indentation)
|
||||
}
|
||||
// Apply colorization effects to the icon for the item.
|
||||
MultiEffect {
|
||||
id: iconOverlay
|
||||
|
||||
anchors.fill: directoryIcon
|
||||
anchors.fill: itemIcon
|
||||
brightness: 1.0
|
||||
colorization: 1.0
|
||||
colorizationColor: {
|
||||
@@ -110,7 +103,7 @@ TreeView {
|
||||
else
|
||||
return RustColors.explorer_folder;
|
||||
}
|
||||
source: directoryIcon
|
||||
source: itemIcon
|
||||
}
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
@@ -124,10 +117,10 @@ TreeView {
|
||||
switch (button) {
|
||||
case Qt.LeftButton:
|
||||
if (treeDelegate.hasChildren) {
|
||||
fileSystemTreeView.toggleExpanded(treeDelegate.row);
|
||||
root.toggleExpanded(treeDelegate.row);
|
||||
} else {
|
||||
// If this model item doesn't have children, it means it's representing a file.
|
||||
fileSystemTreeView.fileClicked(treeDelegate.filePath);
|
||||
root.fileClicked(treeDelegate.filePath);
|
||||
}
|
||||
break;
|
||||
case Qt.RightButton:
|
||||
@@ -146,7 +139,7 @@ TreeView {
|
||||
|
||||
onTriggered: {
|
||||
Logger.debug("Setting new root directory: " + treeDelegate.filePath);
|
||||
fileSystemTreeView.rootDirectory = treeDelegate.filePath;
|
||||
root.rootDirectory = treeDelegate.filePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,8 +148,8 @@ TreeView {
|
||||
text: qsTr("Reset root")
|
||||
|
||||
onTriggered: {
|
||||
Logger.log("Resetting root directory: " + fileSystemTreeView.originalRootDirectory);
|
||||
fileSystemTreeView.rootDirectory = fileSystemTreeView.originalRootDirectory;
|
||||
Logger.log("Resetting root directory: " + root.originalRootDirectory);
|
||||
root.rootDirectory = root.originalRootDirectory;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,4 +157,10 @@ TreeView {
|
||||
}
|
||||
selectionModel: ItemSelectionModel {
|
||||
}
|
||||
|
||||
FontLoader {
|
||||
id: localFont
|
||||
|
||||
source: "qrc:/fonts/saucecodepro-xlight.ttf"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import QtQuick.Layouts 1.15
|
||||
import clide.module 1.0
|
||||
import Logger 1.0
|
||||
|
||||
Item {
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property var fullPaths: []
|
||||
@@ -13,36 +13,32 @@ Item {
|
||||
property var segments: []
|
||||
|
||||
signal crumbClicked(string path)
|
||||
signal resetRoot
|
||||
|
||||
function rebuildSegments() {
|
||||
var cleaned = path;
|
||||
function rebuildSegments(): string {
|
||||
let cleaned = path;
|
||||
if (cleaned.endsWith("/"))
|
||||
cleaned = cleaned.slice(0, -1);
|
||||
var parts = cleaned.split("/");
|
||||
root.segments = [];
|
||||
root.fullPaths = [];
|
||||
var current = "";
|
||||
Logger.trace("Building segments for path: " + cleaned);
|
||||
for (var i = 0; i < parts.length; ++i) {
|
||||
if (parts[i] === "") {
|
||||
current = "/";
|
||||
root.segments.push("/");
|
||||
root.fullPaths.push("/");
|
||||
} else {
|
||||
if (current === "/")
|
||||
current += parts[i];
|
||||
else
|
||||
segments = [];
|
||||
fullPaths = [];
|
||||
segments.push("/");
|
||||
fullPaths.push("/");
|
||||
let parts = cleaned.split("/");
|
||||
let current = "";
|
||||
// We already pushed the root `/` path above, so skip index 0.
|
||||
for (let i = 1; i < parts.length; ++i) {
|
||||
current += "/" + parts[i];
|
||||
Logger.trace("Pushing path: " + parts[i] + " Current: " + current);
|
||||
root.segments.push(parts[i]);
|
||||
root.fullPaths.push(current);
|
||||
segments.push(parts[i]);
|
||||
fullPaths.push(current);
|
||||
}
|
||||
}
|
||||
rep.model = root.segments;
|
||||
// Update the model used in the Repeater to show the new segments.
|
||||
repeater.model = segments;
|
||||
}
|
||||
|
||||
anchors.leftMargin: 20
|
||||
height: breadcrumbRow.implicitHeight
|
||||
color: "transparent"
|
||||
implicitHeight: breadcrumbRow.implicitHeight
|
||||
width: parent.width
|
||||
|
||||
Component.onCompleted: rebuildSegments()
|
||||
@@ -51,28 +47,32 @@ Item {
|
||||
Flow {
|
||||
id: breadcrumbRow
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: 2
|
||||
width: parent.width
|
||||
|
||||
Repeater {
|
||||
id: rep
|
||||
id: repeater
|
||||
|
||||
model: root.segments
|
||||
|
||||
delegate: Text {
|
||||
id: linkText
|
||||
|
||||
required property int index
|
||||
required property string modelData
|
||||
|
||||
function getText() {
|
||||
function getText(): string {
|
||||
if (modelData === "/") {
|
||||
return modelData;
|
||||
}
|
||||
Logger.trace("Getting valid text:" + modelData);
|
||||
return modelData + "/";
|
||||
}
|
||||
|
||||
// Show blue underlined hyperlink text if the mouse is hovering a segment.
|
||||
color: mouseArea.containsMouse ? "#2a7fff" : RustColors.explorer_text
|
||||
font.underline: mouseArea.containsMouse
|
||||
text: getText()
|
||||
|
||||
// Click events for each path segment call signal so the parent can set the file explorer root path.
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
@@ -80,8 +80,8 @@ Item {
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
console.log("Breadcrumb clicked:", root.fullPaths[root.segments.indexOf(modelData)]);
|
||||
root.crumbClicked(root.fullPaths[root.segments.indexOf(modelData)]);
|
||||
Logger.info(index + "] Breadcrumb clicked:" + root.fullPaths[index]);
|
||||
crumbClicked(root.fullPaths[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ Item {
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
|
||||
onSingleTapped: (eventPoint, button) => contextMenu.popup()
|
||||
onSingleTapped: contextMenu.popup()
|
||||
}
|
||||
ClideMenu {
|
||||
id: contextMenu
|
||||
@@ -100,8 +100,8 @@ Item {
|
||||
text: qsTr("Reset root")
|
||||
|
||||
onTriggered: {
|
||||
Logger.log("Resetting root directory: " + clideTreeView.originalRootDirectory);
|
||||
clideTreeView.rootDirectory = clideTreeView.originalRootDirectory;
|
||||
Logger.info("Resetting root directory from ClideBreadCrumbs");
|
||||
resetRoot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ ScrollBar {
|
||||
id: scrollBar
|
||||
|
||||
// Height, opacitiy, width
|
||||
property int h: scrollBar.interactive ? 4 * 2 : 4
|
||||
property int h: scrollBar.interactive ? sizeModifier * 2 : sizeModifier
|
||||
property int o: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0
|
||||
property int w: scrollBar.interactive ? 4 * 2 : 4
|
||||
property int sizeModifier: 4
|
||||
property int w: scrollBar.interactive ? sizeModifier * 2 : sizeModifier
|
||||
|
||||
// Scroll bar gutter
|
||||
background: Rectangle {
|
||||
@@ -25,6 +26,7 @@ ScrollBar {
|
||||
|
||||
// Fade the scrollbar gutter when inactive.
|
||||
opacity: scrollBar.o
|
||||
radius: 20
|
||||
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
@@ -55,6 +57,7 @@ ScrollBar {
|
||||
|
||||
// Fade the scrollbar when inactive.
|
||||
opacity: scrollBar.o
|
||||
radius: 20
|
||||
|
||||
// Smooth transition between color changes based on the state above.
|
||||
Behavior on color {
|
||||
|
||||
@@ -22,9 +22,7 @@ ApplicationWindow {
|
||||
menuBar: ClideMenuBar {
|
||||
}
|
||||
|
||||
ClideProjectView {
|
||||
id: clideProjectView
|
||||
|
||||
ClideApplicationView {
|
||||
projectDir: appWindow.appContextPath
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user