TUI #1

Merged
shaunrd0 merged 73 commits from ui into master 2026-01-25 20:57:37 +00:00
4 changed files with 258 additions and 237 deletions
Showing only changes of commit 70e9f79c8a - Show all commits

View File

@ -12,6 +12,8 @@ fn main() {
uri: "clide.module", uri: "clide.module",
rust_files: &["src/main.rs"], rust_files: &["src/main.rs"],
qml_files: &["qml/main.qml", qml_files: &["qml/main.qml",
"qml/ProjectView/ClideProjectView.qml",
"qml/Editor/ClideEditor.qml",
"qml/Menu/ClideMenu.qml", "qml/Menu/ClideMenu.qml",
"qml/Menu/ClideMenuBar.qml", "qml/Menu/ClideMenuBar.qml",
"qml/Menu/ClideMenuBarItem.qml"], "qml/Menu/ClideMenuBarItem.qml"],

200
qml/Editor/ClideEditor.qml Normal file
View File

@ -0,0 +1,200 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import clide.module 1.0
SplitView {
Layout.fillHeight: true
Layout.fillWidth: true
orientation: Qt.Vertical
// Customized handle to drag between the Editor and the Console.
handle: Rectangle {
border.color: SplitHandle.hovered ? "#2b2b2b" : "#3c3f41"
color: SplitHandle.pressed ? "#4b4f51" : "#3c3f41"
implicitHeight: 8
opacity: SplitHandle.hovered || areaConsole.height < 15 ? 1.0 : 0.0
Behavior on opacity {
OpacityAnimator {
duration: 1400
}
}
}
RowLayout {
// 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.
Flickable {
id: lineNumbers
Layout.fillHeight: true
Layout.fillWidth: false
// Calculate the width based on the logarithmic scale.
Layout.preferredWidth: fontMetrics.averageCharacterWidth * (Math.floor(Math.log10(areaText.lineCount)) + 1) + 10
contentY: editorFlickable.contentY
interactive: false
visible: true
Column {
anchors.fill: parent
Repeater {
id: repeatedLineNumbers
delegate: Item {
required property int index
height: Math.ceil(fontMetrics.lineSpacing)
width: parent.width
Label {
id: numbers
color: "white"
font: areaText.font
height: parent.height
horizontalAlignment: Text.AlignLeft
text: parent.index + 1
verticalAlignment: Text.AlignVCenter
width: parent.width
}
Rectangle {
id: indicator
anchors.left: numbers.right
color: Qt.darker("#FFF", 3)
height: parent.height
width: 1
}
}
model: LineCount {
id: lineCountModel
// This count sets the max line numbers shown in the gutter.
count: areaText.lineCount
}
}
}
}
Flickable {
id: editorFlickable
property alias areaText: areaText
Layout.fillHeight: true
Layout.fillWidth: true
boundsBehavior: Flickable.StopAtBounds
height: 650
ScrollBar.horizontal: MyScrollBar {
}
ScrollBar.vertical: MyScrollBar {
}
TextArea.flickable: TextArea {
id: areaText
color: "#ccced3"
focus: true
persistentSelection: true
selectByMouse: true
// selectedTextColor: control.palette.highlightedText
// selectionColor: control.palette.highlight
textFormat: Qt.AutoText
wrapMode: TextArea.Wrap
onTextChanged: {
console.log("Updated line count: " + areaText.lineCount)
}
background: Rectangle {
color: "#2b2b2b"
}
onLinkActivated: function (link) {
Qt.openUrlExternally(link);
}
// TODO: Handle saving
// Component.onCompleted: {
// if (Qt.application.arguments.length === 2)
// textDocument.source = "file:" + Qt.application.arguments[1]
// else
// textDocument.source = "qrc:/texteditor.html"
// }
// textDocument.onStatusChanged: {
// // a message lookup table using computed properties:
// // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
// const statusMessages = {
// [ TextDocument.ReadError ]: qsTr("Failed to load %1"),
// [ TextDocument.WriteError ]: qsTr("Failed to save %1"),
// [ TextDocument.NonLocalFileError ]: qsTr("Not a local file: %1"),
// }
// const err = statusMessages[textDocument.status]
// if (err) {
// errorDialog.text = err.arg(textDocument.source)
// errorDialog.open()
// }
// }
}
FontMetrics {
id: fontMetrics
font: areaText.font
}
}
}
TextArea {
id: areaConsole
height: 100
placeholderText: qsTr("Placeholder for bash terminal.")
placeholderTextColor: "white"
readOnly: true
wrapMode: TextArea.Wrap
background: Rectangle {
color: "#2b2b2b"
implicitHeight: 100
// border.color: control.enabled ? "#21be2b" : "transparent"
}
}
// We use an inline component to customize the horizontal and vertical
// scroll-bars. This is convenient when the component is only used in one file.
component MyScrollBar: ScrollBar {
id: scrollBar
// Scroll bar gutter
background: Rectangle {
color: "#3b3b3b"
implicitHeight: scrollBar.interactive ? 8 : 4
implicitWidth: scrollBar.interactive ? 8 : 4
opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0
Behavior on opacity {
OpacityAnimator {
duration: 500
}
}
}
// Scroll bar
contentItem: Rectangle {
color: "#4b4f51"
implicitHeight: scrollBar.interactive ? 8 : 4
implicitWidth: scrollBar.interactive ? 8 : 4
opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.2
Behavior on opacity {
OpacityAnimator {
duration: 1000
}
}
}
}
}

View File

@ -0,0 +1,54 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "../Editor"
SplitView {
Layout.fillHeight: true
Layout.fillWidth: true
anchors.fill: parent
// Customized handle to drag between the Navigation and the Editor.
handle: Rectangle {
border.color: SplitHandle.hovered ? "#303234" : "#3c3f41"
color: SplitHandle.pressed ? "#4b4f51" : "#3c3f41"
implicitWidth: 8
opacity: SplitHandle.hovered || navigationView.width < 15 ? 1.0 : 0.0
Behavior on opacity {
OpacityAnimator {
duration: 1400
}
}
}
Rectangle {
id: navigationView
SplitView.fillHeight: true
SplitView.preferredWidth: 200
color: "#303234"
StackLayout {
anchors.fill: parent
// Shows the help text.
TextArea {
placeholderText: qsTr("File system view placeholder")
placeholderTextColor: "white"
readOnly: true
wrapMode: TextArea.Wrap
}
// TODO: Shows the files on the file system.
// ClideTreeView {
// id: fileSystemView
// color: Colors.surface1
// onFileClicked: path => root.currentFilePath = path
// }
}
}
ClideEditor {
}
}

View File

@ -4,6 +4,7 @@ import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import "Menu" import "Menu"
import "ProjectView"
import clide.module 1.0 import clide.module 1.0
@ -27,243 +28,7 @@ ApplicationWindow {
title: qsTr("Error") title: qsTr("Error")
} }
RowLayout { ClideProjectView {
anchors.fill: parent
spacing: 0
SplitView {
Layout.fillHeight: true
Layout.fillWidth: true
// Customized handle to drag between the Navigation and the Editor.
handle: Rectangle {
border.color: SplitHandle.hovered ? "#303234" : "#3c3f41"
color: SplitHandle.pressed ? "#4b4f51" : "#3c3f41"
implicitWidth: 8
opacity: SplitHandle.hovered || navigationView.width < 15 ? 1.0 : 0.0
Behavior on opacity {
OpacityAnimator {
duration: 1400
}
}
}
Rectangle {
id: navigationView
SplitView.fillHeight: true
SplitView.preferredWidth: 200
color: "#303234"
StackLayout {
anchors.fill: parent
// Shows the help text.
TextArea {
placeholderText: qsTr("File system view placeholder")
placeholderTextColor: "white"
readOnly: true
wrapMode: TextArea.Wrap
}
// TODO: Shows the files on the file system.
// ClideProjectView {
// id: fileSystemView
// color: Colors.surface1
// onFileClicked: path => root.currentFilePath = path
// }
}
}
SplitView {
Layout.fillHeight: true
Layout.fillWidth: true
orientation: Qt.Vertical
// Customized handle to drag between the Navigation and the Editor.
handle: Rectangle {
border.color: SplitHandle.hovered ? "#2b2b2b" : "#3c3f41"
color: SplitHandle.pressed ? "#4b4f51" : "#3c3f41"
implicitHeight: 8
opacity: SplitHandle.hovered || navigationView.width < 15 ? 1.0 : 0.0
Behavior on opacity {
OpacityAnimator {
duration: 1400
}
}
}
RowLayout {
// 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.
Flickable {
id: lineNumbers
Layout.fillHeight: true
Layout.fillWidth: false
// Calculate the width based on the logarithmic scale.
Layout.preferredWidth: fontMetrics.averageCharacterWidth * (Math.floor(Math.log10(areaText.lineCount)) + 1) + 10
contentY: editorFlickable.contentY
interactive: false
visible: true
Column {
anchors.fill: parent
Repeater {
id: repeatedLineNumbers
delegate: Item {
required property int index
height: Math.ceil(fontMetrics.lineSpacing)
width: parent.width
Label {
id: numbers
color: "white"
font: areaText.font
height: parent.height
horizontalAlignment: Text.AlignLeft
text: parent.index + 1
verticalAlignment: Text.AlignVCenter
width: parent.width
}
Rectangle {
id: indicator
anchors.left: numbers.right
color: Qt.darker("#FFF", 3)
height: parent.height
width: 1
}
}
model: LineCount {
id: lineCountModel
// This count sets the max line numbers shown in the gutter.
count: areaText.lineCount
}
}
}
}
Flickable {
id: editorFlickable
height: 650
property alias areaText: areaText
Layout.fillHeight: true
Layout.fillWidth: true
boundsBehavior: Flickable.StopAtBounds
ScrollBar.horizontal: MyScrollBar {
}
ScrollBar.vertical: MyScrollBar {
}
TextArea.flickable: TextArea {
id: areaText
color: "#ccced3"
focus: true
persistentSelection: true
selectByMouse: true
// selectedTextColor: control.palette.highlightedText
// selectionColor: control.palette.highlight
textFormat: Qt.AutoText
wrapMode: TextArea.Wrap
background: Rectangle {
color: "#2b2b2b"
}
onLinkActivated: function (link) {
Qt.openUrlExternally(link);
}
// TODO: Handle saving
// Component.onCompleted: {
// if (Qt.application.arguments.length === 2)
// textDocument.source = "file:" + Qt.application.arguments[1]
// else
// textDocument.source = "qrc:/texteditor.html"
// }
// textDocument.onStatusChanged: {
// // a message lookup table using computed properties:
// // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
// const statusMessages = {
// [ TextDocument.ReadError ]: qsTr("Failed to load %1"),
// [ TextDocument.WriteError ]: qsTr("Failed to save %1"),
// [ TextDocument.NonLocalFileError ]: qsTr("Not a local file: %1"),
// }
// const err = statusMessages[textDocument.status]
// if (err) {
// errorDialog.text = err.arg(textDocument.source)
// errorDialog.open()
// }
// }
}
FontMetrics {
id: fontMetrics
font: areaText.font
}
}
}
TextArea {
id: areaConsole
height: 100
placeholderText: qsTr("Placeholder for bash terminal.")
placeholderTextColor: "white"
readOnly: true
wrapMode: TextArea.Wrap
background: Rectangle {
color: "#2b2b2b"
implicitHeight: 100
// border.color: control.enabled ? "#21be2b" : "transparent"
}
}
}
}
}
// We use an inline component to customize the horizontal and vertical
// scroll-bars. This is convenient when the component is only used in one file.
component MyScrollBar: ScrollBar {
id: scrollBar
background: Rectangle {
color: "#2b2b2b"
implicitHeight: scrollBar.interactive ? 8 : 4
implicitWidth: scrollBar.interactive ? 8 : 4
opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0
Behavior on opacity {
OpacityAnimator {
duration: 500
}
}
}
contentItem: Rectangle {
color: "#4b4f51"
implicitHeight: scrollBar.interactive ? 8 : 4
implicitWidth: scrollBar.interactive ? 8 : 4
opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0
Behavior on opacity {
OpacityAnimator {
duration: 1000
}
}
}
} }
} }