TUI #1

Merged
shaunrd0 merged 73 commits from ui into master 2026-01-25 20:57:37 +00:00
11 changed files with 145 additions and 197 deletions
Showing only changes of commit d2f5823594 - Show all commits

View File

@ -10,7 +10,8 @@ fn main() {
.qt_module("Network")
.qml_module(QmlModule {
uri: "clide.module",
rust_files: &["src/line_count.rs"],
rust_files: &["src/line_count.rs",
"src/colors.rs"],
qml_files: &["qml/main.qml",
"qml/ProjectView/ClideProjectView.qml",
"qml/Editor/ClideEditor.qml",

View File

@ -11,14 +11,15 @@ SplitView {
// Customized handle to drag between the Editor and the Console.
handle: Rectangle {
border.color: SplitHandle.hovered ? "#2b2b2b" : "#3c3f41"
color: SplitHandle.pressed ? "#4b4f51" : "#3c3f41"
border.color: SplitHandle.pressed ? RustColors.pressed : SplitHandle.hovered ? RustColors.hovered : RustColors.gutter
color: SplitHandle.pressed ? RustColors.pressed : SplitHandle.hovered ? RustColors.hovered : RustColors.gutter
implicitHeight: 8
opacity: SplitHandle.hovered || areaConsole.height < 15 ? 1.0 : 0.0
radius: 2.5
Behavior on opacity {
OpacityAnimator {
duration: 700
// Execute these behaviors when the color is changed.
Behavior on color {
ColorAnimation {
duration: 400
}
}
}
@ -33,7 +34,7 @@ SplitView {
Layout.fillWidth: false
// Calculate the width based on the logarithmic scale.
Layout.preferredWidth: fontMetrics.averageCharacterWidth * (Math.floor(Math.log10(areaText.lineCount)) + 1) + 10
Layout.preferredWidth: fontMetrics.averageCharacterWidth * (Math.floor(Math.log10(textArea.lineCount)) + 1) + 10
contentY: editorFlickable.contentY
interactive: false
visible: true
@ -49,14 +50,15 @@ SplitView {
delegate: Item {
required property int index
height: 17
// Calculate the height of each line in the text area.
height: textArea.contentHeight / textArea.lineCount
width: parent.width
Label {
id: numbers
color: "white"
font: areaText.font
font: textArea.font
height: parent.height
horizontalAlignment: Text.AlignLeft
text: parent.index + 1
@ -67,20 +69,19 @@ SplitView {
id: indicator
anchors.left: numbers.right
color: Qt.darker("#FFF", 3)
color: RustColors.linenumber
height: parent.height
width: 1
}
}
model: areaText.lineCount
// TODO: Bug where text wrapping shows as new line number.
model: textArea.lineCount
}
}
}
Flickable {
id: editorFlickable
property alias areaText: areaText
Layout.fillHeight: true
Layout.fillWidth: true
boundsBehavior: Flickable.StopAtBounds
@ -91,20 +92,19 @@ SplitView {
ScrollBar.vertical: MyScrollBar {
}
TextArea.flickable: TextArea {
id: areaText
id: textArea
color: "#ccced3"
color: RustColors.editor_text
focus: true
persistentSelection: true
selectByMouse: true
// selectedTextColor: control.palette.highlightedText
// selectionColor: control.palette.highlight
// selectedTextColor: RustColors.editor_highlighted_text
// selectionColor: RustColors.editor_highlight
textFormat: Qt.AutoText
wrapMode: TextArea.Wrap
background: Rectangle {
color: "#2b2b2b"
color: RustColors.editor_background
}
onLinkActivated: function (link) {
@ -137,7 +137,7 @@ SplitView {
FontMetrics {
id: fontMetrics
font: areaText.font
font: textArea.font
}
}
}
@ -150,9 +150,9 @@ SplitView {
readOnly: true
wrapMode: TextArea.Wrap
background: Rectangle {
color: "#2b2b2b"
color: RustColors.editor_background
implicitHeight: 100
// border.color: control.enabled ? "#21be2b" : "transparent"
// border.color: control.enabled ? RustColors.active : RustColors.inactive
}
}
@ -163,11 +163,12 @@ SplitView {
// 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
color: RustColors.scrollbar_gutter
// Fade the scrollbar gutter when inactive.
opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.2
Behavior on opacity {
OpacityAnimator {
duration: 500
@ -177,16 +178,25 @@ SplitView {
// 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 {
// If we don't need a scrollbar, fallback to the gutter color.
// If the scrollbar is required change it's color based on activity.
color: scrollBar.size < 1.0 ? scrollBar.active ? RustColors.scrollbar_active : RustColors.scrollbar : RustColors.scrollbar_gutter
// Smooth transition between color changes based on the state above.
Behavior on color {
ColorAnimation {
duration: 1000
}
}
// Fade the scrollbar when inactive.
opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.35
Behavior on opacity {
OpacityAnimator {
duration: 500
}
}
}
}
}

View File

@ -1,9 +1,11 @@
import QtQuick
import QtQuick.Controls
import clide.module 1.0
Menu {
background: Rectangle {
color: "#3c3f41"
color: RustColors.menubar
implicitWidth: 100
radius: 2
}

View File

@ -1,10 +1,12 @@
import QtQuick
import QtQuick.Controls
import clide.module 1.0
MenuBar {
background: Rectangle {
color: "#3c3f41"
border.color: "#575757"
color: RustColors.menubar
border.color: RustColors.menubar_border
}
Action {
@ -44,7 +46,7 @@ MenuBar {
MenuSeparator {
background: Rectangle {
border.color: color
color: "#3c3f41"
color: RustColors.menubar_border
implicitHeight: 3
implicitWidth: 200
}

View File

@ -1,140 +0,0 @@
import QtQuick
import QtQuick.Controls
MenuBar {
background: Rectangle {
color: "#3c3f41"
border.color: "#575757"
}
Action {
id: actionNewProject
text: qsTr("&New Project...")
}
Action {
id: actionOpen
text: qsTr("&Open...")
}
Action {
id: actionSave
text: qsTr("&Save")
}
Action {
id: actionExit
text: qsTr("&Exit")
onTriggered: Qt.quit()
}
ClideMenu {
title: qsTr("&File")
ClideMenuBarItem {
action: actionNewProject
}
ClideMenuBarItem {
action: actionOpen
}
ClideMenuBarItem {
action: actionSave
}
MenuSeparator {
background: Rectangle {
border.color: color
color: "#3c3f41"
implicitHeight: 3
implicitWidth: 200
}
}
ClideMenuBarItem {
action: actionExit
}
}
Action {
id: actionUndo
text: qsTr("&Undo")
}
Action {
id: actionRedo
text: qsTr("&Redo")
}
Action {
id: actionCut
text: qsTr("&Cut")
}
Action {
id: actionCopy
text: qsTr("&Copy")
}
Action {
id: actionPaste
text: qsTr("&Paste")
}
ClideMenu {
title: qsTr("&Edit")
ClideMenuBarItem {
action: actionUndo
}
ClideMenuBarItem {
action: actionRedo
}
ClideMenuBarItem {
action: actionCut
}
ClideMenuBarItem {
action: actionCopy
}
ClideMenuBarItem {
action: actionPaste
}
}
Action {
id: actionToolWindows
text: qsTr("&Tool Windows")
}
Action {
id: actionAppearance
text: qsTr("&Appearance")
}
ClideMenu {
title: qsTr("&View")
ClideMenuBarItem {
action: actionToolWindows
}
ClideMenuBarItem {
action: actionAppearance
}
}
Action {
id: actionDocumentation
text: qsTr("&Documentation")
}
Action {
id: actionAbout
text: qsTr("&About")
}
ClideMenu {
title: qsTr("&Help")
ClideMenuBarItem {
action: actionDocumentation
}
ClideMenuBarItem {
action: actionAbout
}
}
}

View File

@ -1,11 +1,13 @@
import QtQuick
import QtQuick.Controls
import clide.module 1.0
MenuItem {
id: root
background: Rectangle {
color: root.hovered ? "#4b4f51" : "#3c3f41" // Hover effect
color: root.hovered ? RustColors.hovered : RustColors.unhovered
radius: 2.5
}
contentItem: IconLabel {

View File

@ -1,16 +0,0 @@
import QtQuick
import QtQuick.Controls
MenuItem {
id: root
background: Rectangle {
color: root.hovered ? "#4b4f51" : "#3c3f41" // Hover effect
radius: 2.5
}
contentItem: IconLabel {
color: "black"
font.family: "Helvetica"
text: root.text
}
}

View File

@ -4,6 +4,8 @@ import QtQuick.Layouts
import "../Editor"
import clide.module 1.0
SplitView {
Layout.fillHeight: true
Layout.fillWidth: true
@ -11,14 +13,15 @@ SplitView {
// Customized handle to drag between the Navigation and the Editor.
handle: Rectangle {
border.color: SplitHandle.hovered ? "#303234" : "#3c3f41"
color: SplitHandle.pressed ? "#4b4f51" : "#3c3f41"
border.color: SplitHandle.pressed ? RustColors.pressed : SplitHandle.hovered ? RustColors.hovered : RustColors.gutter
color: SplitHandle.pressed ? RustColors.pressed : SplitHandle.hovered ? RustColors.hovered : RustColors.gutter
implicitWidth: 8
opacity: SplitHandle.hovered || navigationView.width < 15 ? 1.0 : 0.0
radius: 2.5
Behavior on opacity {
OpacityAnimator {
duration: 700
// Execute these behaviors when the color is changed.
Behavior on color {
ColorAnimation {
duration: 400
}
}
}
@ -28,7 +31,7 @@ SplitView {
SplitView.fillHeight: true
SplitView.preferredWidth: 200
color: "#303234"
color: RustColors.explorer_background
StackLayout {
anchors.fill: parent

View File

@ -21,8 +21,9 @@ ApplicationWindow {
Rectangle {
anchors.fill: parent
color: "#1e1f22"
color: RustColors.gutter
}
MessageDialog {
id: errorDialog

77
src/colors.rs Normal file
View File

@ -0,0 +1,77 @@
#[cxx_qt::bridge]
pub mod qobject {
unsafe extern "C++" {
include!("cxx-qt-lib/qcolor.h");
type QColor = cxx_qt_lib::QColor;
}
unsafe extern "RustQt" {
#[qobject]
#[qml_element]
#[qml_singleton]
#[qproperty(QColor, hovered)]
#[qproperty(QColor, unhovered)]
#[qproperty(QColor, pressed)]
#[qproperty(QColor, menubar)]
#[qproperty(QColor, menubar_border)]
#[qproperty(QColor, scrollbar)]
#[qproperty(QColor, scrollbar_active)]
#[qproperty(QColor, scrollbar_gutter)]
#[qproperty(QColor, linenumber)]
#[qproperty(QColor, active)]
#[qproperty(QColor, inactive)]
#[qproperty(QColor, editor_background)]
#[qproperty(QColor, editor_text)]
#[qproperty(QColor, editor_highlighted_text)]
#[qproperty(QColor, editor_highlight)]
#[qproperty(QColor, gutter)]
#[qproperty(QColor, explorer_background)]
type RustColors = super::RustColorsImpl;
}
}
use cxx_qt_lib::QColor;
pub struct RustColorsImpl {
hovered: QColor,
unhovered: QColor,
pressed: QColor,
menubar: QColor,
menubar_border: QColor,
scrollbar: QColor,
scrollbar_active: QColor,
scrollbar_gutter: QColor,
linenumber: QColor,
active: QColor,
inactive: QColor,
editor_background: QColor,
editor_text: QColor,
editor_highlighted_text: QColor,
editor_highlight: QColor,
gutter: QColor,
explorer_background: QColor,
}
impl Default for RustColorsImpl {
fn default() -> Self {
Self {
hovered: QColor::try_from("#303234").unwrap(),
unhovered: QColor::try_from("#3c3f41").unwrap(),
pressed: QColor::try_from("#4b4f51").unwrap(),
menubar: QColor::try_from("#3c3f41").unwrap(),
menubar_border: QColor::try_from("#575757").unwrap(),
scrollbar: QColor::try_from("#4b4f51").unwrap(),
scrollbar_active: QColor::try_from("#4b4f51").unwrap(),
scrollbar_gutter: QColor::try_from("#3b3b3b").unwrap(),
linenumber: QColor::try_from("#FFF").unwrap(),
active: QColor::try_from("#a9acb0").unwrap(),
inactive: QColor::try_from("#FFF").unwrap(),
editor_background: QColor::try_from("#2b2b2b").unwrap(),
editor_text: QColor::try_from("#ccced3").unwrap(),
editor_highlighted_text: QColor::try_from("#ccced3").unwrap(),
editor_highlight: QColor::try_from("#ccced3").unwrap(),
gutter: QColor::try_from("#1e1f22").unwrap(),
explorer_background: QColor::try_from("#3c3f41").unwrap(),
}
}
}

View File

@ -1,6 +1,9 @@
// TODO: Header
use cxx_qt_lib::QString;
pub mod line_count;
pub mod colors;
fn main() {
use cxx_qt_lib::{QGuiApplication, QQmlApplicationEngine, QUrl};
@ -8,6 +11,9 @@ fn main() {
let mut app = QGuiApplication::new();
let mut engine = QQmlApplicationEngine::new();
if let Some(engine) = engine.as_mut() {
engine.add_import_path(&QString::from("qml/"));
}
if let Some(engine) = engine.as_mut() {
engine.load(&QUrl::from("qml/main.qml"));
}