From 125041f4698edca0770d00929170de76d2a81d23 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Fri, 6 Feb 2026 19:32:02 -0500 Subject: [PATCH] Add ClideEditorView. --- build.rs | 1 + qml/ClideEditor.qml | 232 +++++++++++++++++---------------------- qml/ClideEditorView.qml | 46 ++++++++ qml/ClideProjectView.qml | 2 +- 4 files changed, 148 insertions(+), 133 deletions(-) create mode 100644 qml/ClideEditorView.qml diff --git a/build.rs b/build.rs index 72e9b4e..8c91aa8 100644 --- a/build.rs +++ b/build.rs @@ -7,6 +7,7 @@ fn main() { "qml/ClideTreeView.qml", "qml/ClideProjectView.qml", "qml/ClideEditor.qml", + "qml/ClideEditorView.qml", "qml/ClideMenuBar.qml", "qml/ClideLogger.qml", "qml/Components/ClideScrollBar.qml", diff --git a/qml/ClideEditor.qml b/qml/ClideEditor.qml index 262642e..df221a3 100644 --- a/qml/ClideEditor.qml +++ b/qml/ClideEditor.qml @@ -9,160 +9,128 @@ import QtQuick.Layouts import clide.module 1.0 import Logger 1.0 -SplitView { - id: root +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 - // 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. - required property string filePath + Layout.fillHeight: true + Layout.fillWidth: false + // Calculating the width correctly is important as the number grows. + // We need to ensure space required to show N line number digits. + // We use log10 to find how many digits are needed in a line number. + // log10(9) ~= .95; log10(10) = 1.0; log10(100) = 2.0 ...etc + // We +1 to ensure space for at least 1 digit, as floor(1.95) = 1. + // The +10 is additional spacing and can be adjusted. + Layout.preferredWidth: fontMetrics.averageCharacterWidth * (Math.floor(Math.log10(textArea.lineCount)) + 1) + 10 + contentY: editorFlickable.contentY + interactive: false - Layout.fillHeight: true - Layout.fillWidth: true - orientation: Qt.Vertical + Column { + anchors.fill: parent + topPadding: textArea.topPadding - // Customized handle to drag between the Editor and the Console. - handle: ClideHandle { - pressed: SplitHandle.pressed - hovered: SplitHandle.hovered - } + Repeater { + id: repeatedLineNumbers - Component.onCompleted: { - // Show logging is working. - Logger.info("Info logs"); - Logger.warn("Warning logs"); - Logger.debug("Debug logs"); - Logger.error("Error logs"); - Logger.trace("Trace logs"); - } + // TODO: Bug where text wrapping shows as new line number. + model: textArea.lineCount - 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 + // This Item is used for each line number in the gutter. + delegate: Item { + required property int index - Layout.fillHeight: true - Layout.fillWidth: false - // Calculating the width correctly is important as the number grows. - // We need to ensure space required to show N line number digits. - // We use log10 to find how many digits are needed in a line number. - // log10(9) ~= .95; log10(10) = 1.0; log10(100) = 2.0 ...etc - // We +1 to ensure space for at least 1 digit, as floor(1.95) = 1. - // The +10 is additional spacing and can be adjusted. - Layout.preferredWidth: fontMetrics.averageCharacterWidth * (Math.floor(Math.log10(textArea.lineCount)) + 1) + 10 - contentY: editorFlickable.contentY - interactive: false + // Calculates the height of each line in the text area. + height: textArea.contentHeight / textArea.lineCount + width: parent.width - Column { - anchors.fill: parent - topPadding: textArea.topPadding + // Show the line number. + Label { + id: numbers - Repeater { - id: repeatedLineNumbers + color: RustColors.linenumber + font: textArea.font + height: parent.height + horizontalAlignment: Text.AlignLeft + text: parent.index + 1 + verticalAlignment: Text.AlignVCenter + width: parent.width - indicator.width - // TODO: Bug where text wrapping shows as new line number. - model: textArea.lineCount - - // This Item is used for each line number in the gutter. - delegate: Item { - required property int index - - // Calculates the height of each line in the text area. - height: textArea.contentHeight / textArea.lineCount - width: parent.width - - // Show the line number. - Label { - id: numbers - - color: RustColors.linenumber - font: textArea.font - height: parent.height - horizontalAlignment: Text.AlignLeft - text: parent.index + 1 - verticalAlignment: Text.AlignVCenter - width: parent.width - indicator.width - - background: Rectangle { - color: RustColors.terminal_background - } + background: Rectangle { + color: RustColors.terminal_background } - // Draw edge along the right side of the line number. - Rectangle { - id: indicator + } + // Draw edge along the right side of the line number. + Rectangle { + id: indicator - anchors.left: numbers.right - color: RustColors.linenumber - height: parent.height - width: 1 - } + anchors.left: numbers.right + color: RustColors.linenumber + height: parent.height + width: 1 } } } } - Flickable { - id: editorFlickable + } + Flickable { + id: editorFlickable - Layout.fillHeight: true - Layout.fillWidth: true - boundsBehavior: Flickable.StopAtBounds - height: 650 + Layout.fillHeight: true + Layout.fillWidth: true + boundsBehavior: Flickable.StopAtBounds + height: 650 - ScrollBar.horizontal: ClideScrollBar { - } - ScrollBar.vertical: ClideScrollBar { - } - TextArea.flickable: TextArea { - id: textArea + ScrollBar.horizontal: ClideScrollBar { + } + ScrollBar.vertical: ClideScrollBar { + } + TextArea.flickable: TextArea { + id: textArea - antialiasing: true - focus: true - persistentSelection: true - selectByMouse: true - selectedTextColor: RustColors.editor_highlighted_text - selectionColor: RustColors.editor_highlight - text: FileSystem.readFile(root.filePath) - textFormat: Qt.AutoText - wrapMode: TextArea.Wrap + antialiasing: true + focus: true + persistentSelection: true + selectByMouse: true + selectedTextColor: RustColors.editor_highlighted_text + selectionColor: RustColors.editor_highlight + text: FileSystem.readFile(root.filePath) + textFormat: Qt.AutoText + wrapMode: TextArea.Wrap - 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() - // } - // } + onLinkActivated: function (link) { + Qt.openUrlExternally(link); } - FontMetrics { - id: fontMetrics + // 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() + // } + // } + } - font: textArea.font - } + FontMetrics { + id: fontMetrics + + font: textArea.font } } - ClideLogger { - id: areaConsole - - } } diff --git a/qml/ClideEditorView.qml b/qml/ClideEditorView.qml new file mode 100644 index 0000000..9d1325f --- /dev/null +++ b/qml/ClideEditorView.qml @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2026, Shaun Reed +// +// 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 + + // 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. + required property string filePath + + Layout.fillHeight: true + Layout.fillWidth: true + orientation: Qt.Vertical + + // Customized handle to drag between the Editor and the Console. + handle: ClideHandle { + pressed: SplitHandle.pressed + hovered: SplitHandle.hovered + } + + Component.onCompleted: { + // Show logging is working. + Logger.info("Info logs"); + Logger.warn("Warning logs"); + Logger.debug("Debug logs"); + Logger.error("Error logs"); + Logger.trace("Trace logs"); + } + + ClideEditor{ + id: clideEditor + } + ClideLogger { + id: areaConsole + + } +} diff --git a/qml/ClideProjectView.qml b/qml/ClideProjectView.qml index 309404b..02066bb 100644 --- a/qml/ClideProjectView.qml +++ b/qml/ClideProjectView.qml @@ -100,7 +100,7 @@ SplitView { } } } - ClideEditor { + ClideEditorView { id: clideEditor SplitView.fillWidth: true