Compare commits

...

31 Commits

Author SHA1 Message Date
5ba880bc7e WIP 2025-04-19 12:46:22 -04:00
6a4957588d Pass root path to GUI process. 2025-04-13 13:47:12 -04:00
f4242f7749 Factor out TUI code. 2025-04-13 12:17:11 -04:00
fd3c8fb204 Factor out GUI code. 2025-04-13 12:15:31 -04:00
fd9d47f0c0 Clean up. 2025-04-13 12:09:18 -04:00
d53ef9aa1b Launch clide in separate process by default.
Improve CLI to support tui and gui modes.
Also supports attaching the GUI to the current terminal via -g
2025-04-13 11:58:28 -04:00
a29ae43e84 Use CWD if no directory is provided. 2025-04-13 11:01:37 -04:00
41a9a2a3bf Use structopt. 2025-04-13 10:21:02 -04:00
7bf6c3299c Add basic CLI for launching head(less) mode. 2025-04-13 09:35:55 -04:00
90c10d2a16 Debug missing console placeholder text.
It appears on resizing horizontally
2025-04-13 08:31:41 -04:00
2dcf0529d1 Custom highlighting to fix UI bugs.
+ Selecting text caused blurry editor view.
+ Now prefers syntect theme background color over QML background color.
2025-04-12 13:33:39 -04:00
f740ff347b Fix file loading. 2025-03-31 22:56:57 -04:00
9b86553513 Add syntax highlighting with syntect. 2025-03-31 22:32:17 -04:00
365940267f Add some checks before reading file. 2025-03-31 19:26:04 -04:00
b426b88b79 Fix colors. 2025-03-30 21:58:38 -04:00
bdf942371c Add basic FileSystem view. 2025-03-30 21:38:57 -04:00
b62dce631f Add ClideTreeView. 2025-03-30 16:14:58 -04:00
1546eb1028 Add FileSystem Rust module. 2025-03-30 13:50:23 -04:00
4f3aebe64f Add basic stub for filesystem. 2025-03-30 13:14:58 -04:00
413500dad3 Add an about window. 2025-03-30 12:52:44 -04:00
b0064c2f69 Update README. 2025-03-30 12:00:27 -04:00
0f055603a2 Remove unused LineCount. 2025-03-30 11:51:23 -04:00
094ac92fe4 Reorganize qml files. 2025-03-30 11:49:00 -04:00
3b8e407632 Consolidate source files. 2025-03-30 11:47:16 -04:00
d2f5823594 Add RustColors singleton helper. 2025-03-30 11:20:21 -04:00
be9981291c Speed up animation for dock handles. 2025-03-29 20:20:28 -04:00
500a329dea Separate LineCount from main. 2025-03-29 20:14:42 -04:00
70e9f79c8a Separate ProjectView and Editor. 2025-03-29 18:00:16 -04:00
13a405a801 Add LineCount module. 2025-03-29 16:55:26 -04:00
a6d2fb9e31 Change colors. 2025-03-29 10:26:39 -04:00
8b71af06a8 Add placeholders for side panel and console. 2025-03-29 09:55:09 -04:00
22 changed files with 1605 additions and 159 deletions

7
.gitignore vendored
View File

