Add LineCount module.
This commit is contained in:
parent
a6d2fb9e31
commit
13a405a801
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
/target
|
**/target/**
|
||||||
.qtcreator
|
**/.qtcreator/**
|
||||||
.idea
|
**/.idea/**
|
||||||
|
**/*.autosave/**
|
||||||
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -62,6 +62,7 @@ dependencies = [
|
|||||||
"cxx-qt",
|
"cxx-qt",
|
||||||
"cxx-qt-build",
|
"cxx-qt-build",
|
||||||
"cxx-qt-lib",
|
"cxx-qt-lib",
|
||||||
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -266,6 +267,12 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
|
|||||||
@ -7,6 +7,7 @@ edition = "2024"
|
|||||||
cxx = "1.0.95"
|
cxx = "1.0.95"
|
||||||
cxx-qt = "0.7"
|
cxx-qt = "0.7"
|
||||||
cxx-qt-lib = { version="0.7", features = ["qt_full"] }
|
cxx-qt-lib = { version="0.7", features = ["qt_full"] }
|
||||||
|
log = { version = "0.4.27", features = [] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
# The link_qt_object_files feature is required for statically linking Qt 6.
|
# The link_qt_object_files feature is required for statically linking Qt 6.
|
||||||
|
|||||||
2
build.rs
2
build.rs
@ -9,7 +9,7 @@ fn main() {
|
|||||||
// - Qt Qml requires linking Qt Network on macOS
|
// - Qt Qml requires linking Qt Network on macOS
|
||||||
.qt_module("Network")
|
.qt_module("Network")
|
||||||
.qml_module(QmlModule {
|
.qml_module(QmlModule {
|
||||||
uri: "test",
|
uri: "clide.module",
|
||||||
rust_files: &["src/main.rs"],
|
rust_files: &["src/main.rs"],
|
||||||
qml_files: &["qml/main.qml",
|
qml_files: &["qml/main.qml",
|
||||||
"qml/Menu/ClideMenu.qml",
|
"qml/Menu/ClideMenu.qml",
|
||||||
|
|||||||
140
qml/Menu/ClideMenuBar.qml.autosave
Normal file
140
qml/Menu/ClideMenuBar.qml.autosave
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
qml/main.qml
123
qml/main.qml
@ -5,6 +5,8 @@ import QtQuick.Dialogs
|
|||||||
|
|
||||||
import "Menu"
|
import "Menu"
|
||||||
|
|
||||||
|
import clide.module 1.0
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: appWindow
|
id: appWindow
|
||||||
|
|
||||||
@ -51,7 +53,7 @@ ApplicationWindow {
|
|||||||
id: navigationView
|
id: navigationView
|
||||||
|
|
||||||
SplitView.fillHeight: true
|
SplitView.fillHeight: true
|
||||||
SplitView.preferredWidth: 250
|
SplitView.preferredWidth: 200
|
||||||
color: "#303234"
|
color: "#303234"
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
@ -59,9 +61,9 @@ ApplicationWindow {
|
|||||||
|
|
||||||
// Shows the help text.
|
// Shows the help text.
|
||||||
TextArea {
|
TextArea {
|
||||||
readOnly: true
|
|
||||||
placeholderText: qsTr("File system view placeholder")
|
placeholderText: qsTr("File system view placeholder")
|
||||||
placeholderTextColor: "white"
|
placeholderTextColor: "white"
|
||||||
|
readOnly: true
|
||||||
wrapMode: TextArea.Wrap
|
wrapMode: TextArea.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +94,79 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextArea {
|
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
|
id: areaText
|
||||||
|
|
||||||
color: "#ccced3"
|
color: "#ccced3"
|
||||||
@ -106,7 +180,6 @@ ApplicationWindow {
|
|||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: "#2b2b2b"
|
color: "#2b2b2b"
|
||||||
implicitHeight: 650
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onLinkActivated: function (link) {
|
onLinkActivated: function (link) {
|
||||||
@ -135,12 +208,20 @@ ApplicationWindow {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FontMetrics {
|
||||||
|
id: fontMetrics
|
||||||
|
|
||||||
|
font: areaText.font
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
TextArea {
|
TextArea {
|
||||||
id: areaConsole
|
id: areaConsole
|
||||||
|
|
||||||
|
height: 100
|
||||||
placeholderText: qsTr("Placeholder for bash terminal.")
|
placeholderText: qsTr("Placeholder for bash terminal.")
|
||||||
placeholderTextColor: "white"
|
placeholderTextColor: "white"
|
||||||
height: 100
|
|
||||||
readOnly: true
|
readOnly: true
|
||||||
wrapMode: TextArea.Wrap
|
wrapMode: TextArea.Wrap
|
||||||
|
|
||||||
@ -153,4 +234,36 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
136
src/main.rs
136
src/main.rs
@ -1,77 +1,103 @@
|
|||||||
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
// TODO: Header
|
||||||
// SPDX-FileContributor: Leon Matthes <leon.matthes@kdab.com>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
||||||
|
|
||||||
#[cxx_qt::bridge]
|
#[cxx_qt::bridge]
|
||||||
mod qobject {
|
pub mod qobject {
|
||||||
unsafe extern "C++" {
|
unsafe extern "C++" {
|
||||||
include!("cxx-qt-lib/qstring.h");
|
include!("cxx-qt-lib/qvariant.h");
|
||||||
type QString = cxx_qt_lib::QString;
|
type QVariant = cxx_qt_lib::QVariant;
|
||||||
}
|
include!(<QtCore/QAbstractListModel>);
|
||||||
|
type QModelIndex = cxx_qt_lib::QModelIndex;
|
||||||
#[qenum(Greeter)]
|
type QAbstractListModel;
|
||||||
pub enum Language {
|
|
||||||
English,
|
|
||||||
German,
|
|
||||||
French,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[qenum(Greeter)]
|
|
||||||
pub enum Greeting {
|
|
||||||
Hello,
|
|
||||||
Bye,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "RustQt" {
|
unsafe extern "RustQt" {
|
||||||
#[qobject]
|
#[qobject]
|
||||||
|
#[base = QAbstractListModel]
|
||||||
|
type AbstractListModel = super::AbstractListModelRust;
|
||||||
|
|
||||||
|
#[qobject]
|
||||||
|
#[base = AbstractListModel]
|
||||||
#[qml_element]
|
#[qml_element]
|
||||||
#[qproperty(Greeting, greeting)]
|
#[qproperty(i32, count)]
|
||||||
#[qproperty(Language, language)]
|
type LineCount = super::LineCountRust;
|
||||||
type Greeter = super::GreeterRust;
|
|
||||||
|
#[cxx_name = "beginInsertRows"]
|
||||||
|
#[inherit]
|
||||||
|
fn beginInsertRows(self: Pin<&mut LineCount>, parent: &QModelIndex, first: i32, last: i32);
|
||||||
|
|
||||||
|
#[cxx_name = "endInsertRows"]
|
||||||
|
#[inherit]
|
||||||
|
fn endInsertRows(self: Pin<&mut LineCount>);
|
||||||
|
|
||||||
|
#[cxx_name = "beginRemoveRows"]
|
||||||
|
#[inherit]
|
||||||
|
fn beginRemoveRows(self: Pin<&mut LineCount>, parent: &QModelIndex, first: i32, last: i32);
|
||||||
|
|
||||||
|
#[cxx_name = "endRemoveRows"]
|
||||||
|
#[inherit]
|
||||||
|
fn endRemoveRows(self: Pin<&mut LineCount>);
|
||||||
|
|
||||||
#[qinvokable]
|
#[qinvokable]
|
||||||
fn greet(self: &Greeter) -> QString;
|
pub fn set_line_count(self: Pin<&mut LineCount>, line_count: i32);
|
||||||
|
|
||||||
|
#[qinvokable]
|
||||||
|
#[cxx_override]
|
||||||
|
fn data(self: &LineCount, index: &QModelIndex, role: i32) -> QVariant;
|
||||||
|
|
||||||
|
#[qinvokable]
|
||||||
|
#[cxx_override]
|
||||||
|
#[cxx_name = "rowCount"]
|
||||||
|
fn row_count(self: &LineCount, _parent: &QModelIndex) -> i32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use qobject::*;
|
use cxx_qt::CxxQtType;
|
||||||
|
use cxx_qt_lib::{QModelIndex, QVariant};
|
||||||
|
|
||||||
impl Greeting {
|
impl qobject::LineCount {
|
||||||
fn translate(&self, language: Language) -> String {
|
pub fn set_line_count(mut self: std::pin::Pin<&mut Self>, line_count: i32) {
|
||||||
match (self, language) {
|
let current_count = self.as_mut().rust_mut().count;
|
||||||
(&Greeting::Hello, Language::English) => "Hello, World!",
|
if line_count < 0 || current_count == line_count {
|
||||||
(&Greeting::Hello, Language::German) => "Hallo, Welt!",
|
log::warn!(
|
||||||
(&Greeting::Hello, Language::French) => "Bonjour, le monde!",
|
"Can't set line count: {}; Current count: {}",
|
||||||
(&Greeting::Bye, Language::English) => "Bye!",
|
line_count,
|
||||||
(&Greeting::Bye, Language::German) => "Auf Wiedersehen!",
|
current_count
|
||||||
(&Greeting::Bye, Language::French) => "Au revoir!",
|
);
|
||||||
_ => "🤯",
|
return;
|
||||||
}
|
}
|
||||||
.to_string()
|
if current_count < line_count {
|
||||||
|
self.as_mut()
|
||||||
|
.beginInsertRows(&QModelIndex::default(), current_count, line_count - 1);
|
||||||
|
self.as_mut().endInsertRows();
|
||||||
|
} else if current_count > line_count {
|
||||||
|
self.as_mut()
|
||||||
|
.beginRemoveRows(&QModelIndex::default(), line_count, current_count - 1);
|
||||||
|
self.as_mut().endRemoveRows();
|
||||||
|
}
|
||||||
|
self.as_mut().rust_mut().count = line_count;
|
||||||
|
log::warn!(
|
||||||
|
"Line count changed from {} to {}",
|
||||||
|
current_count,
|
||||||
|
line_count
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn row_count(self: &Self, _parent: &QModelIndex) -> i32 {
|
||||||
|
*self.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(self: &Self, _index: &QModelIndex, _role: i32) -> QVariant {
|
||||||
|
QVariant::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GreeterRust {
|
/// A struct which inherits from QAbstractListModel
|
||||||
greeting: Greeting,
|
#[derive(Default)]
|
||||||
language: Language,
|
pub struct AbstractListModelRust {}
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GreeterRust {
|
#[derive(Default)]
|
||||||
fn default() -> Self {
|
pub struct LineCountRust {
|
||||||
Self {
|
pub count: i32,
|
||||||
greeting: Greeting::Hello,
|
|
||||||
language: Language::English,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use cxx_qt_lib::QString;
|
|
||||||
|
|
||||||
impl qobject::Greeter {
|
|
||||||
fn greet(&self) -> QString {
|
|
||||||
QString::from(self.greeting.translate(self.language))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user