diff --git a/build.rs b/build.rs index 5328c17..e44b48b 100644 --- a/build.rs +++ b/build.rs @@ -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", diff --git a/qml/Editor/ClideEditor.qml b/qml/Editor/ClideEditor.qml index 853df35..26eea2e 100644 --- a/qml/Editor/ClideEditor.qml +++ b/qml/Editor/ClideEditor.qml @@ -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,14 +178,23 @@ 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 + // 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: 1000 + duration: 500 } } } diff --git a/qml/Menu/ClideMenu.qml b/qml/Menu/ClideMenu.qml index 8b89d96..c5e5a1e 100644 --- a/qml/Menu/ClideMenu.qml +++ b/qml/Menu/ClideMenu.qml @@ -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 } diff --git a/qml/Menu/ClideMenuBar.qml b/qml/Menu/ClideMenuBar.qml index f252803..a165f2d 100644 --- a/qml/Menu/ClideMenuBar.qml +++ b/qml/Menu/ClideMenuBar.qml @@ -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 } diff --git a/qml/Menu/ClideMenuBar.qml.autosave b/qml/Menu/ClideMenuBar.qml.autosave deleted file mode 100644 index f252803..0000000 --- a/qml/Menu/ClideMenuBar.qml.autosave +++ /dev/null @@ -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 - } - } -} diff --git a/qml/Menu/ClideMenuBarItem.qml b/qml/Menu/ClideMenuBarItem.qml index 15f049f..899b22d 100644 --- a/qml/Menu/ClideMenuBarItem.qml +++ b/qml/Menu/ClideMenuBarItem.qml @@ -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 { diff --git a/qml/Menu/ClideMenuBarItem.qml.autosave b/qml/Menu/ClideMenuBarItem.qml.autosave deleted file mode 100644 index 15f049f..0000000 --- a/qml/Menu/ClideMenuBarItem.qml.autosave +++ /dev/null @@ -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 - } -} diff --git a/qml/ProjectView/ClideProjectView.qml b/qml/ProjectView/ClideProjectView.qml index c450f2c..a1aea84 100644 --- a/qml/ProjectView/ClideProjectView.qml +++ b/qml/ProjectView/ClideProjectView.qml @@ -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 diff --git a/qml/main.qml b/qml/main.qml index 2632981..d031490 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -21,8 +21,9 @@ ApplicationWindow { Rectangle { anchors.fill: parent - color: "#1e1f22" + color: RustColors.gutter } + MessageDialog { id: errorDialog diff --git a/src/colors.rs b/src/colors.rs new file mode 100644 index 0000000..1b699b5 --- /dev/null +++ b/src/colors.rs @@ -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(), + } + } +} diff --git a/src/main.rs b/src/main.rs index cdbdc57..505c4cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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")); }