Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e5b91eaed8 | |||
| be383869b2 | |||
| b9eee50e52 | |||
| 4cc43916cb | |||
| a5bed9ed2c | |||
| 0fac2b71ab | |||
| db2f878018 | |||
| 048d40eb83 | |||
| c70bba16e4 | |||
| 325cf285fc | |||
| aa8590cd5c |
17
README.md
17
README.md
@ -19,6 +19,22 @@ And of course, [Rust](https://www.rust-lang.org/tools/install).
|
|||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This project requires at least Qt 6.7. To check your Qt version
|
||||||
|
|
||||||
|
```bash
|
||||||
|
qmake6 -query QT_VERSION
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the [Qt Installer](https://www.qt.io/development/download) to download and install the Qt version of your choice.
|
||||||
|
|
||||||
|
**You must set the QMAKE variable before building clide**. This should be a path to `qmake6` binary installed on your system.
|
||||||
|
The following export is the default installation path for Qt 6.7 on Ubuntu 24.04
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export QMAKE=$HOME/Qt/6.7.3/gcc_64/bin/qmake6
|
||||||
|
export LD_LIBRARY_PATH=$HOME/Qt/6.7.3/gcc_64/lib
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
To install and run clide
|
To install and run clide
|
||||||
@ -126,6 +142,7 @@ Some helpful links for reading up on QML if you're just getting started.
|
|||||||
* [All QML Controls Types](https://doc.qt.io/qt-6/qtquick-controls-qmlmodule.html)
|
* [All QML Controls Types](https://doc.qt.io/qt-6/qtquick-controls-qmlmodule.html)
|
||||||
* [KDAB CXX-Qt Book](https://kdab.github.io/cxx-qt/book/)
|
* [KDAB CXX-Qt Book](https://kdab.github.io/cxx-qt/book/)
|
||||||
* [github.com/KDAB/cxx-qt](https://github.com/KDAB/cxx-qt)
|
* [github.com/KDAB/cxx-qt](https://github.com/KDAB/cxx-qt)
|
||||||
|
* [QML and C++ Intergration](https://doc.qt.io/qt-6/qtqml-cppintegration-overview.html)
|
||||||
|
|
||||||
|
|
||||||
### Plugins
|
### Plugins
|
||||||
|
|||||||
3
build.rs
3
build.rs
@ -8,6 +8,8 @@ fn main() {
|
|||||||
"qml/ClideProjectView.qml",
|
"qml/ClideProjectView.qml",
|
||||||
"qml/ClideEditor.qml",
|
"qml/ClideEditor.qml",
|
||||||
"qml/ClideMenuBar.qml",
|
"qml/ClideMenuBar.qml",
|
||||||
|
"qml/ClideLogger.qml",
|
||||||
|
"qml/Logger/Logger.qml",
|
||||||
]))
|
]))
|
||||||
// Link Qt's Network library
|
// Link Qt's Network library
|
||||||
// - Qt Core is always linked
|
// - Qt Core is always linked
|
||||||
@ -18,6 +20,7 @@ fn main() {
|
|||||||
.qt_module("Gui")
|
.qt_module("Gui")
|
||||||
.qt_module("Svg")
|
.qt_module("Svg")
|
||||||
.qt_module("Xml")
|
.qt_module("Xml")
|
||||||
|
.qrc("./resources.qrc")
|
||||||
.files(["src/gui/colors.rs", "src/gui/filesystem.rs"])
|
.files(["src/gui/colors.rs", "src/gui/filesystem.rs"])
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
@ -1,4 +1,6 @@
|
|||||||
// TODO: Header
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls.Basic
|
import QtQuick.Controls.Basic
|
||||||
@ -29,7 +31,7 @@ ApplicationWindow {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.margins: 20
|
anchors.margins: 20
|
||||||
|
|
||||||
source: "../icons/kilroy-256.png"
|
source: "qrc:/images/kilroy.png"
|
||||||
sourceSize.width: 80
|
sourceSize.width: 80
|
||||||
sourceSize.height: 80
|
sourceSize.height: 80
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|||||||
@ -1,8 +1,13 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import clide.module 1.0
|
import clide.module 1.0
|
||||||
|
import Logger 1.0
|
||||||
|
|
||||||
SplitView {
|
SplitView {
|
||||||
id: root
|
id: root
|
||||||
@ -74,6 +79,9 @@ SplitView {
|
|||||||
text: parent.index + 1
|
text: parent.index + 1
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
width: parent.width - indicator.width
|
width: parent.width - indicator.width
|
||||||
|
background: Rectangle {
|
||||||
|
color: RustColors.terminal_background
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Draw edge along the right side of the line number.
|
// Draw edge along the right side of the line number.
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@ -145,19 +153,8 @@ SplitView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TextArea {
|
ClideLogger {
|
||||||
id: areaConsole
|
id: areaConsole
|
||||||
|
|
||||||
height: 100
|
|
||||||
placeholderText: qsTr("Placeholder for bash terminal.")
|
|
||||||
placeholderTextColor: "white"
|
|
||||||
readOnly: true
|
|
||||||
wrapMode: TextArea.Wrap
|
|
||||||
background: Rectangle {
|
|
||||||
color: RustColors.editor_background
|
|
||||||
implicitHeight: 100
|
|
||||||
// border.color: control.enabled ? RustColors.active : RustColors.inactive
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use an inline component to customize the horizontal and vertical
|
// We use an inline component to customize the horizontal and vertical
|
||||||
@ -203,4 +200,11 @@ SplitView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
// Show logging is working.
|
||||||
|
Logger.debug("Debug console ready")
|
||||||
|
Logger.warn("Warnings show up too")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
58
qml/ClideLogger.qml
Normal file
58
qml/ClideLogger.qml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// 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 clide.module 1.0
|
||||||
|
import Logger 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
ListModel { id: model }
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "#111"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: listView
|
||||||
|
anchors.fill: parent
|
||||||
|
model: model
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
function getLogColor(level) {
|
||||||
|
switch (level) {
|
||||||
|
case "INFO":
|
||||||
|
return RustColors.info_log
|
||||||
|
break;
|
||||||
|
case "DEBUG":
|
||||||
|
return RustColors.debug_log
|
||||||
|
break;
|
||||||
|
case "WARN":
|
||||||
|
return RustColors.warn_log
|
||||||
|
break;
|
||||||
|
case "ERROR":
|
||||||
|
return RustColors.error_log
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return RustColors.info_log
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Text {
|
||||||
|
text: `[${level}] ${message}`
|
||||||
|
font.family: "monospace"
|
||||||
|
color: listView.getLogColor(level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Logger
|
||||||
|
function onLogged(level, message) {
|
||||||
|
model.append({ level, message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,23 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import clide.module 1.0
|
import clide.module 1.0
|
||||||
|
|
||||||
MenuBar {
|
MenuBar {
|
||||||
|
// Background for this MenuBar.
|
||||||
|
background: Rectangle {
|
||||||
|
color: RustColors.menubar
|
||||||
|
border.color: RustColors.explorer_background
|
||||||
|
}
|
||||||
|
|
||||||
// Base settings for each Menu.
|
// Base settings for each Menu.
|
||||||
component ClideMenu : Menu {
|
component ClideMenu : Menu {
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: RustColors.menubar
|
color: RustColors.explorer_background
|
||||||
implicitWidth: 100
|
implicitWidth: 100
|
||||||
radius: 2
|
radius: 2
|
||||||
}
|
}
|
||||||
@ -19,7 +29,7 @@ MenuBar {
|
|||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: root.hovered ? RustColors.hovered : RustColors.unhovered
|
color: root.hovered ? RustColors.hovered : RustColors.unhovered
|
||||||
radius: 2.5
|
radius: 1.0
|
||||||
}
|
}
|
||||||
contentItem: IconLabel {
|
contentItem: IconLabel {
|
||||||
color: "black"
|
color: "black"
|
||||||
@ -28,13 +38,6 @@ MenuBar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background for this MenuBar.
|
|
||||||
background: Rectangle {
|
|
||||||
color: RustColors.menubar
|
|
||||||
border.color: RustColors.menubar_border
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// File Menu
|
// File Menu
|
||||||
Action {
|
Action {
|
||||||
@ -75,7 +78,7 @@ MenuBar {
|
|||||||
MenuSeparator {
|
MenuSeparator {
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
border.color: color
|
border.color: color
|
||||||
color: RustColors.menubar_border
|
color: RustColors.explorer_background
|
||||||
implicitHeight: 3
|
implicitHeight: 3
|
||||||
implicitWidth: 200
|
implicitWidth: 200
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,13 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import clide.module 1.0
|
import clide.module 1.0
|
||||||
|
import Logger 1.0
|
||||||
|
|
||||||
SplitView {
|
SplitView {
|
||||||
id: root
|
id: root
|
||||||
@ -39,18 +44,58 @@ SplitView {
|
|||||||
SplitView.preferredWidth: 200
|
SplitView.preferredWidth: 200
|
||||||
SplitView.maximumWidth: 250
|
SplitView.maximumWidth: 250
|
||||||
|
|
||||||
StackLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
spacing: 2
|
||||||
|
// TODO: Make a ClideBreadCrumb element to support select parent paths as root
|
||||||
|
Rectangle {
|
||||||
|
width: navigationView.width
|
||||||
|
height: 25
|
||||||
|
color: RustColors.explorer_background
|
||||||
|
Text {
|
||||||
|
id: breadCrumb
|
||||||
|
anchors.fill: parent
|
||||||
|
text: clideTreeView.rootDirectory
|
||||||
|
color: RustColors.explorer_text
|
||||||
|
elide: Text.ElideLeft
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onSingleTapped: (eventPoint, button) => contextMenu.popup()
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
id: contextMenu
|
||||||
|
Action {
|
||||||
|
text: qsTr("Reset root index")
|
||||||
|
onTriggered: {
|
||||||
|
Logger.log("Resetting root directory: " + clideTreeView.originalRootDirectory)
|
||||||
|
clideTreeView.rootDirectory = clideTreeView.originalRootDirectory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ClideTreeView {
|
ClideTreeView {
|
||||||
id: clideTreeView
|
id: clideTreeView
|
||||||
onFileClicked: path => root.projectDir = path
|
onFileClicked: path => clideEditor.filePath = path
|
||||||
|
width: navigationView.width
|
||||||
|
height: navigationView.height
|
||||||
|
|
||||||
// Path to the directory opened in the file explorer.
|
// Path to the directory opened in the file explorer.
|
||||||
|
originalRootDirectory: root.projectDir
|
||||||
rootDirectory: root.projectDir
|
rootDirectory: root.projectDir
|
||||||
|
onRootDirectoryChanged: {
|
||||||
|
Logger.log(clideTreeView.rootDirectory)
|
||||||
|
breadCrumb.text = clideTreeView.rootDirectory
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClideEditor {
|
ClideEditor {
|
||||||
|
id: clideEditor
|
||||||
SplitView.fillWidth: true
|
SplitView.fillWidth: true
|
||||||
|
|
||||||
// Provide a path to the file currently open in the text editor.
|
// Provide a path to the file currently open in the text editor.
|
||||||
|
|||||||
@ -1,151 +1,168 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import clide.module 1.0
|
import clide.module 1.0
|
||||||
|
import Logger 1.0
|
||||||
|
|
||||||
Rectangle {
|
TreeView {
|
||||||
id: root
|
id: fileSystemTreeView
|
||||||
color: RustColors.explorer_background
|
model: FileSystem
|
||||||
|
|
||||||
required property string rootDirectory
|
property int lastIndex: -1
|
||||||
|
|
||||||
|
required property string originalRootDirectory
|
||||||
|
property string rootDirectory
|
||||||
|
|
||||||
signal fileClicked(string filePath)
|
signal fileClicked(string filePath)
|
||||||
|
|
||||||
TreeView {
|
rootIndex: FileSystem.setDirectory(fileSystemTreeView.rootDirectory)
|
||||||
id: fileSystemTreeView
|
leftMargin: 5
|
||||||
anchors.margins: 15
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
boundsMovement: Flickable.StopAtBounds
|
||||||
|
clip: true
|
||||||
|
|
||||||
property int lastIndex: -1
|
// The delegate represents a single entry in the filesystem.
|
||||||
|
delegate: TreeViewDelegate {
|
||||||
|
id: treeDelegate
|
||||||
|
indentation: 12
|
||||||
|
implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250
|
||||||
|
implicitHeight: 25
|
||||||
|
|
||||||
model: FileSystem
|
required property int index
|
||||||
anchors.fill: parent
|
required property url filePath
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
required property string fileName
|
||||||
boundsMovement: Flickable.StopAtBounds
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
indicator: Image {
|
||||||
FileSystem.setDirectory(root.rootDirectory)
|
id: directoryIcon
|
||||||
fileSystemTreeView.expandRecursively(0, -1)
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
contentItem: Text {
|
||||||
delegate: TreeViewDelegate {
|
text: treeDelegate.fileName
|
||||||
id: treeDelegate
|
color: RustColors.explorer_text
|
||||||
indentation: 8
|
}
|
||||||
implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250
|
|
||||||
implicitHeight: 25
|
|
||||||
|
|
||||||
required property int index
|
background: Rectangle {
|
||||||
required property url filePath
|
// TODO: Fix flickering from color transition on states here.
|
||||||
required property string fileName
|
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 {
|
Behavior on color {
|
||||||
id: directoryIcon
|
ColorAnimation {
|
||||||
|
duration: 300
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.setDirectory(treeDelegate.filePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Action {
|
|
||||||
text: qsTr("Reset root index")
|
|
||||||
onTriggered: {
|
|
||||||
FileSystem.setDirectory("")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provide our own custom ScrollIndicator for the TreeView.
|
MultiEffect {
|
||||||
ScrollIndicator.vertical: ScrollIndicator {
|
id: iconOverlay
|
||||||
active: true
|
|
||||||
implicitWidth: 15
|
|
||||||
|
|
||||||
contentItem: Rectangle {
|
anchors.fill: directoryIcon
|
||||||
implicitWidth: 6
|
source: directoryIcon
|
||||||
implicitHeight: 6
|
colorization: 1.0
|
||||||
|
brightness: 1.0
|
||||||
|
colorizationColor: {
|
||||||
|
const isFile = !treeDelegate.hasChildren;
|
||||||
|
if (isFile)
|
||||||
|
return Qt.lighter(RustColors.explorer_folder, 2)
|
||||||
|
|
||||||
color: RustColors.scrollbar
|
const isExpandedFolder = treeDelegate.expanded && treeDelegate.hasChildren;
|
||||||
opacity: fileSystemTreeView.movingVertically ? 0.5 : 0.0
|
if (isExpandedFolder)
|
||||||
|
return Qt.darker(RustColors.explorer_folder, 2)
|
||||||
|
else
|
||||||
|
return RustColors.explorer_folder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
HoverHandler {
|
||||||
OpacityAnimator {
|
id: hoverHandler
|
||||||
duration: 500
|
acceptedDevices: PointerDevice.Mouse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
break;
|
||||||
|
case Qt.RightButton:
|
||||||
|
contextMenu.popup();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
id: contextMenu
|
||||||
|
Action {
|
||||||
|
text: qsTr("Set as root index")
|
||||||
|
enabled: treeDelegate.hasChildren
|
||||||
|
onTriggered: {
|
||||||
|
Logger.debug("Setting new root directory: " + treeDelegate.filePath)
|
||||||
|
fileSystemTreeView.rootDirectory = treeDelegate.filePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action {
|
||||||
|
text: qsTr("Reset root index")
|
||||||
|
onTriggered: {
|
||||||
|
Logger.log("Resetting root directory: " + fileSystemTreeView.originalRootDirectory)
|
||||||
|
fileSystemTreeView.rootDirectory = fileSystemTreeView.originalRootDirectory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
qml/Logger/Logger.qml
Normal file
30
qml/Logger/Logger.qml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
|
pragma Singleton
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
signal logged(string level, string message)
|
||||||
|
|
||||||
|
function log(msg) {
|
||||||
|
console.log(msg)
|
||||||
|
logged("INFO", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug(msg) {
|
||||||
|
console.log(msg)
|
||||||
|
logged("DEBUG", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
function warn(msg) {
|
||||||
|
console.warn(msg)
|
||||||
|
logged("WARN", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
function error(msg) {
|
||||||
|
console.error(msg)
|
||||||
|
logged("ERROR", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
1
qml/Logger/qmldir
Normal file
1
qml/Logger/qmldir
Normal file
@ -0,0 +1 @@
|
|||||||
|
singleton Logger 1.0 Logger.qml
|
||||||
13
qml/main.qml
13
qml/main.qml
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@ -15,13 +19,7 @@ ApplicationWindow {
|
|||||||
|
|
||||||
required property string appContextPath
|
required property string appContextPath
|
||||||
|
|
||||||
menuBar: ClideMenuBar {
|
menuBar: ClideMenuBar { }
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: RustColors.gutter
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageDialog {
|
MessageDialog {
|
||||||
id: errorDialog
|
id: errorDialog
|
||||||
@ -29,6 +27,7 @@ ApplicationWindow {
|
|||||||
title: qsTr("Error")
|
title: qsTr("Error")
|
||||||
}
|
}
|
||||||
ClideProjectView {
|
ClideProjectView {
|
||||||
|
id: clideProjectView
|
||||||
projectDir: appWindow.appContextPath
|
projectDir: appWindow.appContextPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
resources.qrc
Normal file
5
resources.qrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/images">
|
||||||
|
<file alias="kilroy.png">images/kilroy-256.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
use crate::AppContext;
|
use crate::AppContext;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use cxx_qt_lib::{QMapPair, QMapPair_QString_QVariant, QString, QVariant};
|
use cxx_qt_lib::{QMapPair, QMapPair_QString_QVariant, QString, QVariant};
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
#[cxx_qt::bridge]
|
#[cxx_qt::bridge]
|
||||||
|
|
||||||
pub mod qobject {
|
pub mod qobject {
|
||||||
unsafe extern "C++" {
|
unsafe extern "C++" {
|
||||||
include!("cxx-qt-lib/qcolor.h");
|
include!("cxx-qt-lib/qcolor.h");
|
||||||
@ -31,6 +36,11 @@ pub mod qobject {
|
|||||||
#[qproperty(QColor, explorer_background)]
|
#[qproperty(QColor, explorer_background)]
|
||||||
#[qproperty(QColor, explorer_folder)]
|
#[qproperty(QColor, explorer_folder)]
|
||||||
#[qproperty(QColor, explorer_folder_open)]
|
#[qproperty(QColor, explorer_folder_open)]
|
||||||
|
#[qproperty(QColor, terminal_background)]
|
||||||
|
#[qproperty(QColor, info_log)]
|
||||||
|
#[qproperty(QColor, debug_log)]
|
||||||
|
#[qproperty(QColor, warn_log)]
|
||||||
|
#[qproperty(QColor, error_log)]
|
||||||
type RustColors = super::RustColorsImpl;
|
type RustColors = super::RustColorsImpl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,6 +70,11 @@ pub struct RustColorsImpl {
|
|||||||
explorer_background: QColor,
|
explorer_background: QColor,
|
||||||
explorer_folder: QColor,
|
explorer_folder: QColor,
|
||||||
explorer_folder_open: QColor,
|
explorer_folder_open: QColor,
|
||||||
|
terminal_background: QColor,
|
||||||
|
info_log: QColor,
|
||||||
|
debug_log: QColor,
|
||||||
|
warn_log: QColor,
|
||||||
|
error_log: QColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RustColorsImpl {
|
impl Default for RustColorsImpl {
|
||||||
@ -68,7 +83,7 @@ impl Default for RustColorsImpl {
|
|||||||
hovered: QColor::try_from("#303234").unwrap(),
|
hovered: QColor::try_from("#303234").unwrap(),
|
||||||
unhovered: QColor::try_from("#3c3f41").unwrap(),
|
unhovered: QColor::try_from("#3c3f41").unwrap(),
|
||||||
pressed: QColor::try_from("#4b4f51").unwrap(),
|
pressed: QColor::try_from("#4b4f51").unwrap(),
|
||||||
menubar: QColor::try_from("#3c3f41").unwrap(),
|
menubar: QColor::try_from("#262626").unwrap(),
|
||||||
menubar_border: QColor::try_from("#575757").unwrap(),
|
menubar_border: QColor::try_from("#575757").unwrap(),
|
||||||
scrollbar: QColor::try_from("#4b4f51").unwrap(),
|
scrollbar: QColor::try_from("#4b4f51").unwrap(),
|
||||||
scrollbar_active: QColor::try_from("#4b4f51").unwrap(),
|
scrollbar_active: QColor::try_from("#4b4f51").unwrap(),
|
||||||
@ -76,17 +91,22 @@ impl Default for RustColorsImpl {
|
|||||||
linenumber: QColor::try_from("#94989b").unwrap(),
|
linenumber: QColor::try_from("#94989b").unwrap(),
|
||||||
active: QColor::try_from("#a9acb0").unwrap(),
|
active: QColor::try_from("#a9acb0").unwrap(),
|
||||||
inactive: QColor::try_from("#FFF").unwrap(),
|
inactive: QColor::try_from("#FFF").unwrap(),
|
||||||
editor_background: QColor::try_from("#2b2b2b").unwrap(),
|
editor_background: QColor::try_from("#111111").unwrap(),
|
||||||
editor_text: QColor::try_from("#acaea3").unwrap(),
|
editor_text: QColor::try_from("#acaea3").unwrap(),
|
||||||
editor_highlighted_text: QColor::try_from("#ccced3").unwrap(),
|
editor_highlighted_text: QColor::try_from("#ccced3").unwrap(),
|
||||||
editor_highlight: QColor::try_from("#ccced3").unwrap(),
|
editor_highlight: QColor::try_from("#ccced3").unwrap(),
|
||||||
gutter: QColor::try_from("#1e1f22").unwrap(),
|
gutter: QColor::try_from("#1e1f22").unwrap(),
|
||||||
explorer_hovered: QColor::try_from("#4c5053").unwrap(),
|
explorer_hovered: QColor::try_from("#4c5053").unwrap(),
|
||||||
explorer_text: QColor::try_from("#3b3b3b").unwrap(),
|
explorer_text: QColor::try_from("#FFF").unwrap(),
|
||||||
explorer_text_selected: QColor::try_from("#8b8b8b").unwrap(),
|
explorer_text_selected: QColor::try_from("#262626").unwrap(),
|
||||||
explorer_background: QColor::try_from("#676c70").unwrap(),
|
explorer_background: QColor::try_from("#1E1F22").unwrap(),
|
||||||
explorer_folder: QColor::try_from("#54585b").unwrap(),
|
explorer_folder: QColor::try_from("#54585b").unwrap(),
|
||||||
explorer_folder_open: QColor::try_from("#FFF").unwrap(),
|
explorer_folder_open: QColor::try_from("#2b2b2b").unwrap(),
|
||||||
|
terminal_background: QColor::try_from("#111111").unwrap(),
|
||||||
|
info_log: QColor::try_from("#C4FFFF").unwrap(),
|
||||||
|
debug_log: QColor::try_from("#55ff99").unwrap(),
|
||||||
|
warn_log: QColor::try_from("#ffaa00").unwrap(),
|
||||||
|
error_log: QColor::try_from("#ff5555").unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,20 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
|
use cxx_qt_lib::{QModelIndex, QString};
|
||||||
|
use dirs;
|
||||||
|
use log::warn;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
use syntect::easy::HighlightLines;
|
||||||
|
use syntect::highlighting::ThemeSet;
|
||||||
|
use syntect::html::{
|
||||||
|
IncludeBackground, append_highlighted_html_for_styled_line, start_highlighted_html_snippet,
|
||||||
|
};
|
||||||
|
use syntect::parsing::SyntaxSet;
|
||||||
|
use syntect::util::LinesWithEndings;
|
||||||
|
|
||||||
#[cxx_qt::bridge]
|
#[cxx_qt::bridge]
|
||||||
pub mod qobject {
|
pub mod qobject {
|
||||||
unsafe extern "C++" {
|
unsafe extern "C++" {
|
||||||
@ -17,7 +34,6 @@ pub mod qobject {
|
|||||||
#[qml_element]
|
#[qml_element]
|
||||||
#[qml_singleton]
|
#[qml_singleton]
|
||||||
#[qproperty(QString, file_path, cxx_name = "filePath")]
|
#[qproperty(QString, file_path, cxx_name = "filePath")]
|
||||||
#[qproperty(QModelIndex, root_index, cxx_name = "rootIndex")]
|
|
||||||
type FileSystem = super::FileSystemImpl;
|
type FileSystem = super::FileSystemImpl;
|
||||||
|
|
||||||
#[inherit]
|
#[inherit]
|
||||||
@ -39,22 +55,9 @@ pub mod qobject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use cxx_qt_lib::{QModelIndex, QString};
|
// TODO: Implement a provider for QFileSystemModel::setIconProvider for icons.
|
||||||
use dirs;
|
|
||||||
use log::warn;
|
|
||||||
use std::fs;
|
|
||||||
use std::io::BufRead;
|
|
||||||
use syntect::easy::HighlightFile;
|
|
||||||
use syntect::highlighting::ThemeSet;
|
|
||||||
use syntect::html::{
|
|
||||||
IncludeBackground, append_highlighted_html_for_styled_line, start_highlighted_html_snippet,
|
|
||||||
};
|
|
||||||
use syntect::parsing::SyntaxSet;
|
|
||||||
|
|
||||||
// TODO: Impleent a provider for QFileSystemModel::setIconProvider for icons.
|
|
||||||
pub struct FileSystemImpl {
|
pub struct FileSystemImpl {
|
||||||
file_path: QString,
|
file_path: QString,
|
||||||
root_index: QModelIndex,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default is explicit to make the editor open this source file initially.
|
// Default is explicit to make the editor open this source file initially.
|
||||||
@ -62,7 +65,6 @@ impl Default for FileSystemImpl {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_path: QString::from(file!()),
|
file_path: QString::from(file!()),
|
||||||
root_index: Default::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,42 +74,46 @@ impl qobject::FileSystem {
|
|||||||
if path.is_empty() {
|
if path.is_empty() {
|
||||||
return QString::default();
|
return QString::default();
|
||||||
}
|
}
|
||||||
if !fs::metadata(path.to_string())
|
let meta = fs::metadata(path.to_string())
|
||||||
.expect(format!("Failed to get file metadata {path:?}").as_str())
|
.expect(format!("Failed to get file metadata {path:?}").as_str());
|
||||||
.is_file()
|
if !meta.is_file() {
|
||||||
{
|
|
||||||
warn!(target:"FileSystem", "Attempted to open file {path:?} that is not a valid file");
|
warn!(target:"FileSystem", "Attempted to open file {path:?} that is not a valid file");
|
||||||
return QString::default();
|
return QString::default();
|
||||||
}
|
}
|
||||||
let ss = SyntaxSet::load_defaults_nonewlines();
|
let path_str = path.to_string();
|
||||||
let ts = ThemeSet::load_defaults();
|
if let Ok(lines) = fs::read_to_string(path_str.as_str()) {
|
||||||
let theme = &ts.themes["base16-ocean.dark"];
|
let ss = SyntaxSet::load_defaults_nonewlines();
|
||||||
|
let ts = ThemeSet::load_defaults();
|
||||||
|
let theme = &ts.themes["base16-ocean.dark"];
|
||||||
|
let lang = ss
|
||||||
|
.find_syntax_by_extension(
|
||||||
|
Path::new(path_str.as_str())
|
||||||
|
.extension()
|
||||||
|
.map(|s| s.to_str())
|
||||||
|
.unwrap_or_else(|| Some("md"))
|
||||||
|
.expect("Failed to get file extension"),
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|| ss.find_syntax_plain_text());
|
||||||
|
let mut highlighter = HighlightLines::new(lang, theme);
|
||||||
|
let (mut output, _bg) = start_highlighted_html_snippet(theme);
|
||||||
|
for line in LinesWithEndings::from(lines.as_str()) {
|
||||||
|
let regions = highlighter
|
||||||
|
.highlight_line(line, &ss)
|
||||||
|
.expect("Failed to highlight");
|
||||||
|
|
||||||
let mut highlighter =
|
append_highlighted_html_for_styled_line(
|
||||||
HighlightFile::new(path.to_string(), &ss, theme).expect("Failed to create highlighter");
|
®ions[..],
|
||||||
let (mut output, _bg) = start_highlighted_html_snippet(theme);
|
IncludeBackground::Yes,
|
||||||
let mut line = String::new();
|
&mut output,
|
||||||
while highlighter
|
)
|
||||||
.reader
|
.expect("Failed to insert highlighted html");
|
||||||
.read_line(&mut line)
|
}
|
||||||
.expect("Failed to read file.")
|
|
||||||
> 0
|
|
||||||
{
|
|
||||||
let regions = highlighter
|
|
||||||
.highlight_lines
|
|
||||||
.highlight_line(&line, &ss)
|
|
||||||
.expect("Failed to highlight");
|
|
||||||
|
|
||||||
append_highlighted_html_for_styled_line(
|
output.push_str("</pre>\n");
|
||||||
®ions[..],
|
QString::from(output)
|
||||||
IncludeBackground::Yes,
|
} else {
|
||||||
&mut output,
|
return QString::default();
|
||||||
)
|
|
||||||
.expect("Failed to insert highlighted html");
|
|
||||||
line.clear();
|
|
||||||
}
|
}
|
||||||
output.push_str("</pre>\n");
|
|
||||||
QString::from(output)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// There will never be more than one column.
|
// There will never be more than one column.
|
||||||
@ -124,14 +130,13 @@ impl qobject::FileSystem {
|
|||||||
self.set_root_path(path)
|
self.set_root_path(path)
|
||||||
} else {
|
} else {
|
||||||
// If the initial directory can't be opened, attempt to find the home directory.
|
// If the initial directory can't be opened, attempt to find the home directory.
|
||||||
self.set_root_path(&QString::from(
|
let homedir = dirs::home_dir()
|
||||||
dirs::home_dir()
|
.expect("Failed to get home directory")
|
||||||
.expect("Failed to get home directory")
|
.as_path()
|
||||||
.as_path()
|
.to_str()
|
||||||
.to_str()
|
.unwrap()
|
||||||
.unwrap()
|
.to_string();
|
||||||
.to_string(),
|
self.set_root_path(&QString::from(homedir))
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
use anyhow::{Context, Result, anyhow};
|
use anyhow::{Context, Result, anyhow};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::{info, trace};
|
use log::{info, trace};
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
mod about;
|
mod about;
|
||||||
mod app;
|
mod app;
|
||||||
mod component;
|
mod component;
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
||||||
use ratatui::text::{Line, Span};
|
use ratatui::text::{Line, Span};
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
use crate::tui::about::About;
|
use crate::tui::about::About;
|
||||||
use crate::tui::app::AppComponent::{AppEditor, AppExplorer, AppLogger};
|
use crate::tui::app::AppComponent::{AppEditor, AppExplorer, AppLogger};
|
||||||
use crate::tui::component::{Action, Component, Focus, FocusState, Visibility, VisibleState};
|
use crate::tui::component::{Action, Component, Focus, FocusState, Visibility, VisibleState};
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
#![allow(dead_code, unused_variables)]
|
#![allow(dead_code, unused_variables)]
|
||||||
|
|
||||||
use crate::tui::component::Focus::Inactive;
|
use crate::tui::component::Focus::Inactive;
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState};
|
use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState};
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use edtui::{
|
use edtui::{
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
use crate::tui::component::{Action, Component, Focus, FocusState};
|
use crate::tui::component::{Action, Component, Focus, FocusState};
|
||||||
use crate::tui::editor::Editor;
|
use crate::tui::editor::Editor;
|
||||||
use anyhow::{Context, Result, anyhow};
|
use anyhow::{Context, Result, anyhow};
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState};
|
use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState};
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState};
|
use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState};
|
||||||
use log::{LevelFilter, trace};
|
use log::{LevelFilter, trace};
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026, Shaun Reed <shaunrd0@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GNU General Public License v3.0 or later
|
||||||
|
|
||||||
use crate::tui::component::{Action, Component, ComponentState, FocusState};
|
use crate::tui::component::{Action, Component, ComponentState, FocusState};
|
||||||
use crate::tui::menu_bar::MenuBarItemOption::{
|
use crate::tui::menu_bar::MenuBarItemOption::{
|
||||||
About, CloseTab, Exit, Reload, Save, ShowHideExplorer, ShowHideLogger,
|
About, CloseTab, Exit, Reload, Save, ShowHideExplorer, ShowHideLogger,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user