@ -1,3 +1,4 @@
/target **/target/**
.qtcreator **/.qtcreator/**
.idea **/.idea/**
**/*.autosave/**

547
Cargo.lock generated
View File

@ -2,12 +2,65 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.10" version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.16" version = "1.2.16"
@ -19,13 +72,34 @@ dependencies = [
"shlex", "shlex",
] ]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clang-format" name = "clang-format"
version = "0.3.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "696283b40e1a39d208ee614b92e5f6521d16962edeb47c48372585ec92419943" checksum = "696283b40e1a39d208ee614b92e5f6521d16962edeb47c48372585ec92419943"
dependencies = [ dependencies = [
"thiserror", "thiserror 1.0.69",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags 1.3.2",
"strsim 0.8.0",
"textwrap",
"unicode-width",
"vec_map",
] ]
[[package]] [[package]]
@ -45,7 +119,7 @@ checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"clap_lex", "clap_lex",
"strsim", "strsim 0.11.1",
] ]
[[package]] [[package]]
@ -62,6 +136,10 @@ dependencies = [
"cxx-qt", "cxx-qt",
"cxx-qt-build", "cxx-qt-build",
"cxx-qt-lib", "cxx-qt-lib",
"dirs",
"log",
"structopt",
"syntect",
] ]
[[package]] [[package]]
@ -83,6 +161,15 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "cxx" name = "cxx"
version = "1.0.148" version = "1.0.148"
@ -106,7 +193,7 @@ dependencies = [
"codespan-reporting", "codespan-reporting",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -120,7 +207,7 @@ dependencies = [
"cxx-qt-macro", "cxx-qt-macro",
"qt-build-utils", "qt-build-utils",
"static_assertions", "static_assertions",
"thiserror", "thiserror 1.0.69",
] ]
[[package]] [[package]]
@ -152,7 +239,7 @@ dependencies = [
"indoc", "indoc",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -175,7 +262,7 @@ checksum = "58a4fe02c0604eda28c605792f5ba0d0251b4947f8f0fc43e55b61c06b2b8ec6"
dependencies = [ dependencies = [
"cxx-qt-gen", "cxx-qt-gen",
"proc-macro2", "proc-macro2",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -184,11 +271,11 @@ version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40399fddbf3977647bfff7453dacffc6b5701b19a282a283369a870115d0a049" checksum = "40399fddbf3977647bfff7453dacffc6b5701b19a282a283369a870115d0a049"
dependencies = [ dependencies = [
"clap", "clap 4.5.32",
"codespan-reporting", "codespan-reporting",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -206,7 +293,37 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn", "syn 2.0.100",
]
[[package]]
name = "deranged"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058"
dependencies = [
"powerfmt",
]
[[package]]
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys",
] ]
[[package]] [[package]]
@ -215,12 +332,79 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "flate2"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.1.5" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]] [[package]]
name = "indoc" name = "indoc"
version = "2.0.6" version = "2.0.6"
@ -251,12 +435,28 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.171" version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.9.0",
"libc",
]
[[package]] [[package]]
name = "link-cplusplus" name = "link-cplusplus"
version = "1.0.10" version = "1.0.10"
@ -266,6 +466,18 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[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"
@ -278,6 +490,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
dependencies = [
"adler2",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -288,6 +509,95 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "onig"
version = "6.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f"
dependencies = [
"bitflags 1.3.2",
"libc",
"once_cell",
"onig_sys",
]
[[package]]
name = "onig_sys"
version = "69.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "plist"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d"
dependencies = [
"base64",
"indexmap",
"quick-xml",
"serde",
"time",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.94" version = "1.0.94"
@ -304,10 +614,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efb239fdd8c036fabb95364320041ef68197cd4ab971bb3b4ca3ea0b7b93d12c" checksum = "efb239fdd8c036fabb95364320041ef68197cd4ab971bb3b4ca3ea0b7b93d12c"
dependencies = [ dependencies = [
"cc", "cc",
"thiserror", "thiserror 1.0.69",
"versions", "versions",
] ]
[[package]]
name = "quick-xml"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.40" version = "1.0.40"
@ -317,6 +636,23 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "redox_users"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [
"getrandom",
"libredox",
"thiserror 2.0.12",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.20" version = "1.0.20"
@ -329,6 +665,15 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.219" version = "1.0.219"
@ -346,7 +691,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -373,12 +718,53 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "structopt"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [
"clap 2.34.0",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.100" version = "2.0.100"
@ -390,6 +776,28 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "syntect"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1"
dependencies = [
"bincode",
"bitflags 1.3.2",
"flate2",
"fnv",
"once_cell",
"onig",
"plist",
"regex-syntax",
"serde",
"serde_derive",
"serde_json",
"thiserror 1.0.69",
"walkdir",
"yaml-rust",
]
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.4.1" version = "1.4.1"
@ -399,13 +807,31 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl 2.0.12",
] ]
[[package]] [[package]]
@ -416,7 +842,49 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "time"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "time-macros"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
dependencies = [
"num-conv",
"time-core",
] ]
[[package]] [[package]]
@ -437,6 +905,12 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@ -453,6 +927,38 @@ dependencies = [
"nom", "nom",
] ]
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.9" version = "0.1.9"
@ -462,6 +968,12 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"
@ -534,3 +1046,12 @@ name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View File

@ -6,7 +6,11 @@ edition = "2024"
[dependencies] [dependencies]
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", "qt_gui"] }
log = { version = "0.4.27", features = [] }
dirs = "6.0.0"
syntect = "5.2.0"
structopt = "0.3.26"
[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.

View File

@ -5,7 +5,7 @@ CLIDE is an IDE written in Rust that supports both full and headless Linux envir
The following packages must be installed before the application will build. The following packages must be installed before the application will build.
```bash ```bash
sudo apt install qt6-base-dev qt6-declarative-dev qt6-tools-dev qml6-module-qtquick-controls qml6-module-qtquick-layouts qml6-module-qtquick-window qml6-module-qtqml-workerscript qml6-module-qtquick-templates qml6-module-qtquick sudo apt install qt6-base-dev qt6-declarative-dev qt6-tools-dev qml6-module-qtquick-controls qml6-module-qtquick-layouts qml6-module-qtquick-window qml6-module-qtqml-workerscript qml6-module-qtquick-templates qml6-module-qtquick qml6-module-qtquick-dialogs qt6-svg-dev
``` ```
And of course, [Rust](https://www.rust-lang.org/tools/install). And of course, [Rust](https://www.rust-lang.org/tools/install).
@ -22,10 +22,39 @@ The [Qt Installer](https://www.qt.io/download-qt-installer) will provide the lat
If using RustRover be sure to set your QML binaries path in the settings menu. If using RustRover be sure to set your QML binaries path in the settings menu.
If Qt was installed to its default directory this will be `$HOME/Qt/6.8.3/gcc_64/bin/`. If Qt was installed to its default directory this will be `$HOME/Qt/6.8.3/gcc_64/bin/`.
Viewing documentation in the web browser is possible, but you will end up in a mess of tabs.
Using Qt Assistant is recommended. It comes with Qt6 when installed. Run the following command to start it.
```bash
nohup $HOME/Qt/6.8.3/gcc_64/bin/assistant > /dev/null 2>&1 &
```
If you are looking for an include path from Qt
```bash
find /usr/include/x86_64-linux-gnu/qt6/ -name QFile*
/usr/include/x86_64-linux-gnu/qt6/QtWidgets/QFileIconProvider
/usr/include/x86_64-linux-gnu/qt6/QtWidgets/QFileDialog
/usr/include/x86_64-linux-gnu/qt6/QtGui/QFileSystemModel
/usr/include/x86_64-linux-gnu/qt6/QtGui/QFileOpenEvent
/usr/include/x86_64-linux-gnu/qt6/QtCore/QFile
/usr/include/x86_64-linux-gnu/qt6/QtCore/QFileDevice
/usr/include/x86_64-linux-gnu/qt6/QtCore/QFileSystemWatcher
/usr/include/x86_64-linux-gnu/qt6/QtCore/QFileInfoList
/usr/include/x86_64-linux-gnu/qt6/QtCore/QFileInfo
/usr/include/x86_64-linux-gnu/qt6/QtCore/QFileSelector
```
This helped find that QFileSystemModel is in QtGui and not QtCore.
### Resources ### Resources
Some helpful links for reading up on QML if you're just getting started. Some helpful links for reading up on QML if you're just getting started.
* [Rust Crates - cxx-qt](https://docs.rs/releases/search?query=cxx_qt)
* [QML Reference](https://doc.qt.io/qt-6/qmlreference.html) * [QML Reference](https://doc.qt.io/qt-6/qmlreference.html)
* [QML Coding Conventions](https://doc.qt.io/qt-6/qml-codingconventions.html) * [QML Coding Conventions](https://doc.qt.io/qt-6/qml-codingconventions.html)
* [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/)
* [github.com/KDAB/cxx-qt](https://github.com/KDAB/cxx-qt)

View File

@ -8,13 +8,20 @@ fn main() {
// - Qt Qml is linked by enabling the qt_qml Cargo feature of cxx-qt-lib. // - Qt Qml is linked by enabling the qt_qml Cargo feature of cxx-qt-lib.
// - Qt Qml requires linking Qt Network on macOS // - Qt Qml requires linking Qt Network on macOS
.qt_module("Network") .qt_module("Network")
.qt_module("Gui")
.qt_module("Svg")
.qt_module("Xml")
.qml_module(QmlModule { .qml_module(QmlModule {
uri: "test", uri: "clide.module",
rust_files: &["src/main.rs"], rust_files: &["src/gui/colors.rs", "src/gui/filesystem.rs"],
qml_files: &["qml/main.qml", qml_files: &[
"qml/Menu/ClideMenu.qml", "qml/main.qml",
"qml/Menu/ClideMenuBar.qml", "qml/ClideAboutWindow.qml",
"qml/Menu/ClideMenuBarItem.qml"], "qml/ClideTreeView.qml",
"qml/ClideProjectView.qml",
"qml/ClideEditor.qml",
"qml/ClideMenuBar.qml",
],
..Default::default() ..Default::default()
}) })
.build(); .build();

38
icons/folder_closed.svg Normal file
View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
viewBox="-10 0 1792 1792"
id="svg51"
sodipodi:docname="folder_closed.svg"
width="1792"
height="1792"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs55" />
<sodipodi:namedview
id="namedview53"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.45033482"
inkscape:cx="842.70632"
inkscape:cy="896"
inkscape:window-width="1846"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg51" />
<path
fill="currentColor"
d="m 1718,672 v 704 q 0,92 -66,158 -66,66 -158,66 H 278 q -92,0 -158,-66 -66,-66 -66,-158 V 416 q 0,-92 66,-158 66,-66 158,-66 h 320 q 92,0 158,66 66,66 66,158 v 32 h 672 q 92,0 158,66 66,66 66,158 z"
id="path49" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

38
icons/folder_open.svg Normal file
View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
viewBox="-10 0 1792 1792"
id="svg139"
sodipodi:docname="folder_open.svg"
width="1792"
height="1792"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs143" />
<sodipodi:namedview
id="namedview141"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.24358259"
inkscape:cx="149.84651"
inkscape:cy="1098.1901"
inkscape:window-width="1846"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg139" />
<path
fill="currentColor"
d="M 1590,1376 V 672 q 0,-40 -28,-68 -28,-28 -68,-28 H 790 q -40,0 -68,-28 -28,-28 -28,-68 v -64 q 0,-40 -28,-68 -28,-28 -68,-28 H 278 q -40,0 -68,28 -28,28 -28,68 v 960 q 0,40 28,68 28,28 68,28 h 1216 q 40,0 68,-28 28,-28 28,-68 z m 128,-704 v 704 q 0,92 -66,158 -66,66 -158,66 H 278 q -92,0 -158,-66 -66,-66 -66,-158 V 416 q 0,-92 66,-158 66,-66 158,-66 h 320 q 92,0 158,66 66,66 66,158 v 32 h 672 q 92,0 158,66 66,66 66,158 z"
id="path137" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

38
icons/generic_file.svg Normal file
View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
viewBox="-10 0 1792 1792"
id="svg147"
sodipodi:docname="generic_file.svg"
width="1792"
height="1792"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs151" />
<sodipodi:namedview
id="namedview149"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.12179129"
inkscape:cx="-578.85911"
inkscape:cy="1687.3127"
inkscape:window-width="1846"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg147" />
<path
fill="currentColor"
d="m 1586,476 q 14,14 28,36 H 1142 V 40 q 22,14 36,28 z m -476,164 h 544 v 1056 q 0,40 -28,68 -28,28 -68,28 H 214 q -40,0 -68,-28 -28,-28 -28,-68 V 96 Q 118,56 146,28 174,0 214,0 h 800 v 544 q 0,40 28,68 28,28 68,28 z m 160,736 v -64 q 0,-14 -9,-23 -9,-9 -23,-9 H 534 q -14,0 -23,9 -9,9 -9,23 v 64 q 0,14 9,23 9,9 23,9 h 704 q 14,0 23,-9 9,-9 9,-23 z m 0,-256 v -64 q 0,-14 -9,-23 -9,-9 -23,-9 H 534 q -14,0 -23,9 -9,9 -9,23 v 64 q 0,14 9,23 9,9 23,9 h 704 q 14,0 23,-9 9,-9 9,-23 z m 0,-256 v -64 q 0,-14 -9,-23 -9,-9 -23,-9 H 534 q -14,0 -23,9 -9,9 -9,23 v 64 q 0,14 9,23 9,9 23,9 h 704 q 14,0 23,-9 9,-9 9,-23 z"
id="path145" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
icons/kilroy-256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

73
qml/ClideAboutWindow.qml Normal file
View File

@ -0,0 +1,73 @@
// TODO: Header
import QtQuick
import QtQuick.Controls.Basic
import clide.module 1.0
ApplicationWindow {
id: root
width: 450
height: 350
// Create the window with no frame and keep it on top.
flags: Qt.Window | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
color: RustColors.gutter
// Hide the window when it loses focus.
onActiveChanged: {
if (!active) {
root.visible = false;
}
}
// Kilroy logo.
Image {
id: logo
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 20
source: "../icons/kilroy-256.png"
sourceSize.width: 80
sourceSize.height: 80
fillMode: Image.PreserveAspectFit
smooth: true
antialiasing: true
asynchronous: true
}
ScrollView {
anchors.top: logo.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 20
TextArea {
selectedTextColor: RustColors.editor_highlighted_text
selectionColor: RustColors.editor_highlight
horizontalAlignment: Text.AlignHCenter
textFormat: Text.RichText
text: qsTr("<h3>About CLIDE</h3>"
+ "<p>A simple text editor written in Rust and QML using CXX-Qt.</p>"
+ "<p>Personal website <a href=\"http://shaunreed.com\">shaunreed.com</a></p>"
+ "<p>Project notes <a href=\"http://knoats.com\">knoats.com</a></p>"
+ "<p>This project is developed at <a href=\"http://git.shaunreed.com/shaunrd0/clide\">git.shaunreed.com</a></p>"
+ "<p><a href=\"https://github.com/KDAB/cxx-qt\">KDAB CXX-Qt repository</a></p>"
+ "<p>Copyright (C) 2025 Shaun Reed, all rights reserved.</p>")
color: RustColors.editor_text
wrapMode: Text.WordWrap
readOnly: true
antialiasing: true
background: null
onLinkActivated: function (link) {
Qt.openUrlExternally(link)
}
}
}
}

206
qml/ClideEditor.qml Normal file
View File

@ -0,0 +1,206 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import clide.module 1.0
SplitView {
id: root
Layout.fillHeight: true
Layout.fillWidth: true
orientation: Qt.Vertical
// 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;
// Customized handle to drag between the Editor and the Console.
handle: Rectangle {
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
radius: 2.5
// Execute these behaviors when the color is changed.
Behavior on color {
ColorAnimation {
duration: 400
}
}
}
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
// 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
Column {
anchors.fill: parent
topPadding: textArea.topPadding
Repeater {
id: repeatedLineNumbers
// 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 {
// Calculates the height of each line in the text area.
height: textArea.contentHeight / textArea.lineCount
width: parent.width
required property int index
// 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
}
// 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
}
}
}
}
}
Flickable {
id: editorFlickable
Layout.fillHeight: true
Layout.fillWidth: true
boundsBehavior: Flickable.StopAtBounds
height: 650
ScrollBar.horizontal: MyScrollBar {
}
ScrollBar.vertical: MyScrollBar {
}
TextArea.flickable: TextArea {
id: textArea
focus: true
persistentSelection: true
antialiasing: true
selectByMouse: true
selectionColor: RustColors.editor_highlight
selectedTextColor: RustColors.editor_highlighted_text
textFormat: Qt.AutoText
wrapMode: TextArea.Wrap
text: FileSystem.readFile(root.filePath)
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()
// }
// }
}
FontMetrics {
id: fontMetrics
font: textArea.font
}
}
}
TextArea {
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
// scroll-bars. This is convenient when the component is only used in one file.
component MyScrollBar: ScrollBar {
id: scrollBar
// Scroll bar gutter
background: Rectangle {
implicitHeight: scrollBar.interactive ? 8 : 4
implicitWidth: scrollBar.interactive ? 8 : 4
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
}
}
}
// Scroll bar
contentItem: Rectangle {
implicitHeight: scrollBar.interactive ? 8 : 4
implicitWidth: scrollBar.interactive ? 8 : 4
// 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,11 +1,42 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import clide.module 1.0
MenuBar { MenuBar {
// Base settings for each Menu.
component ClideMenu : Menu {
background: Rectangle { background: Rectangle {
color: "#3b3e40" // Dark background like CLion color: RustColors.menubar
implicitWidth: 100
radius: 2
}
} }
// Base settings for each MenuItem.
component ClideMenuItem : MenuItem {
id: root
background: Rectangle {
color: root.hovered ? RustColors.hovered : RustColors.unhovered
radius: 2.5
}
contentItem: IconLabel {
color: "black"
font.family: "Helvetica"
text: root.text
}
}
// Background for this MenuBar.
background: Rectangle {
color: RustColors.menubar
border.color: RustColors.menubar_border
}
//
// File Menu
Action { Action {
id: actionNewProject id: actionNewProject
@ -25,32 +56,37 @@ MenuBar {
id: actionExit id: actionExit
text: qsTr("&Exit") text: qsTr("&Exit")
onTriggered: Qt.quit() onTriggered: Qt.quit()
} }
ClideMenu { ClideMenu {
title: qsTr("&File") title: qsTr("&File")
ClideMenuBarItem { ClideMenuItem {
action: actionNewProject action: actionNewProject
} }
ClideMenuBarItem { ClideMenuItem {
action: actionOpen action: actionOpen
onTriggered: FileSystem.setDirectory(FileSystem.filePath)
} }
ClideMenuBarItem { ClideMenuItem {
action: actionSave action: actionSave
} }
MenuSeparator { MenuSeparator {
background: Rectangle { background: Rectangle {
border.color: color border.color: color
color: "#3c3f41" color: RustColors.menubar_border
implicitHeight: 3 implicitHeight: 3
implicitWidth: 200 implicitWidth: 200
} }
} }
ClideMenuBarItem { ClideMenuItem {
action: actionExit action: actionExit
} }
} }
//
// Edit Menu
Action { Action {
id: actionUndo id: actionUndo
@ -79,22 +115,25 @@ MenuBar {
ClideMenu { ClideMenu {
title: qsTr("&Edit") title: qsTr("&Edit")
ClideMenuBarItem { ClideMenuItem {
action: actionUndo action: actionUndo
} }
ClideMenuBarItem { ClideMenuItem {
action: actionRedo action: actionRedo
} }
ClideMenuBarItem { ClideMenuItem {
action: actionCut action: actionCut
} }
ClideMenuBarItem { ClideMenuItem {
action: actionCopy action: actionCopy
} }
ClideMenuBarItem { ClideMenuItem {
action: actionPaste action: actionPaste
} }
} }
//
// View Menu
Action { Action {
id: actionToolWindows id: actionToolWindows
@ -108,13 +147,20 @@ MenuBar {
ClideMenu { ClideMenu {
title: qsTr("&View") title: qsTr("&View")
ClideMenuBarItem { ClideMenuItem {
action: actionToolWindows action: actionToolWindows
} }
ClideMenuBarItem { ClideMenuItem {
action: actionAppearance action: actionAppearance
} }
} }
//
// Help Menu
ClideAboutWindow {
id: clideAbout
}
Action { Action {
id: actionDocumentation id: actionDocumentation
@ -122,16 +168,18 @@ MenuBar {
} }
Action { Action {
id: actionAbout id: actionAbout
// Toggle the about window with the menu item is clicked.
onTriggered: clideAbout.visible = !clideAbout.visible
text: qsTr("&About") text: qsTr("&About")
} }
ClideMenu { ClideMenu {
title: qsTr("&Help") title: qsTr("&Help")
ClideMenuBarItem { ClideMenuItem {
action: actionDocumentation action: actionDocumentation
} }
ClideMenuBarItem { ClideMenuItem {
action: actionAbout action: actionAbout
} }
} }

55
qml/ClideProjectView.qml Normal file
View File

@ -0,0 +1,55 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import clide.module 1.0
SplitView {
id: root
// Path to the file selected in the tree view.
default property string selectedFilePath: FileSystem.filePath;
Layout.fillHeight: true
Layout.fillWidth: true
anchors.fill: parent
// Customized handle to drag between the Navigation and the Editor.
handle: Rectangle {
id: verticalSplitHandle
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
radius: 2.5
// Execute these behaviors when the color is changed.
Behavior on color {
ColorAnimation {
duration: 400
}
}
}
Rectangle {
id: navigationView
color: RustColors.explorer_background
SplitView.fillHeight: true
SplitView.minimumWidth: 0
SplitView.preferredWidth: 200
SplitView.maximumWidth: 250
StackLayout {
anchors.fill: parent
ClideTreeView {
id: clideTreeView
onFileClicked: path => root.selectedFilePath = path
}
}
}
ClideEditor {
SplitView.fillWidth: true
// Initialize using the Default trait in Rust QML singleton FileSystem.
filePath: root.selectedFilePath
}
}

146
qml/ClideTreeView.qml Normal file
View File

@ -0,0 +1,146 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import clide.module 1.0
Rectangle {
id: root
color: RustColors.explorer_background
signal fileClicked(string filePath)
TreeView {
id: fileSystemTreeView
anchors.margins: 15
// rootIndex: FileSystem.rootIndex
property int lastIndex: -1
model: FileSystem
anchors.fill: parent
boundsBehavior: Flickable.StopAtBounds
boundsMovement: Flickable.StopAtBounds
clip: true
Component.onCompleted: {
FileSystem.setDirectory(FileSystem.filePath)
fileSystemTreeView.expandRecursively(0, 4)
}
// The delegate represents a single entry in the filesystem.
delegate: TreeViewDelegate {
id: treeDelegate
indentation: 8
implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250
implicitHeight: 25
required property int index
required property url filePath
required property string fileName
indicator: Image {
id: directoryIcon
x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation)
anchors.verticalCenter: parent.verticalCenter
source: {
// If the item has children, it's a directory.
if (treeDelegate.hasChildren) {
return treeDelegate.expanded ?
"../icons/folder-open-solid.svg" : "../icons/folder-solid.svg";
}
return "../icons/file-solid.svg"
}
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.
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
}
}
}
}
}
}

View File

@ -1,10 +0,0 @@
import QtQuick
import QtQuick.Controls
Menu {
background: Rectangle {
color: "#3c3f41"
implicitWidth: 200
radius: 2
}
}

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: "white"
font.family: "Helvetica"
text: root.text
}
}

View File

@ -1,10 +1,13 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs
import "Menu" import clide.module 1.0
ApplicationWindow { ApplicationWindow {
id: appWindow
height: 800 height: 800
title: "CLIDE" title: "CLIDE"
visible: true visible: true
@ -15,6 +18,14 @@ ApplicationWindow {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
color: "#1e1f22" // Dark background color: RustColors.gutter
}
MessageDialog {
id: errorDialog
title: qsTr("Error")
}
ClideProjectView {
} }
} }

38
src/gui.rs Normal file
View File

@ -0,0 +1,38 @@
use crate::gui::filesystem::FS_STATE;
use crate::gui::filesystem::qobject::FileSystem;
use cxx_qt_lib::{QModelIndex, QQmlEngine, QString};
use std::error::Error;
pub mod colors;
pub mod filesystem;
pub fn run(root_path: std::path::PathBuf) -> Result<(), Box<dyn Error>> {
println!("Starting the GUI editor at {:?}", root_path);
use cxx_qt_lib::{QGuiApplication, QQmlApplicationEngine, QUrl};
let mut app = QGuiApplication::new();
let mut engine = QQmlApplicationEngine::new();
// let clide_fs = FileSystemImpl {
// file_path: QString::from(root_path.to_str().unwrap()),
// root_index: QModelIndex::default(),
// };
unsafe {
let mut fs_state = FS_STATE.take_fs();
fs_state.file_path = QString::from(root_path.to_str().unwrap());
};
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"));
}
if let Some(app) = app.as_mut() {
app.exec();
}
Ok(())
}

92
src/gui/colors.rs Normal file
View File

@ -0,0 +1,92 @@
#[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_hovered)]
#[qproperty(QColor, explorer_text)]
#[qproperty(QColor, explorer_text_selected)]
#[qproperty(QColor, explorer_background)]
#[qproperty(QColor, explorer_folder)]
#[qproperty(QColor, explorer_folder_open)]
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_hovered: QColor,
explorer_text: QColor,
explorer_text_selected: QColor,
explorer_background: QColor,
explorer_folder: QColor,
explorer_folder_open: 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("#94989b").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("#acaea3").unwrap(),
editor_highlighted_text: QColor::try_from("#ccced3").unwrap(),
editor_highlight: QColor::try_from("#ccced3").unwrap(),
gutter: QColor::try_from("#1e1f22").unwrap(),
explorer_hovered: QColor::try_from("#4c5053").unwrap(),
explorer_text: QColor::try_from("#3b3b3b").unwrap(),
explorer_text_selected: QColor::try_from("#8b8b8b").unwrap(),
explorer_background: QColor::try_from("#676c70").unwrap(),
explorer_folder: QColor::try_from("#54585b").unwrap(),
explorer_folder_open: QColor::try_from("#FFF").unwrap(),
}
}
}

153
src/gui/filesystem.rs Normal file
View File

@ -0,0 +1,153 @@
#[cxx_qt::bridge]
pub mod qobject {
unsafe extern "C++" {
// Import Qt Types from C++
include!("cxx-qt-lib/qstring.h");
type QString = cxx_qt_lib::QString;
include!("cxx-qt-lib/qmodelindex.h");
type QModelIndex = cxx_qt_lib::QModelIndex;
include!(<QtGui/QFileSystemModel>);
type QFileSystemModel;
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
// Export QML Types from Rust
#[qobject]
#[base = QFileSystemModel]
#[qml_element]
#[qml_singleton]
#[qproperty(QString, file_path]
#[qproperty(QModelIndex, root_index]
type FileSystem = super::FileSystemImpl;
#[inherit]
fn set_root_path(self: Pin<&mut FileSystem>, path: &QString) -> QModelIndex;
#[qinvokable]
#[cxx_override]
fn column_count(self: &FileSystem, _index: &QModelIndex) -> i32;
#[qinvokable]
fn read_file(self: &FileSystem, path: &QString) -> QString;
#[qinvokable]
fn set_directory(self: Pin<&mut FileSystem>, path: &QString) -> QModelIndex;
#[qinvokable]
fn take_fs(self: &mut) -> FileSystem;
}
}
use cxx_qt_lib::{QModelIndex, QString};
use dirs;
use log::warn;
use std::fs;
use std::io::BufRead;
use std::ptr::replace;
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;
/// Singleton holding filesystem state.
pub struct FileSystemState {
pub fs: Option<FileSystemImpl>,
}
impl FileSystemState {
pub unsafe fn take_fs(&mut self) -> FileSystemImpl {
let fs = replace(&mut self.fs, None);
fs.unwrap()
}
}
pub static mut FS_STATE : FileSystemState = FileSystemState { fs: Some(FileSystemImpl::default()) };
// TODO: Implement a provider for QFileSystemModel::setIconProvider for icons.
pub struct FileSystemImpl {
pub file_path: QString,
pub root_index: QModelIndex,
}
// Default is explicit to make the editor open this source file initially.
impl Default for FileSystemImpl {
fn default() -> Self {
Self {
file_path: QString::from(file!()),
root_index: Default::default(),
}
}
}
impl qobject::FileSystem {
fn read_file(&self, path: &QString) -> QString {
if path.is_empty() {
return QString::default();
}
if !fs::metadata(path.to_string())
.expect(format!("Failed to get file metadata {}", path).as_str())
.is_file()
{
warn!("Attempted to open file {} that is not a valid file", path);
return QString::default();
}
let ss = SyntaxSet::load_defaults_nonewlines();
let ts = ThemeSet::load_defaults();
let theme = &ts.themes["base16-ocean.dark"];
let mut highlighter =
HighlightFile::new(path.to_string(), &ss, theme).expect("Failed to create highlighter");
let (mut output, _bg) = start_highlighted_html_snippet(theme);
let mut line = String::new();
while highlighter
.reader
.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(
&regions[..],
IncludeBackground::Yes,
&mut output,
)
.expect("Failed to insert highlighted html");
line.clear();
}
output.push_str("</pre>\n");
QString::from(output)
}
// There will never be more than one column.
fn column_count(&self, _index: &QModelIndex) -> i32 {
1
}
fn set_directory(self: std::pin::Pin<&mut Self>, path: &QString) -> QModelIndex {
if !path.is_empty()
&& fs::metadata(path.to_string())
.expect(format!("Failed to get metadata for path {}", path).as_str())
.is_dir()
{
self.set_root_path(path)
} else {
// If the initial directory can't be opened, attempt to find the home directory.
self.set_root_path(&QString::from(
dirs::home_dir()
.expect("Failed to get home directory")
.as_path()
.to_str()
.unwrap()
.to_string(),
))
}
}
}

View File

@ -1,90 +1,58 @@
// 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] use std::error::Error;
mod qobject { use std::process::{Command, Stdio};
unsafe extern "C++" { use structopt::StructOpt;
include!("cxx-qt-lib/qstring.h");
type QString = cxx_qt_lib::QString;
}
#[qenum(Greeter)] pub mod gui;
pub enum Language { pub mod tui;
English,
German,
French,
}
#[qenum(Greeter)] /// Command line interface IDE with full GUI and headless modes.
pub enum Greeting { /// If no flags are provided the GUI editor is launched in a separate process.
Hello, /// If no path is provided the current directory is used.
Bye, #[derive(StructOpt, Debug)]
} #[structopt(name = "clide", verbatim_doc_comment)]
struct Cli {
/// The root directory for the project to open with the clide editor.
#[structopt(parse(from_os_str))]
pub path: Option<std::path::PathBuf>,
unsafe extern "RustQt" { /// Run clide in headless mode.
#[qobject] #[structopt(name = "tui", short, long)]
#[qml_element] pub tui: bool,
#[qproperty(Greeting, greeting)]
#[qproperty(Language, language)]
type Greeter = super::GreeterRust;
#[qinvokable] /// Run the clide GUI in the current process, blocking the terminal and showing all output streams.
fn greet(self: &Greeter) -> QString; #[structopt(name = "gui", short, long)]
} pub gui: bool,
} }
use qobject::*; fn main() -> Result<(), Box<dyn Error>> {
let args = Cli::from_args();
impl Greeting { let root_path = match args.path {
fn translate(&self, language: Language) -> String { // If the CLI was provided a directory convert it to absolute.
match (self, language) { Some(path) => std::path::absolute(path)?,
(&Greeting::Hello, Language::English) => "Hello, World!", // If no path was provided, use current directory.
(&Greeting::Hello, Language::German) => "Hallo, Welt!", None => std::env::current_dir().unwrap_or_else(|_|
(&Greeting::Hello, Language::French) => "Bonjour, le monde!", // If we can't find the CWD attempt to open the home directory.
(&Greeting::Bye, Language::English) => "Bye!", dirs::home_dir().expect("Failed to access filesystem.")),
(&Greeting::Bye, Language::German) => "Auf Wiedersehen!", };
(&Greeting::Bye, Language::French) => "Au revoir!",
_ => "🤯", match args.gui {
true => gui::run(root_path),
false => match args.tui {
// Open the TUI editor if requested, otherwise use the QML GUI by default.
true => tui::run(root_path),
false => {
// Relaunch the CLIDE GUI in a separate process.
Command::new(std::env::current_exe()?)
.args(&["--gui", root_path.to_str().unwrap()])
.stdout(Stdio::null())
.stderr(Stdio::null())
.stdin(Stdio::null())
.spawn()?;
Ok(())
} }
.to_string() },
}
}
pub struct GreeterRust {
greeting: Greeting,
language: Language,
}
impl Default for GreeterRust {
fn default() -> Self {
Self {
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() {
use cxx_qt_lib::{QGuiApplication, QQmlApplicationEngine, QUrl};
let mut app = QGuiApplication::new();
let mut engine = QQmlApplicationEngine::new();
if let Some(engine) = engine.as_mut() {
engine.load(&QUrl::from("qml/main.qml"));
}
if let Some(app) = app.as_mut() {
app.exec();
} }
} }

6
src/tui.rs Normal file
View File

@ -0,0 +1,6 @@
use std::error::Error;
pub fn run(root_path: std::path::PathBuf) -> Result<(), Box<dyn Error>> {
println!("Starting the TUI editor at {:?}", root_path);
Ok(())
}