diff --git a/Cargo.lock b/Cargo.lock index 434b976..85118a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,47 @@ +[[package]] +name = "argon2rs" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayvec" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bit-set" version = "0.4.0" @@ -21,11 +65,25 @@ name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "c_vec" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cc" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cfg-if" version = "0.1.6" @@ -39,13 +97,52 @@ dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "constant_time_eq" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "dirs" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "editor" version = "0.1.0" dependencies = [ + "dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "plex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -62,6 +159,11 @@ name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "itoa" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "lalr" version = "0.0.2" @@ -74,7 +176,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.44" +version = "0.2.45" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "nodrop" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -143,7 +250,7 @@ version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -153,7 +260,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -164,7 +271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -191,6 +298,37 @@ dependencies = [ "vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "redox_syscall" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_users" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ryu" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "sdl2" version = "0.31.0" @@ -199,7 +337,7 @@ dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "c_vec 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2-sys 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -213,6 +351,31 @@ dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "serde" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.14.9" @@ -223,6 +386,27 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "0.15.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-xid" version = "0.1.0" @@ -253,18 +437,31 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" +"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" +"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" +"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" "checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum c_vec 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c32b15e95ce816aaf991a41420854e6ba772a2679a9296d906eab1114f1b4e9" +"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" +"checksum dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88972de891f6118092b643d85a0b28e0678e0f948d7f879aa32f2d5aafe97d2a" +"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" +"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum lalr 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "106d7548f95adbe3019b4fc4954554d7b72535867aa9ce326d2f766b68958de7" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" -"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311" +"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" +"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" @@ -278,9 +475,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" "checksum redfa 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "29cc2771cc9f5fb0061cdedc05a37170254694dffec6b89920a6e767f08c4220" +"checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2" +"checksum redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "214a97e49be64fd2c86f568dd0cb2c757d2cc53de95b273b6ad0a1c908482f26" +"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" +"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" +"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum sdl2 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74c2a98a354b20713b90cce70aef9e927e46110d1bc4ef728fd74e0d53eba60" "checksum sdl2-sys 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c543ce8a6e33a30cb909612eeeb22e693848211a84558d5a00bb11e791b7ab7" +"checksum serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "0e732ed5a5592c17d961555e3b552985baf98d50ce418b7b655f31f6ba7eb1b7" +"checksum serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d6115a3ca25c224e409185325afc16a0d5aaaabc15c42b09587d6f1ba39a5b" +"checksum serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "bdf540260cfee6da923831f4776ddc495ada940c30117977c70f1313a6130545" "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" +"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" +"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac5efe5cb0fa14ec2f84f83c701c562ee63f6dcc680861b21d65c682adfb05f" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" diff --git a/Cargo.toml b/Cargo.toml index 737d566..487d625 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,10 @@ edition = "2018" [dependencies] rand = "0.5" plex = "*" +dirs = "*" +serde = "*" +serde_json = "*" +serde_derive = "*" [dependencies.sdl2] version = "0.31.0" diff --git a/assets/theme.txt b/assets/theme.txt new file mode 100644 index 0000000..4045dc1 --- /dev/null +++ b/assets/theme.txt @@ -0,0 +1,56 @@ +```railscasts + +``comment +#BC9358 +italic + +``escaped character +#509E4F + +``class +#FFF + +``constant +#FFF + +``float +#A4C260 + +``function +#FFC56D + +``global variable +#D0CFFE + +``integer +#A4C260 + +``inline code +#151515 + +``instance variable +#D0CFFE + +``doctype +#E7BE69 + +``keyword +#CB7832 + +``keyword +#CB7832 + +``regex +#A4C260 + +``string +#A4C260 + +``symbol +#6C9CBD + +``html tag +#E7BE69 + +``boolean +#6C9CBD diff --git a/src/app/app_state.rs b/src/app/app_state.rs index fe2d5fe..55a94d2 100644 --- a/src/app/app_state.rs +++ b/src/app/app_state.rs @@ -2,43 +2,54 @@ use std::rc::Rc; use std::sync::Arc; use std::boxed::Box; use crate::app::{UpdateResult, WindowCanvas}; +use crate::ui::*; +use crate::ui::caret::Caret; use crate::file::*; -use crate::renderer::Renderer; use crate::file::editor_file::EditorFile; +use crate::renderer::Renderer; -pub struct AppState<'a> { - pub files: Vec>, +pub struct AppState { + pub files: Vec, pub current_file: i16, + caret: Caret, } -impl<'a> AppState<'a> { +impl AppState { pub fn new() -> Self { Self { files: vec![], current_file: -1, + caret: Caret::new(), } } - pub fn open_file(&mut self, file_path: String, renderer: &mut Renderer) { + pub fn open_file(&mut self, file_path: String) { use std::fs::read_to_string; if let Ok(buffer) = read_to_string(&file_path) { println!("read: {}\n{}", file_path, buffer); - let file = EditorFile::new(file_path.clone(), buffer, renderer); + let file = EditorFile::new(file_path.clone(), buffer); self.current_file = self.files.len() as i16; self.files.push(file); }; } +} - pub fn update(&mut self, ticks: i32) -> UpdateResult { - if let Some(ref mut file) = self.files.get(self.current_file as usize) { - file.update(ticks); - } - UpdateResult::NoOp - } - - pub fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) { - if let Some(ref mut file) = self.files.get(self.current_file as usize) { +impl Render for AppState { + fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult { + if let Some(file) = self.files.get_mut(self.current_file as usize) { file.render(canvas, renderer); } + self.caret.render(canvas, renderer); + UpdateResult::NoOp + } +} + +impl Update for AppState { + fn update(&mut self, ticks: i32) -> UpdateResult { + if let Some(file) = self.files.get_mut(self.current_file as usize) { + file.update(ticks); + } + self.caret.update(ticks); + UpdateResult::NoOp } } diff --git a/src/app/config.rs b/src/app/config.rs index 113e6fa..e46cc8e 100644 --- a/src/app/config.rs +++ b/src/app/config.rs @@ -1,3 +1,5 @@ +use crate::themes::Theme; + #[derive(Debug, Clone)] pub struct EditorConfig { pub character_size: u16, @@ -9,6 +11,7 @@ pub struct Config { pub width: u32, pub height: u32, pub editor_config: EditorConfig, + pub theme: Theme, } impl Config { @@ -20,6 +23,7 @@ impl Config { character_size: 24, font_path: "./assets/fonts/hinted-ElaineSans-Medium.ttf".to_string(), }, + theme: Theme::load("default".to_string()), } } } diff --git a/src/app/mod.rs b/src/app/mod.rs index 6ed15c1..f5eff84 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -16,6 +16,8 @@ pub type WindowCanvas = Canvas; use crate::app::app_state::AppState; use crate::app::config::Config; +use crate::themes::Theme; +use crate::ui::*; use crate::renderer::Renderer; #[derive(PartialEq, Clone, Debug)] @@ -71,12 +73,13 @@ impl Application { let mut timer: TimerSubsystem = self.sdl_context.timer().unwrap(); let mut event_pump = self.sdl_context.event_pump().unwrap(); let font_context = sdl2::ttf::init().unwrap(); + let texture_creator = self.canvas.texture_creator(); let sleep_time = Duration::new(0, 1_000_000_000u32 / 60); let mut app_state = AppState::new(); let mut renderer = Renderer::new( self.config.clone(), &font_context, - self.canvas.texture_creator() + &texture_creator ); 'running: loop { @@ -89,14 +92,7 @@ impl Application { match task { Task::OpenFile { file_path } => { use crate::file::editor_file::*; - app_state.open_file(file_path.clone(), &mut renderer); -// use std::fs::read_to_string; -// if let Ok(buffer) = read_to_string(&file_path) { -// println!("read: {}\n{}", file_path, buffer); -// let file = EditorFile::new(file_path.clone(), buffer, &mut renderer); -// app_state.current_file = app_state.files.len() as i16; -// app_state.files.push(file); -// } + app_state.open_file(file_path.clone()); }, } } diff --git a/src/file/caret.rs b/src/file/caret.rs deleted file mode 100644 index e7b567b..0000000 --- a/src/file/caret.rs +++ /dev/null @@ -1,19 +0,0 @@ -pub struct Caret<'a> { - character: char, - source: Rect, - dest: Rect, - visible: bool, - texture: Option>>, -} - -impl<'a> Caret<'a> { - pub fn new() -> Self { - Self { - character: '│', - source: Rect::new(0, 0, 0, 0), - dest: Rect::new(0, 0, 0, 0), - visible: true, - texture: None, - } - } -} diff --git a/src/file/editor_file.rs b/src/file/editor_file.rs index c441d0f..d7790cb 100644 --- a/src/file/editor_file.rs +++ b/src/file/editor_file.rs @@ -1,41 +1,22 @@ use sdl2::rect::Rect; use crate::file::editor_file_section::EditorFileSection; use crate::renderer::Renderer; -use crate::app::UpdateResult; -use crate::app::WindowCanvas; +use crate::app::{UpdateResult, WindowCanvas}; +use crate::ui::*; #[derive(Clone)] -pub struct EditorFile<'l> { +pub struct EditorFile { pub path: String, - pub sections: Vec>, + pub sections: Vec, } -impl<'l> EditorFile<'l> { - pub fn new(path: String, buffer: String, renderer: &'l mut Renderer) -> Self { - let section = EditorFileSection::new(buffer, renderer); +impl EditorFile { + pub fn new(path: String, buffer: String) -> Self { + let section = EditorFileSection::new(buffer); let sections = vec![section]; Self { path, sections } } - pub fn update(&mut self, ticks: i32) -> UpdateResult { - let mut result = UpdateResult::NoOp; - for section in self.sections.iter_mut() { - result = section.update(ticks); - } - - if result == UpdateResult::RefreshPositions { - self.refresh_characters_position(); - result = UpdateResult::NoOp; - } - result - } - - pub fn render(&self, canvas: &mut WindowCanvas, renderer: &mut Renderer) { - for ref section in self.sections.iter() { - section.render(canvas, renderer); - } - } - fn refresh_characters_position(&mut self) { let mut current: Rect = Rect::new(0, 0, 0, 0); for section in self.sections.iter_mut() { @@ -43,3 +24,29 @@ impl<'l> EditorFile<'l> { } } } + +impl Render for EditorFile { + fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult { + let mut res = UpdateResult::NoOp; + for section in self.sections.iter_mut() { + res = section.render(canvas, renderer); + } + if res == UpdateResult::RefreshPositions { + self.refresh_characters_position(); + for section in self.sections.iter_mut() { + section.render(canvas, renderer); + } + } + UpdateResult::NoOp + } +} + +impl Update for EditorFile { + fn update(&mut self, ticks: i32) -> UpdateResult { + let mut result = UpdateResult::NoOp; + for section in self.sections.iter_mut() { + result = section.update(ticks); + } + result + } +} diff --git a/src/file/editor_file_section.rs b/src/file/editor_file_section.rs index 6e0766b..b4d9159 100644 --- a/src/file/editor_file_section.rs +++ b/src/file/editor_file_section.rs @@ -4,48 +4,54 @@ use crate::app::UpdateResult; use crate::app::WindowCanvas; use crate::renderer::Renderer; use crate::file::editor_file_token::EditorFileToken; +use crate::ui::*; #[derive(Clone)] -pub struct EditorFileSection<'l> { - pub tokens: Vec>, +pub struct EditorFileSection { + pub tokens: Vec, pub language: Language, } -impl<'l> EditorFileSection<'l> { - pub fn new(buffer: String, renderer: &'l mut Renderer) -> Self { +impl EditorFileSection { + pub fn new(buffer: String) -> Self { use crate::lexer; let lexer_tokens = lexer::parse(buffer.clone(), Language::PlainText); let mut tokens: Vec = vec![]; for token_type in lexer_tokens { - let token = EditorFileToken::new( - renderer, - token_type.get_start(), - token_type.clone(), - ); + let token = EditorFileToken::new(token_type); tokens.push(token.clone()); } let language = Language::PlainText; Self { tokens, language } } - pub fn update(&mut self, ticks: i32) -> UpdateResult { - let mut result = UpdateResult::NoOp; - for file_char in self.tokens.iter_mut() { - result = file_char.update(ticks) - } - result - } - - pub fn render(&self, canvas: &mut WindowCanvas, renderer: &mut Renderer) { - for ref character in self.tokens.iter() { - character.render(canvas, renderer); - } - } - pub fn update_positions(&mut self, current: &mut Rect) { for c in self.tokens.iter_mut() { c.update_position(current); } } } + +impl Render for EditorFileSection { + fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult { + let mut res = UpdateResult::NoOp; + for character in self.tokens.iter_mut() { + let r = character.render(canvas, renderer); + if res == UpdateResult::NoOp { + res = r; + } + } + res + } +} + +impl Update for EditorFileSection { + fn update(&mut self, ticks: i32) -> UpdateResult { + let mut result = UpdateResult::NoOp; + for file_char in self.tokens.iter_mut() { + result = file_char.update(ticks) + } + result + } +} diff --git a/src/file/editor_file_token.rs b/src/file/editor_file_token.rs index 23abe38..05c3894 100644 --- a/src/file/editor_file_token.rs +++ b/src/file/editor_file_token.rs @@ -2,116 +2,90 @@ use std::rc::Rc; use sdl2::rect::Rect; use sdl2::render::Texture; use sdl2::ttf::Font; +use sdl2::pixels::Color; + use crate::lexer::TokenType; use crate::renderer::Renderer; use crate::renderer::managers::TextDetails; -use crate::renderer::resolve_color::resolve_color; -use crate::app::UpdateResult; -use crate::app::WindowCanvas; +use crate::app::{UpdateResult, WindowCanvas}; use crate::renderer::managers::FontDetails; +use crate::ui::*; +use crate::ui::text_character::*; #[derive(Clone)] -pub struct EditorFileToken<'l> { - pos: usize, - text: String, - font_size: u16, +pub struct TextCharacterMeasure { source: Rect, dest: Rect, - token_type: TokenType, - texture: Option>>, } -impl<'l> EditorFileToken<'l> { - pub fn new(renderer: &'l mut Renderer, pos: usize, token_type: TokenType) -> Self { - let c = match token_type { - _ if token_type.is_space() => "°".to_string(), - _ if token_type.is_new_line() => "\n".to_string(), - TokenType::Whitespace { .. } => "°".to_string(), - _ => token_type.get_text(), - }; - let details = TextDetails { - text: c.clone(), - font_details: FontDetails::new( - renderer.config.editor_config.font_path.as_str(), - renderer.config.editor_config.character_size.clone(), - ), - color: resolve_color(&token_type), - }; +#[derive(Clone)] +pub struct EditorFileToken { + characters: Vec, + token_type: TokenType, +} + +impl Into for TokenType { + fn into(self) -> Color { + match &self { + &TokenType::Whitespace { .. } => Color::RGBA(220, 220, 220, 90), + _ => Color::RGBA(0, 0, 0, 0), + } + } +} + +impl EditorFileToken { + pub fn new(token_type: TokenType) -> Self { Self { - pos, - text: c, - font_size: 0, - source: Rect::new(0, 0, 0, 0), - dest: Rect::new(0, 0, 0, 0), + characters: vec![], token_type, - texture: renderer.render_text(details).clone(), - } - } - - pub fn update(&mut self, _ticks: i32) -> UpdateResult { -// if self.font_size != config.editor_config.character_size { -// self.update_view(renderer); -// return UpdateResult::RefreshPositions; -// } - UpdateResult::NoOp - } - - pub fn render(&self, canvas: &mut WindowCanvas, renderer: &mut Renderer) { - if self.token_type.is_new_line() { - return; - } - match &self.texture { - Some(texture) => { - renderer.render_texture(canvas, &texture, &self.source, &self.dest) - } - _ => {} } } pub fn update_position(&mut self, current: &mut Rect) { - match self.token_type { - _ if self.token_type.is_new_line() => { - current.set_x(0); - current.set_y( - (self.token_type.line() as usize * self.source.height() as usize) as i32, - ); - } - _ => { - self.dest.set_x(current.x()); - self.dest.set_y(current.y()); - self.dest.set_width(self.source.width()); - self.dest.set_height(self.source.height()); - current.set_x(self.dest.x() + self.source.width() as i32); - } - }; - } - - fn update_view(&mut self, renderer: &mut Renderer) { - self.font_size = renderer.config.editor_config.character_size.clone(); - let font_details = FontDetails::new( - renderer.config.editor_config.font_path.as_str(), - self.font_size.clone(), - ); - - if let Ok(font) = renderer.font_manager.load(&font_details) { - if let Some((width, height)) = self.measure_text(&font) { - self.source.set_width(width as u32); - self.source.set_height(height as u32); - } - }; - } - - fn measure_text(&self, font: &Rc) -> Option<(usize, usize)> { - let mut w: usize = 0; - let mut h: usize = 0; - for c in self.text.chars() { - if let Ok((width, height)) = font.size_of_char(c) { - w += width as usize; - h = height as usize; - } else { - return None; - } + for text_character in self.characters.iter_mut() { + text_character.update_position(current); } - Some((w, h)) + } + + fn update_view(&mut self, renderer: &mut Renderer) -> UpdateResult { + for c in self.token_type.text().chars() { + let mut text_character = TextCharacter::new( + c.clone(), + self.token_type.line(), + self.token_type.clone().into(), + ); + text_character.update_view(renderer); + self.characters.push(text_character); + } + + UpdateResult::RefreshPositions + } +} + +impl Render for EditorFileToken { + /** + * Must first create targets so even if new line appear renderer will know + * where move render starting point + */ + fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult { + if self.characters.is_empty() { + return self.update_view(renderer); + } + if self.token_type.is_new_line() { + return UpdateResult::NoOp; + } + for text_character in self.characters.iter_mut() { + text_character.render(canvas, renderer); + } + UpdateResult::NoOp + } +} + +impl Update for EditorFileToken { + fn update(&mut self, ticks: i32) -> UpdateResult { + for text_character in self.characters.iter_mut() { + text_character.update(ticks); + } + UpdateResult::NoOp } } diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 2c29e3a..18b4fb3 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -14,6 +14,10 @@ pub enum TokenType { String { token: Token }, Number { token: Token }, Identifier { token: Token }, + Literal { token: Token }, + Comment { token: Token }, + Operator { token: Token }, + Separator { token: Token }, } impl TokenType { @@ -34,42 +38,34 @@ impl TokenType { TokenType::Identifier { token } => TokenType::Identifier { token: token.move_to(line, character, start, end), }, + TokenType::Literal { token } => TokenType::Literal { + token: token.move_to(line, character, start, end), + }, + TokenType::Comment { token } => TokenType::Comment { + token: token.move_to(line, character, start, end), + }, + TokenType::Operator { token } => TokenType::Operator { + token: token.move_to(line, character, start, end), + }, + TokenType::Separator { token } => TokenType::Separator { + token: token.move_to(line, character, start, end), + }, } } pub fn is_new_line(&self) -> bool { match self { - TokenType::Whitespace { token } => token.text() == "\n".to_string(), + TokenType::Whitespace { token } => token.text().as_str() == "\n", _ => false, } } pub fn is_space(&self) -> bool { match self { - TokenType::Whitespace { token } => token.text() == " ".to_string(), + TokenType::Whitespace { token } => token.text().as_str() == " ", _ => false, } } - - pub fn get_start(&self) -> usize { - match self { - TokenType::Whitespace { token } => token.start(), - TokenType::Keyword { token } => token.start(), - TokenType::String { token } => token.start(), - TokenType::Number { token } => token.start(), - TokenType::Identifier { token } => token.start(), - } - } - - pub fn get_text(&self) -> String { - match self { - TokenType::Whitespace { token } => token.text(), - TokenType::Keyword { token } => token.text(), - TokenType::String { token } => token.text(), - TokenType::Number { token } => token.text(), - TokenType::Identifier { token } => token.text(), - } - } } impl Deref for TokenType { @@ -82,6 +78,10 @@ impl Deref for TokenType { TokenType::String { token } => token, TokenType::Number { token } => token, TokenType::Identifier { token } => token, + TokenType::Literal { token } => token, + TokenType::Comment { token } => token, + TokenType::Operator { token } => token, + TokenType::Separator { token } => token, } } } @@ -112,8 +112,8 @@ impl Token { } } - pub fn text(&self) -> String { - self.text.clone() + pub fn text(&self) -> &String { + &self.text } pub fn line(&self) -> usize { @@ -146,7 +146,7 @@ impl Token { pub fn parse(text: String, language: Language) -> Vec { match language { Language::PlainText => plain::lexer::Lexer::new(text.as_str()) - .inspect(|tok| eprintln!("tok: {:?}", tok)) + .inspect(|tok| println!("tok: {:?}", tok)) .map(|t| t.0) .collect(), } diff --git a/src/main.rs b/src/main.rs index 29fa1ae..e21f994 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,22 @@ #![allow(unused_imports)] -#![feature(use_extern_macros)] extern crate plex; extern crate rand; extern crate sdl2; +extern crate dirs; +#[macro_use] +extern crate serde; +#[macro_use] +extern crate serde_json; +#[macro_use] +extern crate serde_derive; pub mod app; +pub mod ui; pub mod file; pub mod lexer; pub mod renderer; +pub mod themes; use crate::app::Application; diff --git a/src/renderer/managers.rs b/src/renderer/managers.rs index 402a359..af75dc0 100644 --- a/src/renderer/managers.rs +++ b/src/renderer/managers.rs @@ -26,16 +26,15 @@ pub struct FontDetails { pub struct TextDetails { pub text: String, pub color: Color, - pub font_details: FontDetails, + pub font: FontDetails, } impl TextDetails { pub fn get_cache_key(&self) -> String { format!( "text({}) size({}) {:?}", - self.text, self.font_details.size, self.color - ) - .to_string() + self.text, self.font.size, self.color + ).to_string() } } @@ -44,7 +43,7 @@ impl<'a> From<&'a TextDetails> for TextDetails { Self { text: details.text.clone(), color: details.color.clone(), - font_details: details.font_details.clone(), + font: details.font.clone(), } } } @@ -72,28 +71,28 @@ pub type FontManager<'l> = ResourceManager<'l, FontDetails, Font<'l, 'static>, S #[derive(Clone)] pub struct ResourceManager<'l, K, R, L> -where - K: Hash + Eq, - L: 'l + ResourceLoader<'l, R>, + where + K: Hash + Eq, + L: 'l + ResourceLoader<'l, R>, { loader: &'l L, cache: HashMap>, } impl<'l, K, R, L> ResourceManager<'l, K, R, L> -where - K: Hash + Eq, - L: ResourceLoader<'l, R>, + where + K: Hash + Eq, + L: ResourceLoader<'l, R>, { pub fn new(loader: &'l L) -> Self { Self { cache: HashMap::new(), loader } } pub fn load(&mut self, details: &D) -> Result, String> - where - L: ResourceLoader<'l, R, Args = D>, - D: Eq + Hash + ?Sized, - K: Borrow + for<'a> From<&'a D>, + where + L: ResourceLoader<'l, R, Args=D>, + D: Eq + Hash + ?Sized, + K: Borrow + for<'a> From<&'a D>, { self.cache.get(details).cloned().map_or_else( || { @@ -104,6 +103,10 @@ where Ok, ) } + + pub fn loader(&self) -> &L { + self.loader + } } impl<'l, T> ResourceLoader<'l, Texture<'l>> for TextureCreator { @@ -124,25 +127,25 @@ impl<'l> ResourceLoader<'l, Font<'l, 'static>> for Sdl2TtfContext { } } -//impl<'l, T> TextureManager<'l, T> { -// pub fn load_text( -// &mut self, -// details: &mut TextDetails, -// font: &Font, -// ) -> Result>, String> { -// let key = details.get_cache_key(); -// self.cache.get(key.as_str()).cloned().map_or_else( -// || { -// let surface = font -// .render(details.text.as_str()) -// .blended(details.color) -// .unwrap(); -// let texture = self.loader.create_texture_from_surface(&surface).unwrap(); -// let resource = Rc::new(texture); -// self.cache.insert(key, resource.clone()); -// Ok(resource) -// }, -// Ok, -// ) -// } -//} +impl<'l, T> TextureManager<'l, T> { + pub fn load_text( + &mut self, + details: &mut TextDetails, + font: &Rc, + ) -> Result>, String> { + let key = details.get_cache_key(); + self.cache.get(key.as_str()).cloned().map_or_else( + || { + let surface = font + .render(details.text.as_str()) + .blended(details.color) + .unwrap(); + let texture = self.loader.create_texture_from_surface(&surface).unwrap(); + let resource = Rc::new(texture); + self.cache.insert(key, resource.clone()); + Ok(resource) + }, + Ok, + ) + } +} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index fb8b8f1..111c9a2 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -1,5 +1,4 @@ pub mod managers; -pub mod resolve_color; use crate::renderer::managers::{FontManager, TextureManager}; @@ -18,7 +17,6 @@ pub struct Renderer<'a> { pub config: Config, pub font_manager: FontManager<'a>, pub texture_manager: TextureManager<'a, WindowContext>, - pub texture_creator: TextureCreator, pub scroll: Point, } @@ -26,13 +24,12 @@ impl<'a> Renderer<'a> { pub fn new( config: Config, font_context: &'a Sdl2TtfContext, - texture_creator: TextureCreator, + texture_creator: &'a TextureCreator, ) -> Self { Self { config, font_manager: FontManager::new(&font_context), texture_manager: TextureManager::new(&texture_creator), - texture_creator, scroll: (0, 0).into(), } } @@ -52,7 +49,7 @@ impl<'a> Renderer<'a> { } pub fn render_text(&mut self, details: TextDetails) -> Option> { - let font = self.font_manager.load(&details.font_details).unwrap(); + let font = self.font_manager.load(&details.font).unwrap(); let surface = font .render(details.text.as_str()) .blended(details.color); @@ -62,7 +59,8 @@ impl<'a> Renderer<'a> { return None; }; let texture = self - .texture_creator + .texture_manager + .loader() .create_texture_from_surface(&surface); let texture = if let Ok(t) = texture { Rc::new(t) diff --git a/src/renderer/resolve_color.rs b/src/renderer/resolve_color.rs deleted file mode 100644 index 7b3fc65..0000000 --- a/src/renderer/resolve_color.rs +++ /dev/null @@ -1,10 +0,0 @@ -use sdl2::pixels::Color; - -use crate::lexer::TokenType; - -pub fn resolve_color(token_type: &TokenType) -> Color { - match token_type { - &TokenType::Whitespace { .. } => Color::RGBA(220, 220, 220, 90), - _ => Color::RGBA(0, 0, 0, 0), - } -} diff --git a/src/themes/mod.rs b/src/themes/mod.rs new file mode 100644 index 0000000..4592987 --- /dev/null +++ b/src/themes/mod.rs @@ -0,0 +1,376 @@ +use std::fs; +use std::path::PathBuf; +use std::env; +use sdl2::pixels::Color; +use serde_json; +use serde::ser::{Serialize, Serializer, SerializeSeq, SerializeMap}; + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +pub struct SerdeColor { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8 +} + +impl SerdeColor { + pub fn new(r: u8,g: u8,b: u8,a: u8) -> Self { + Self { r,g,b,a } + } +} + +impl Into for SerdeColor { + fn into(self) -> Color { + Color { + r: self.r, + g: self.g, + b: self.b, + a: self.a + } + } +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +pub struct ThemeConfig { + color: SerdeColor, + italic: bool, + bold: bool, +} + +impl ThemeConfig { + pub fn color(&self) -> &SerdeColor { + &self.color + } + + pub fn italic(&self) -> bool { + self.italic + } + + pub fn bold(&self) -> bool { + self.bold + } +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +pub struct CaretColor { + bright: ThemeConfig, + blur: ThemeConfig, +} + +impl Default for CaretColor { + fn default() -> Self { + Self { + bright: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + italic: false, + bold: false, + }, + blur: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + italic: false, + bold: false, + }, + } + } +} + +impl CaretColor { + pub fn bright(&self) -> &ThemeConfig { + &self.bright + } + + pub fn blur(&self) -> &ThemeConfig { + &self.blur + } +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +pub struct CodeHighlightingColor { + whitespace: ThemeConfig, + keyword: ThemeConfig, + string: ThemeConfig, + number: ThemeConfig, + identifier: ThemeConfig, + literal: ThemeConfig, + comment: ThemeConfig, + operator: ThemeConfig, + separator: ThemeConfig, +} + +impl Default for CodeHighlightingColor { + fn default() -> Self { + Self { + whitespace: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + bold: false, + italic: false, + }, + keyword: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + bold: false, + italic: false, + }, + string: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + bold: false, + italic: false, + }, + number: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + bold: false, + italic: false, + }, + identifier: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + bold: false, + italic: false, + }, + literal: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + bold: false, + italic: false, + }, + comment: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + bold: false, + italic: false, + }, + operator: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + bold: false, + italic: false, + }, + separator: ThemeConfig { + color: SerdeColor::new(0, 0, 0, 0), + bold: false, + italic: false, + }, + } + } +} + +impl CodeHighlightingColor { + pub fn whitespace(&self) -> &ThemeConfig { + &self.whitespace + } + + pub fn keyword(&self) -> &ThemeConfig { + &self.keyword + } + + pub fn string(&self) -> &ThemeConfig { + &self.string + } + + pub fn number(&self) -> &ThemeConfig { + &self.number + } + + pub fn identifier(&self) -> &ThemeConfig { + &self.identifier + } + + pub fn literal(&self) -> &ThemeConfig { + &self.literal + } + + pub fn comment(&self) -> &ThemeConfig { + &self.comment + } + + pub fn operator(&self) -> &ThemeConfig { + &self.operator + } + + pub fn separator(&self) -> &ThemeConfig { + &self.separator + } +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +pub struct Theme { + name: String, + background: SerdeColor, + caret: CaretColor, + code_highlighting: CodeHighlightingColor, +} + +impl Default for Theme { + fn default() -> Self { + Self { + name: "Default".to_string(), + background: SerdeColor::new(255, 255, 255, 0), + caret: CaretColor::default(), + code_highlighting: CodeHighlightingColor::default(), + } + } +} + +impl Theme { + pub fn name(&self) -> &String { + &self.name + } + + pub fn background(&self) -> &SerdeColor { + &self.background + } + + pub fn caret(&self) -> &CaretColor { + &self.caret + } + + pub fn code_highlighting(&self) -> &CodeHighlightingColor { + &self.code_highlighting + } + + fn railscasts() -> Self { + Self { + name: "railscasts".to_string(), + background: SerdeColor { + r: 60, + g: 60, + b: 60, + a: 0 + }, + caret: CaretColor { bright: ThemeConfig { + color: SerdeColor { + r: 0, + g: 0, + b: 0, + a: 0 + }, + italic: false, + bold: false + }, blur: ThemeConfig { + color: SerdeColor { + r: 0, + g: 0, + b: 0, + a: 0 + }, + italic: false, + bold: false + } }, + code_highlighting: CodeHighlightingColor { + whitespace: ThemeConfig { + color: SerdeColor { + r: 0, + g: 0, + b: 0, + a: 0 + }, + italic: false, + bold: false + }, + keyword: ThemeConfig { + color: SerdeColor { + r: 203, + g: 120, + b: 50, + a: 0 + }, + italic: false, + bold: true + }, + string: ThemeConfig { + color: SerdeColor { + r: 0, + g: 0, + b: 0, + a: 0 + }, + italic: false, + bold: false + }, + number: ThemeConfig { + color: SerdeColor { + r: 0, + g: 0, + b: 0, + a: 0 + }, + italic: false, + bold: false + }, + identifier: ThemeConfig { + color: SerdeColor { + r: 0, + g: 0, + b: 0, + a: 0 + }, + italic: false, + bold: false + }, + literal: ThemeConfig { + color: SerdeColor { + r: 0, + g: 0, + b: 0, + a: 0 + }, + italic: false, + bold: false + }, + comment: ThemeConfig { + color: SerdeColor { + r: 188, + g: 147, + b: 88, + a: 0 + }, + italic: true, + bold: false + }, + operator: ThemeConfig { + color: SerdeColor { + r: 0, + g: 0, + b: 0, + a: 0 + }, + italic: false, + bold: false + }, + separator: ThemeConfig { + color: SerdeColor { + r: 0, + g: 0, + b: 0, + a: 0 + }, + italic: false, + bold: false + } + } + } + } + + pub fn load(_theme_name: String) -> Self { + use dirs; + let home_dir = dirs::config_dir().unwrap(); + let mut config_dir = home_dir.clone(); + config_dir.push("rider/themes"); + fs::create_dir_all(&config_dir) + .unwrap_or_else(|_| panic!("Cannot create config directory")); + let theme = Self::load_content(&config_dir, "default.json"); + println!("theme config:\n{:?}", theme); + theme + } + + fn load_content(config_dir: &PathBuf, file_name: &str) -> Theme { + let mut config_file = config_dir.clone(); + config_file.push(file_name); + let contents = match fs::read_to_string(&config_file) { + Ok(s) => s, + Err(_) => { + let contents = serde_json::to_string_pretty(&Theme::default()) + .unwrap(); + fs::write(&config_file, contents.clone()) + .unwrap_or_else(|_| panic!("Failed to crate theme config file")); + contents.to_string() + } + }; + serde_json::from_str(&contents).unwrap_or_default() + } +} diff --git a/src/ui/caret.rs b/src/ui/caret.rs new file mode 100644 index 0000000..365baf1 --- /dev/null +++ b/src/ui/caret.rs @@ -0,0 +1,68 @@ +use sdl2::rect::Rect; +use sdl2::render::Texture; +use sdl2::pixels::Color; +use crate::app::{WindowCanvas, UpdateResult}; +use crate::ui::*; +use crate::ui::text_character::TextCharacter; +use crate::renderer::Renderer; + +const CARET_CHARACTER: char = '│'; + +#[derive(Clone, Debug, PartialEq)] +enum CaretState { + Bright, + Blur, +} + +#[derive(Clone)] +pub struct Caret { + state: CaretState, + bright_character: TextCharacter, + blur_character: TextCharacter, + blink_delay: u8, +} + +impl Caret { + pub fn new() -> Self { + Self { + bright_character: TextCharacter::new(CARET_CHARACTER, 0, Color::RGBA(0, 0, 0, 0)), + blur_character: TextCharacter::new(CARET_CHARACTER, 0, Color::RGBA(100, 100, 100, 0)), + state: CaretState::Bright, + blink_delay: 0, + } + } + + fn toggle_state(&mut self) { + self.state = if self.state == CaretState::Bright { + CaretState::Blur + } else { + CaretState::Bright + }; + } +} + +impl Render for Caret { + fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult { + match self.state { + CaretState::Bright => { + self.bright_character.update_position(&mut Rect::new(100, 220, 0, 0)); + self.bright_character.render(canvas, renderer) + }, + CaretState::Blur => { + self.blur_character.update_position(&mut Rect::new(100, 220, 0, 0)); + self.blur_character.render(canvas, renderer) + }, + } + } +} + +impl Update for Caret { + fn update(&mut self, _ticks: i32) -> UpdateResult { + self.blink_delay += 1; + if self.blink_delay >= 30 { + self.blink_delay = 0; + self.toggle_state(); + } + UpdateResult::NoOp + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs new file mode 100644 index 0000000..05cbd0f --- /dev/null +++ b/src/ui/mod.rs @@ -0,0 +1,13 @@ +pub mod caret; +pub mod text_character; + +use crate::renderer::Renderer; +use crate::app::{WindowCanvas,UpdateResult}; + +pub trait Render { + fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult; +} + +pub trait Update { + fn update(&mut self, ticks: i32) -> UpdateResult; +} diff --git a/src/ui/text_character.rs b/src/ui/text_character.rs new file mode 100644 index 0000000..2b890fe --- /dev/null +++ b/src/ui/text_character.rs @@ -0,0 +1,141 @@ +use std::rc::Rc; +use sdl2::rect::Rect; +use sdl2::render::Texture; +use sdl2::ttf::Font; +use sdl2::pixels::Color; + +use crate::lexer::TokenType; +use crate::renderer::Renderer; +use crate::renderer::managers::TextDetails; +use crate::app::{UpdateResult, WindowCanvas}; +use crate::renderer::managers::FontDetails; +use crate::ui::*; + +#[derive(Clone)] +pub struct TextCharacter { + pending: bool, + text_character: char, + line: usize, + source: Rect, + dest: Rect, + color: Color, +} + +impl TextCharacter { + pub fn new(text_character: char, line: usize, color: Color) -> Self { + Self { + pending: true, + text_character, + line, + source: Rect::new(0, 0, 0, 0), + dest: Rect::new(0, 0, 0, 0), + color, + } + } + + pub fn dest(&self) -> &Rect { + &self.dest + } + + pub fn source(&self) -> &Rect { + &self.source + } + + pub fn color(&self) -> &Color { + &self.color + } + + pub fn update_position(&mut self, current: &mut Rect) { + if self.is_new_line() { + let y = (self.line * self.source.height() as usize) as i32; + current.set_x(0); + current.set_y(y); + } else { + self.dest.set_x(current.x()); + self.dest.set_y(current.y()); + self.dest.set_width(self.source.width()); + self.dest.set_height(self.source.height()); + current.set_x(self.dest.x() + self.source.width() as i32); + } + } + + pub fn update_view(&mut self, renderer: &mut Renderer) -> UpdateResult { + let config = &renderer.config.editor_config; + let font_details = FontDetails::new( + config.font_path.as_str(), + config.character_size.clone(), + ); + let font = renderer.font_manager + .load(&font_details) + .unwrap_or_else(|_| panic!("Font not found {:?}", font_details)); + + let c = self.text_character.clone(); + if let Ok((width, height)) = font.size_of_char(c) { + self.source = Rect::new(0, 0, width, height); + self.dest = Rect::new(0, 0, width, height); + } + let mut details = TextDetails { + text: c.to_string(), + color: self.color.clone(), + font: font_details.clone(), + }; + renderer.texture_manager + .load_text(&mut details, &font) + .unwrap_or_else(|_| panic!("Could not create texture for {:?}", c)); + println!("texture for '{}' created", self.text_character); + + self.pending = false; + UpdateResult::RefreshPositions + } + + #[inline] + fn is_new_line(&self) -> bool { + self.text_character == '\n' + } + + #[inline] + fn is_pending(&self) -> bool { + self.pending + } +} + +impl Render for TextCharacter { + /** + * Must first create targets so even if new line appear renderer will know + * where move render starting point + */ + fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult { + if self.is_pending() { + return self.update_view(renderer); + } + if self.is_new_line() { + return UpdateResult::NoOp; + } + + let config = &renderer.config.editor_config; + let font_details = FontDetails::new( + config.font_path.as_str(), + config.character_size.clone(), + ); + let font = renderer.font_manager + .load(&font_details) + .unwrap_or_else(|_| panic!("Could not load font for {:?}", font_details)); + + let c = self.text_character.clone(); + let mut details = TextDetails { + text: c.to_string(), + color: self.color.clone(), + font: font_details.clone(), + }; + if let Ok(texture) = renderer.texture_manager.load_text(&mut details, &font) { + renderer.render_texture(canvas, &texture, &self.source, &self.dest); + } + UpdateResult::NoOp + } +} + +impl Update for TextCharacter { + fn update(&mut self, _ticks: i32) -> UpdateResult { + UpdateResult::NoOp + } +}