├── .github └── FUNDING.yml ├── .gitignore ├── .vscode └── tasks.json ├── Cargo.toml ├── License.txt ├── Readme.md ├── code_editor ├── Cargo.lock ├── Cargo.toml ├── Readme.md └── src │ ├── codeeditor.rs │ ├── draw2d.rs │ ├── error.rs │ ├── lib.rs │ ├── scanner.rs │ ├── settings.rs │ ├── theme.rs │ └── undo.rs ├── example_app ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── fonts └── Source_Code_Pro │ ├── OFL.txt │ ├── README.txt │ ├── SourceCodePro-Italic-VariableFont_wght.ttf │ ├── SourceCodePro-VariableFont_wght.ttf │ └── static │ ├── SourceCodePro-Black.ttf │ ├── SourceCodePro-BlackItalic.ttf │ ├── SourceCodePro-Bold.ttf │ ├── SourceCodePro-BoldItalic.ttf │ ├── SourceCodePro-ExtraBold.ttf │ ├── SourceCodePro-ExtraBoldItalic.ttf │ ├── SourceCodePro-ExtraLight.ttf │ ├── SourceCodePro-ExtraLightItalic.ttf │ ├── SourceCodePro-Italic.ttf │ ├── SourceCodePro-Light.ttf │ ├── SourceCodePro-LightItalic.ttf │ ├── SourceCodePro-Medium.ttf │ ├── SourceCodePro-MediumItalic.ttf │ ├── SourceCodePro-Regular.ttf │ ├── SourceCodePro-SemiBold.ttf │ └── SourceCodePro-SemiBoldItalic.ttf └── images └── screenshot.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: markusmoenig 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | target 4 | example_app/target 5 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "cargo run", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "run", 10 | "--release", 11 | ], 12 | "group": { 13 | "kind": "build", 14 | "isDefault": true 15 | } 16 | }, 17 | { 18 | "label": "cargo build", 19 | "type": "shell", 20 | "command": "cargo build", 21 | "args": [], 22 | "group": { 23 | "kind": "build", 24 | "isDefault": true 25 | } 26 | }, 27 | ] 28 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "code_editor", 5 | "example_app", 6 | ] 7 | 8 | edition = "2021" 9 | resolver = "2" -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | Copyright 2022 Markus Moenig 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/) [![version](https://img.shields.io/badge/version-0.3.4-red.svg)](https://shields.io/) [![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/markusmoenig) 2 | 3 | # CodeEditor 4 | 5 | A standalone code (and text) editor for people like me who write their own user interfaces utilizing crates like [pixels](https://github.com/parasyte/pixels). 6 | 7 | CodeEditor renders its display into a ```Vec``` and is completely independent from any UI crate. It utilizes [fontdue](https://github.com/mooman219/fontdue) for rendering fonts. 8 | 9 | ## Example App 10 | 11 | A standalone ```pixels``` and ```winit``` based example app is included in this repo. 12 | 13 | ![Screenshot](images/screenshot.png) 14 | 15 | ## Usage 16 | 17 | Add code_editor to your Cargo.toml 18 | 19 | ``` 20 | code_editor = "0.3.4" 21 | ``` 22 | 23 | And than in your app 24 | 25 | ```rust 26 | use code_editor::prelude::*; 27 | 28 | let mut code_editor = CodeEditor::new(); 29 | code_editor.set_font("fonts/Source_Code_Pro/static/SourceCodePro-Regular.ttf"); 30 | code_editor.set_mode(CodeEditorMode::Rhai); 31 | code_editor.set_font_size(17.0); 32 | 33 | code_editor.set_text("Your source code".to_string()); 34 | ``` 35 | 36 | In your draw loop you can than draw the editor 37 | 38 | ```rust 39 | code_editor.draw(frame, (0, 0, width, height), width); 40 | ``` 41 | 42 | The second parameter is the drawing rectangle into your frame, the last parameter is the stride in pixels. 43 | 44 | You can get the edited text via ```get_text()```. You will also need to connect mouse and keyboard events to the code editor, see the example app. There are also slots for ```cut```, ```copy```, ```paste```, ```undo``` and ```redo```. You will need to connect these in your app as well (the example app does not handle them). 45 | 46 | #### Syntax Highlighting 47 | 48 | The syntax highlighting is right now not configurable but is pretty universal. Supported modes are right now Rhai and Text (which has no highlighting). I will try to make the syntax highlighting more configurable in the future, in the meantime you can tweak the source code to your needs. 49 | 50 | #### Themes 51 | 52 | The default theme has this implementation: 53 | 54 | ```rust 55 | impl Theme { 56 | pub fn new() -> Self { 57 | Self { 58 | background : [34, 34, 36, 255], 59 | line_numbers : [160, 160, 160, 255], 60 | line_numbers_bg : [30, 30, 32, 255], 61 | 62 | text : [255, 255, 255, 255], 63 | cursor : [170, 170, 170, 255], 64 | 65 | identifier : [120, 214, 255, 255], 66 | number : [159, 197, 146, 255], 67 | keywords : [45, 133, 200, 255], 68 | brackets : [226, 73, 146, 212], 69 | comments : [69, 128, 56, 212], 70 | string : [197, 117, 92, 212], 71 | 72 | error : [237, 55, 54, 255], 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | You can tweak it in the theme property of the CodeEditor struct or you can set a new theme via the ```set_theme(theme);``` function. 79 | 80 | # Disclaimer 81 | 82 | CodeEditor is actively maintained and I will improve it over time as I use it for my own applications. -------------------------------------------------------------------------------- /code_editor/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "autocfg" 18 | version = "1.1.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 21 | 22 | [[package]] 23 | name = "bitflags" 24 | version = "1.3.2" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 27 | 28 | [[package]] 29 | name = "block" 30 | version = "0.1.6" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 33 | 34 | [[package]] 35 | name = "cfg-if" 36 | version = "1.0.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 39 | 40 | [[package]] 41 | name = "clipboard-win" 42 | version = "3.1.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" 45 | dependencies = [ 46 | "lazy-bytes-cast", 47 | "winapi", 48 | ] 49 | 50 | [[package]] 51 | name = "code_editor" 52 | version = "0.1.0" 53 | dependencies = [ 54 | "copypasta", 55 | "fontdue", 56 | ] 57 | 58 | [[package]] 59 | name = "copypasta" 60 | version = "0.8.1" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "d7216b5c1e9ad3867252505995b02d01c6fa7e6db0d8abd42634352ef377777e" 63 | dependencies = [ 64 | "clipboard-win", 65 | "objc", 66 | "objc-foundation", 67 | "objc_id", 68 | "smithay-clipboard", 69 | "x11-clipboard", 70 | ] 71 | 72 | [[package]] 73 | name = "dlib" 74 | version = "0.5.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" 77 | dependencies = [ 78 | "libloading", 79 | ] 80 | 81 | [[package]] 82 | name = "downcast-rs" 83 | version = "1.2.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 86 | 87 | [[package]] 88 | name = "fontdue" 89 | version = "0.7.2" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "6a62391ecb864cf12ed06b2af4eda2e609b97657950d6a8f06841b17726ab253" 92 | dependencies = [ 93 | "hashbrown", 94 | "ttf-parser", 95 | ] 96 | 97 | [[package]] 98 | name = "getrandom" 99 | version = "0.2.7" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 102 | dependencies = [ 103 | "cfg-if", 104 | "libc", 105 | "wasi", 106 | ] 107 | 108 | [[package]] 109 | name = "hashbrown" 110 | version = "0.11.2" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 113 | dependencies = [ 114 | "ahash", 115 | ] 116 | 117 | [[package]] 118 | name = "lazy-bytes-cast" 119 | version = "5.0.1" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" 122 | 123 | [[package]] 124 | name = "lazy_static" 125 | version = "1.4.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 128 | 129 | [[package]] 130 | name = "libc" 131 | version = "0.2.132" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" 134 | 135 | [[package]] 136 | name = "libloading" 137 | version = "0.7.3" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" 140 | dependencies = [ 141 | "cfg-if", 142 | "winapi", 143 | ] 144 | 145 | [[package]] 146 | name = "log" 147 | version = "0.4.17" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 150 | dependencies = [ 151 | "cfg-if", 152 | ] 153 | 154 | [[package]] 155 | name = "malloc_buf" 156 | version = "0.0.6" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 159 | dependencies = [ 160 | "libc", 161 | ] 162 | 163 | [[package]] 164 | name = "memchr" 165 | version = "2.5.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 168 | 169 | [[package]] 170 | name = "memmap2" 171 | version = "0.5.7" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" 174 | dependencies = [ 175 | "libc", 176 | ] 177 | 178 | [[package]] 179 | name = "memoffset" 180 | version = "0.6.5" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 183 | dependencies = [ 184 | "autocfg", 185 | ] 186 | 187 | [[package]] 188 | name = "minimal-lexical" 189 | version = "0.2.1" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 192 | 193 | [[package]] 194 | name = "nix" 195 | version = "0.24.2" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" 198 | dependencies = [ 199 | "bitflags", 200 | "cfg-if", 201 | "libc", 202 | "memoffset", 203 | ] 204 | 205 | [[package]] 206 | name = "nom" 207 | version = "7.1.1" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" 210 | dependencies = [ 211 | "memchr", 212 | "minimal-lexical", 213 | ] 214 | 215 | [[package]] 216 | name = "objc" 217 | version = "0.2.7" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 220 | dependencies = [ 221 | "malloc_buf", 222 | ] 223 | 224 | [[package]] 225 | name = "objc-foundation" 226 | version = "0.1.1" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" 229 | dependencies = [ 230 | "block", 231 | "objc", 232 | "objc_id", 233 | ] 234 | 235 | [[package]] 236 | name = "objc_id" 237 | version = "0.1.1" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" 240 | dependencies = [ 241 | "objc", 242 | ] 243 | 244 | [[package]] 245 | name = "once_cell" 246 | version = "1.13.1" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" 249 | 250 | [[package]] 251 | name = "pkg-config" 252 | version = "0.3.25" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 255 | 256 | [[package]] 257 | name = "proc-macro2" 258 | version = "1.0.43" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" 261 | dependencies = [ 262 | "unicode-ident", 263 | ] 264 | 265 | [[package]] 266 | name = "quick-xml" 267 | version = "0.22.0" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" 270 | dependencies = [ 271 | "memchr", 272 | ] 273 | 274 | [[package]] 275 | name = "quote" 276 | version = "1.0.21" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 279 | dependencies = [ 280 | "proc-macro2", 281 | ] 282 | 283 | [[package]] 284 | name = "scoped-tls" 285 | version = "1.0.0" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 288 | 289 | [[package]] 290 | name = "smallvec" 291 | version = "1.9.0" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" 294 | 295 | [[package]] 296 | name = "smithay-client-toolkit" 297 | version = "0.16.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" 300 | dependencies = [ 301 | "bitflags", 302 | "dlib", 303 | "lazy_static", 304 | "log", 305 | "memmap2", 306 | "nix", 307 | "pkg-config", 308 | "wayland-client", 309 | "wayland-cursor", 310 | "wayland-protocols", 311 | ] 312 | 313 | [[package]] 314 | name = "smithay-clipboard" 315 | version = "0.6.6" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" 318 | dependencies = [ 319 | "smithay-client-toolkit", 320 | "wayland-client", 321 | ] 322 | 323 | [[package]] 324 | name = "ttf-parser" 325 | version = "0.15.2" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" 328 | 329 | [[package]] 330 | name = "unicode-ident" 331 | version = "1.0.3" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" 334 | 335 | [[package]] 336 | name = "version_check" 337 | version = "0.9.4" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 340 | 341 | [[package]] 342 | name = "wasi" 343 | version = "0.11.0+wasi-snapshot-preview1" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 346 | 347 | [[package]] 348 | name = "wayland-client" 349 | version = "0.29.5" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" 352 | dependencies = [ 353 | "bitflags", 354 | "downcast-rs", 355 | "libc", 356 | "nix", 357 | "scoped-tls", 358 | "wayland-commons", 359 | "wayland-scanner", 360 | "wayland-sys", 361 | ] 362 | 363 | [[package]] 364 | name = "wayland-commons" 365 | version = "0.29.5" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" 368 | dependencies = [ 369 | "nix", 370 | "once_cell", 371 | "smallvec", 372 | "wayland-sys", 373 | ] 374 | 375 | [[package]] 376 | name = "wayland-cursor" 377 | version = "0.29.5" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" 380 | dependencies = [ 381 | "nix", 382 | "wayland-client", 383 | "xcursor", 384 | ] 385 | 386 | [[package]] 387 | name = "wayland-protocols" 388 | version = "0.29.5" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" 391 | dependencies = [ 392 | "bitflags", 393 | "wayland-client", 394 | "wayland-commons", 395 | "wayland-scanner", 396 | ] 397 | 398 | [[package]] 399 | name = "wayland-scanner" 400 | version = "0.29.5" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" 403 | dependencies = [ 404 | "proc-macro2", 405 | "quote", 406 | "xml-rs", 407 | ] 408 | 409 | [[package]] 410 | name = "wayland-sys" 411 | version = "0.29.5" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" 414 | dependencies = [ 415 | "dlib", 416 | "lazy_static", 417 | "pkg-config", 418 | ] 419 | 420 | [[package]] 421 | name = "winapi" 422 | version = "0.3.9" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 425 | dependencies = [ 426 | "winapi-i686-pc-windows-gnu", 427 | "winapi-x86_64-pc-windows-gnu", 428 | ] 429 | 430 | [[package]] 431 | name = "winapi-i686-pc-windows-gnu" 432 | version = "0.4.0" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 435 | 436 | [[package]] 437 | name = "winapi-x86_64-pc-windows-gnu" 438 | version = "0.4.0" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 441 | 442 | [[package]] 443 | name = "x11-clipboard" 444 | version = "0.6.1" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "6a7468a5768fea473e6c8c0d4b60d6d7001a64acceaac267207ca0281e1337e8" 447 | dependencies = [ 448 | "xcb", 449 | ] 450 | 451 | [[package]] 452 | name = "xcb" 453 | version = "1.1.1" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "b127bf5bfe9dbb39118d6567e3773d4bbc795411a8e1ef7b7e056bccac0011a9" 456 | dependencies = [ 457 | "bitflags", 458 | "libc", 459 | "quick-xml", 460 | ] 461 | 462 | [[package]] 463 | name = "xcursor" 464 | version = "0.3.4" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" 467 | dependencies = [ 468 | "nom", 469 | ] 470 | 471 | [[package]] 472 | name = "xml-rs" 473 | version = "0.8.4" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" 476 | -------------------------------------------------------------------------------- /code_editor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "code_editor" 3 | version = "0.3.9" 4 | edition = "2021" 5 | authors = ["Markus Moenig"] 6 | description = "A standalone code editor with syntax highlighting" 7 | license = "MIT" 8 | repository = "https://github.com/markusmoenig/code_editor.git" 9 | keywords = ["code", "editor", "syntax", "highlighting"] 10 | 11 | [dependencies] 12 | fontdue = "0.7.2" 13 | copypasta = "0.8.1" 14 | -------------------------------------------------------------------------------- /code_editor/Readme.md: -------------------------------------------------------------------------------- 1 | # CodeEditor 2 | 3 | A standalone code (and text) editor for people like me who write their own user interfaces utilizing crates like [pixels](https://github.com/parasyte/pixels). 4 | 5 | CodeEditor renders its display into a ```Vec``` and is completely independent from any UI crate. It utilizes [fontdue](https://github.com/mooman219/fontdue) for rendering fonts. 6 | 7 | ## Example App 8 | 9 | A standalone ```pixels``` and ```winit``` based example app is included in the [repository](https://github.com/markusmoenig/code_editor). 10 | 11 | ## Usage 12 | 13 | ```rust 14 | use code_editor::prelude::*; 15 | 16 | let mut code_editor = CodeEditor::new(); 17 | code_editor.set_font("fonts/Source_Code_Pro/static/SourceCodePro-Regular.ttf"); 18 | code_editor.set_mode(CodeEditorMode::Rhai); 19 | code_editor.set_font_size(17.0); 20 | 21 | code_editor.set_text("Your source code".to_string()); 22 | ``` 23 | 24 | In your draw loop you can than draw the editor 25 | 26 | ```rust 27 | code_editor.draw(frame, (0, 0, width, height), width); 28 | ``` 29 | 30 | The second parameter is the drawing rectangle into your frame, the last parameter is the stride in pixels. 31 | 32 | You can get the edited text via ```get_text()```. You will also need to connect mouse and keyboard events to the code editor, see the example app. There are also slots for ```cut```, ```copy```, ```paste```, ```undo``` and ```redo```. You will need to connect these in your app as well (the example app does not handle them). 33 | 34 | #### Syntax Highlighting 35 | 36 | The syntax highlighting is right now not configurable but is pretty universal. Supported modes are right now Rhai and Text (which has no highlighting). I will try to make the syntax highlighting more configurable in the future, in the meantime you can tweak the source code to your needs. 37 | 38 | #### Themes 39 | 40 | The default theme has this implementation: 41 | 42 | ```rust 43 | impl Theme { 44 | pub fn new() -> Self { 45 | Self { 46 | background : [34, 34, 36, 255], 47 | line_numbers : [160, 160, 160, 255], 48 | line_numbers_bg : [30, 30, 32, 255], 49 | 50 | text : [255, 255, 255, 255], 51 | cursor : [170, 170, 170, 255], 52 | 53 | identifier : [120, 214, 255, 255], 54 | number : [159, 197, 146, 255], 55 | keywords : [45, 133, 200, 255], 56 | brackets : [226, 73, 146, 212], 57 | comments : [69, 128, 56, 212], 58 | string : [197, 117, 92, 212], 59 | 60 | error : [237, 55, 54, 255], 61 | } 62 | } 63 | } 64 | ``` 65 | 66 | You can tweak it in the theme property of the CodeEditor struct or you can set a new theme via the ```set_theme(theme);``` function. 67 | 68 | # Disclaimer 69 | 70 | CodeEditor is actively maintained and I will improve it over time as I use it for my own applications. -------------------------------------------------------------------------------- /code_editor/src/codeeditor.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{prelude::*, undo::UndoStack}; 4 | 5 | use fontdue::{ Font, Metrics }; 6 | 7 | #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] 8 | pub enum CodeEditorMode { 9 | Rhai, 10 | Text, 11 | Settings, 12 | } 13 | 14 | pub struct CodeEditor { 15 | 16 | font : Option, 17 | draw2d : Draw2D, 18 | 19 | rect : (usize, usize, usize, usize), 20 | text : String, 21 | 22 | pub font_size : f32, 23 | 24 | cursor_offset : usize, 25 | pub cursor_pos : (usize, usize), 26 | pub cursor_rect : (usize, usize, usize, usize), 27 | 28 | needs_update : bool, 29 | pub mode : CodeEditorMode, 30 | 31 | line_numbers_buffer : Vec, 32 | line_numbers_size : (usize, usize), 33 | 34 | text_buffer : Vec, 35 | text_buffer_size : (usize, usize), 36 | 37 | metrics : HashMap)>, 38 | advance_width : usize, 39 | advance_height : usize, 40 | 41 | shift : bool, 42 | ctrl : bool, 43 | alt : bool, 44 | logo : bool, 45 | 46 | pub theme : Theme, 47 | pub settings : Settings, 48 | 49 | error : Option<(String, Option)>, 50 | 51 | mouse_wheel_delta : (isize, isize), 52 | offset : (isize, isize), 53 | max_offset : (usize, usize), 54 | 55 | range_buffer : (usize, usize), 56 | range_start : Option<(usize, usize)>, 57 | range_end : Option<(usize, usize)>, 58 | 59 | last_pos : (usize, usize), 60 | last_click : u128, 61 | click_stage : i32, 62 | 63 | code_safe_rect : (usize, usize, usize, usize), 64 | 65 | pub drag_pos : Option<(usize, usize)>, 66 | 67 | undo_stack : UndoStack, 68 | } 69 | 70 | impl CodeEditor { 71 | 72 | pub fn new() -> Self where Self: Sized { 73 | 74 | Self { 75 | font : None, 76 | draw2d : Draw2D {}, 77 | 78 | rect : (0, 0, 0, 0), 79 | text : "".to_string(), 80 | 81 | font_size : 17.0, 82 | 83 | cursor_offset : 0, 84 | cursor_pos : (0, 0), 85 | cursor_rect : (0, 0, 2, 0), 86 | 87 | needs_update : true, 88 | mode : CodeEditorMode::Rhai, 89 | 90 | line_numbers_buffer : vec![0;1], 91 | line_numbers_size : (0, 0), 92 | 93 | text_buffer : vec![0;1], 94 | text_buffer_size : (0, 0), 95 | 96 | metrics : HashMap::new(), 97 | advance_width : 10, 98 | advance_height : 22, 99 | 100 | shift : false, 101 | ctrl : false, 102 | alt : false, 103 | logo : false, 104 | 105 | theme : Theme::new(), 106 | settings : Settings::new(), 107 | 108 | error : None, 109 | 110 | mouse_wheel_delta : (0, 0), 111 | offset : (0, 0), 112 | max_offset : (0, 0), 113 | 114 | range_buffer : (0, 0), 115 | range_start : None, 116 | range_end : None, 117 | 118 | last_pos : (0, 0), 119 | last_click : 0, 120 | click_stage : 0, 121 | 122 | code_safe_rect : (0, 0, 0, 0), 123 | 124 | drag_pos : None, 125 | 126 | undo_stack : UndoStack::new(), 127 | } 128 | } 129 | 130 | /// Sets the path to the font file 131 | pub fn set_font(&mut self, path: &str) { 132 | 133 | if let Some(font_bytes) = std::fs::read(path).ok() { 134 | if let Some(font) = Font::from_bytes(font_bytes, fontdue::FontSettings::default()).ok() { 135 | self.font = Some(font); 136 | } 137 | } 138 | } 139 | 140 | /// Sets the font directly 141 | pub fn set_font_data(&mut self, font: Font) { 142 | self.font = Some(font); 143 | } 144 | 145 | /// Sets the font size 146 | pub fn set_font_size(&mut self, font_size: f32) { 147 | 148 | if let Some(font) = &self.font { 149 | let m = font.rasterize('w', font_size); 150 | self.advance_width = m.0.advance_width as usize; 151 | self.advance_height = (font_size + 4.0) as usize; 152 | self.font_size = font_size; 153 | } 154 | } 155 | 156 | /// Set the text / code to be edited 157 | pub fn set_text(&mut self, text: String) { 158 | self.text = text; 159 | self.needs_update = true; 160 | self.set_cursor((0, 0)); 161 | self.undo_stack = UndoStack::new(); 162 | } 163 | 164 | /// Returns the edited text 165 | pub fn get_text(&mut self) -> String { 166 | self.text.clone() 167 | } 168 | 169 | pub fn set_theme(&mut self, theme: Theme) { 170 | self.theme = theme; 171 | } 172 | 173 | pub fn set_error(&mut self, error: Option<(String, Option)>) { 174 | self.error = error; 175 | self.needs_update = true; 176 | } 177 | 178 | /// Sets the mode of the editor 179 | pub fn set_mode(&mut self, mode: CodeEditorMode) { 180 | self.offset = (0, 0); 181 | self.mode = mode; 182 | } 183 | 184 | pub fn draw(&mut self, frame: &mut [u8], rect: (usize, usize, usize, usize), stride: usize) { 185 | 186 | if self.needs_update { 187 | self.process_text(); 188 | } 189 | 190 | if let Some(drag_pos) = self.drag_pos { 191 | if drag_pos.1 >= rect.1 + rect.3 - 50 { 192 | if (self.offset.1 as usize) < self.max_offset.1 { 193 | self.offset.1 += 1; 194 | self.offset_sanity_check(); 195 | self.mouse_dragged(drag_pos); 196 | } 197 | } else 198 | if drag_pos.1 <= rect.1 + 50 { 199 | if self.offset.1 > 0 { 200 | self.offset.1 -= 1; 201 | self.offset_sanity_check(); 202 | self.mouse_dragged(drag_pos); 203 | } 204 | } 205 | } 206 | 207 | self.rect = rect.clone(); 208 | 209 | self.draw2d.draw_rect(frame, &rect, stride, &self.theme.background); 210 | self.draw2d.draw_rect(frame, &(rect.0, rect.1, 95, rect.3), stride, &self.theme.line_numbers_bg); 211 | 212 | let x = self.line_numbers_size.0 as isize + rect.0 as isize - self.offset.0 * self.advance_width as isize; 213 | let y = rect.1 as isize - self.offset.1 * self.advance_height as isize; 214 | 215 | // Line Numbers 216 | self.draw2d.blend_slice_safe(frame, &mut &self.line_numbers_buffer[..], &(0, y, self.line_numbers_size.0, self.line_numbers_size.1), stride, &rect); 217 | 218 | // Code 219 | let code_safe_rect = (rect.0 + self.line_numbers_size.0, rect.1, rect.2 - self.line_numbers_size.0, rect.3); 220 | self.draw2d.blend_slice_safe(frame, &mut self.text_buffer[..], &(x, y, self.text_buffer_size.0, self.text_buffer_size.1), stride, &code_safe_rect); 221 | self.code_safe_rect = code_safe_rect; 222 | 223 | // Cursor 224 | self.draw2d.draw_rect_safe(frame, &((rect.0 + self.line_numbers_size.0 + self.cursor_rect.0) as isize - self.offset.0 * self.advance_width as isize, (rect.1 + self.cursor_rect.1) as isize - self.offset.1 * self.advance_height as isize, self.cursor_rect.2, self.cursor_rect.3), stride, &self.theme.cursor, &code_safe_rect); 225 | } 226 | 227 | // Inside the selection range ? 228 | fn inside_selection(&self, x: usize, y: usize) -> bool { 229 | let mut inside = false; 230 | 231 | if let Some(range_start) = self.range_start { 232 | if let Some(range_end) = self.range_end { 233 | if y > range_start.1 && y < range_end.1 || (y == range_start.1 && x >= range_start.0 && y != range_end.1) || (y == range_end.1 && x <= range_end.0 && y != range_start.1) || (y == range_start.1 && y == range_end.1 && x >= range_start.0 && x <= range_end.0) { 234 | inside = true; 235 | } 236 | } 237 | } 238 | 239 | inside 240 | } 241 | 242 | /// Takes the current text and renders it to the text_buffer bitmap 243 | fn process_text(&mut self) { 244 | 245 | if let Some(font) = &self.font { 246 | 247 | let mut lines = self.text.lines(); 248 | 249 | let mut screen_width = 0_usize; 250 | let mut screen_height = 0_usize; 251 | 252 | let mut y = 0; 253 | 254 | while let Some(line) = lines.next() { 255 | let mut x = 0; 256 | 257 | let mut chars = line.chars(); 258 | let mut line_width = 0; 259 | while let Some(c) = chars.next() { 260 | if self.metrics.contains_key(&c) == false { 261 | let m= font.rasterize(c, self.font_size); 262 | self.metrics.insert(c, m); 263 | } 264 | 265 | if let Some((metrics, _bitmap)) = self.metrics.get(&c) { 266 | line_width += metrics.advance_width.ceil() as usize; 267 | x += 1; 268 | } 269 | } 270 | 271 | if line_width > screen_width { 272 | screen_width = line_width; 273 | } 274 | 275 | screen_height += self.advance_height; 276 | self.last_pos = (x, y); 277 | y += 1; 278 | } 279 | 280 | //println!("{} x {}", screen_width, screen_height); 281 | 282 | self.max_offset.0 = screen_width / self.advance_width; 283 | 284 | let left_size = self.settings.line_number_width; 285 | screen_height += left_size; 286 | self.needs_update = false; 287 | 288 | self.line_numbers_buffer = vec![0; left_size * screen_height * 4]; 289 | self.line_numbers_size = (left_size, screen_height); 290 | 291 | self.text_buffer = vec![0; screen_width * screen_height * 4]; 292 | self.text_buffer_size = (screen_width, screen_height); 293 | 294 | // Draw it 295 | 296 | let mut scanner = Scanner::new(self.text.as_str()); 297 | 298 | let mut x = 0; 299 | let mut y = 0; 300 | 301 | let stride = screen_width; 302 | 303 | let mut line_number = 1; 304 | 305 | let mut finished = false; 306 | let mut color : [u8;4] = self.theme.text; 307 | let mut number_printed_for_line = 0_usize; 308 | 309 | let selection_color = [45, 133, 200, 255];//self.theme.keywords; 310 | 311 | while finished == false { 312 | 313 | let token = scanner.scan_token(); 314 | let mut printit = false; 315 | 316 | match token.kind { 317 | 318 | TokenType::LineFeed => { 319 | 320 | let mut text_color = &self.theme.line_numbers; 321 | if let Some(error) = &self.error { 322 | if let Some(line) = error.1 { 323 | if line == line_number { 324 | text_color = &self.theme.error; 325 | } 326 | } 327 | } 328 | self.draw2d.draw_text_rect(&mut self.line_numbers_buffer[..], &(0, y, left_size - 20, self.advance_height), left_size, font, self.font_size, format!("{}", line_number).as_str(), &text_color, &self.theme.background, crate::draw2d::TextAlignment::Right); 329 | number_printed_for_line = line_number; 330 | 331 | if x == 0 { 332 | // Draw empty selection marker ? 333 | if self.inside_selection(0, y / self.advance_height) { 334 | let bcolor = [45, 133, 200, 255];//self.theme.keywords; 335 | self.draw2d.blend_rect(&mut self.text_buffer[..], &(x, y, self.advance_width / 2, self.advance_height), stride, &bcolor); 336 | } 337 | } 338 | x = 0; 339 | y += self.advance_height; 340 | line_number += 1; 341 | }, 342 | TokenType::Space => { 343 | 344 | // Inside the selection range ? 345 | if self.inside_selection(x / self.advance_width, y / self.advance_height) { 346 | let bcolor = [45, 133, 200, 255];//self.theme.keywords; 347 | self.draw2d.blend_rect(&mut self.text_buffer[..], &(x, y, self.advance_width, self.advance_height), stride, &bcolor); 348 | } 349 | 350 | x += self.advance_width 351 | }, 352 | TokenType::Eof => { 353 | 354 | if number_printed_for_line != line_number { 355 | let mut text_color = &self.theme.line_numbers; 356 | if let Some(error) = &self.error { 357 | if let Some(line) = error.1 { 358 | if line == line_number { 359 | text_color = &self.theme.error; 360 | } 361 | } 362 | } 363 | self.draw2d.draw_text_rect(&mut self.line_numbers_buffer[..], &(0, y, left_size - 20, self.advance_height), left_size, font, self.font_size, format!("{}", line_number).as_str(), &text_color, &self.theme.background, crate::draw2d::TextAlignment::Right); 364 | } 365 | 366 | finished = true }, 367 | 368 | TokenType::Identifier if self.mode == CodeEditorMode::Rhai || self.mode == CodeEditorMode::Settings => { color = self.theme.identifier; printit = true; }, 369 | TokenType::SingeLineComment if self.mode == CodeEditorMode::Rhai || self.mode == CodeEditorMode::Settings => { color = self.theme.comments; printit = true; }, 370 | TokenType::HexColor if self.mode == CodeEditorMode::Settings => { color = self.theme.string; printit = true; }, 371 | TokenType::Number if self.mode == CodeEditorMode::Rhai || self.mode == CodeEditorMode::Settings => { color = self.theme.number; printit = true; }, 372 | TokenType::String if self.mode == CodeEditorMode::Rhai || self.mode == CodeEditorMode::Settings => { color = self.theme.string; printit = true; }, 373 | TokenType::While if self.mode == CodeEditorMode::Rhai => { color = self.theme.keywords; printit = true; }, 374 | TokenType::For if self.mode == CodeEditorMode::Rhai => { color = self.theme.keywords; printit = true; }, 375 | TokenType::If if self.mode == CodeEditorMode::Rhai => { color = self.theme.keywords; printit = true; }, 376 | TokenType::Else if self.mode == CodeEditorMode::Rhai => { color = self.theme.keywords; printit = true; }, 377 | TokenType::Let if self.mode == CodeEditorMode::Rhai => { color = self.theme.keywords; printit = true; }, 378 | TokenType::Fun if self.mode == CodeEditorMode::Rhai => { color = self.theme.keywords; printit = true; }, 379 | TokenType::Print if self.mode == CodeEditorMode::Rhai => { color = self.theme.keywords; printit = true; }, 380 | TokenType::Quotation if self.mode == CodeEditorMode::Rhai => { color = self.theme.string; printit = true; }, 381 | 382 | TokenType::LeftBrace | TokenType::RightBrace | TokenType::LeftParen | TokenType::RightParen | TokenType::Dollar => { color = self.theme.brackets; printit = true; }, 383 | 384 | _ => { 385 | color = self.theme.text; 386 | printit = true; 387 | } 388 | } 389 | 390 | // Print the current lexeme 391 | if printit { 392 | 393 | let mut chars = token.lexeme.chars(); 394 | while let Some(c) = chars.next() { 395 | 396 | if let Some((metrics, bitmap)) = self.metrics.get(&c) { 397 | 398 | let mut bcolor = self.theme.background; 399 | 400 | // Inside the selection range ? 401 | if self.inside_selection(x / self.advance_width, y / self.advance_height) { 402 | bcolor = selection_color; 403 | self.draw2d.blend_rect( &mut self.text_buffer[..], &(x, y, self.advance_width, self.advance_height), stride, &bcolor); 404 | } 405 | 406 | let text_buffer_frame = &mut self.text_buffer[..]; 407 | for cy in 0..metrics.height { 408 | for cx in 0..metrics.width { 409 | 410 | let fy = (self.font_size as isize - metrics.height as isize - metrics.ymin as isize) as usize; 411 | 412 | let i = (x + cx + metrics.xmin as usize) * 4 + (y + cy + fy) * stride * 4; 413 | let m = bitmap[cx + cy * metrics.width]; 414 | 415 | text_buffer_frame[i..i + 4].copy_from_slice(&self.draw2d.mix_color(&bcolor, &color, m as f64 / 255.0)); 416 | } 417 | } 418 | x += self.advance_width; 419 | } 420 | } 421 | } 422 | } 423 | 424 | self.max_offset.1 = line_number; 425 | } 426 | } 427 | 428 | /// Sets the cursor offset based on the given screen position 429 | fn set_cursor_offset_from_pos(&mut self, pos: (usize, usize)) -> bool { 430 | 431 | let mut lines = self.text.lines(); 432 | 433 | let px = pos.0; 434 | let py = pos.1; 435 | 436 | let left_size = 0; 437 | let line_height = self.advance_height; 438 | 439 | self.cursor_offset = 0; 440 | 441 | let mut curr_line_index = 0_usize; 442 | 443 | let mut y = 0; 444 | 445 | let mut found = false; 446 | 447 | if self.text.is_empty() { 448 | self.cursor_pos.0 = 0; 449 | self.cursor_pos.1 = 0; 450 | self.cursor_rect.0 = 0; 451 | self.cursor_rect.1 = 0; 452 | self.cursor_rect.3 = self.advance_height; 453 | return true; 454 | } 455 | 456 | while let Some(line) = lines.next() { 457 | 458 | if py >= y && py <= y + self.advance_height { 459 | 460 | self.cursor_pos.0 = 0; 461 | self.cursor_pos.1 = curr_line_index; 462 | self.cursor_rect.0 = 0; 463 | self.cursor_rect.1 = y; 464 | self.cursor_rect.3 = line_height; 465 | 466 | if px > left_size { 467 | self.cursor_pos.0 = (px - left_size) / self.advance_width + 1; 468 | if (px - left_size) % self.advance_width < self.advance_width /2 && self.cursor_pos.0 > 0 && self.cursor_pos.0 <= line.len() { 469 | self.cursor_pos.0 -= 1; 470 | } 471 | self.cursor_pos.0 = std::cmp::min(self.cursor_pos.0, line.len()); 472 | if self.cursor_pos.0 > 0 { 473 | self.cursor_rect.0 += self.cursor_pos.0 * self.advance_width - 2; 474 | } 475 | } 476 | 477 | self.cursor_offset += self.cursor_pos.0; 478 | found = true; 479 | 480 | break; 481 | } else { 482 | self.cursor_offset += line.len(); 483 | } 484 | 485 | curr_line_index += 1; 486 | y += line_height; 487 | self.cursor_offset += 1; 488 | } 489 | 490 | // Check if there is an line feed at the end as this is cut off by lines() 491 | if found == false { 492 | if self.text.ends_with("\n") { 493 | self.cursor_pos.0 = 0; 494 | self.cursor_pos.1 = curr_line_index; 495 | self.cursor_rect.0 = 0; 496 | self.cursor_rect.1 = y; 497 | self.cursor_rect.3 = line_height; 498 | } else { 499 | 500 | // Selection is out of scope, select the end of the text 501 | 502 | let lines = self.text.lines(); 503 | if let Some(l) = lines.last() { 504 | 505 | self.cursor_pos.0 = l.len(); 506 | self.cursor_pos.1 = curr_line_index; 507 | self.cursor_rect.0 = l.len() * self.advance_width - 2; 508 | self.cursor_rect.1 = y - line_height; 509 | self.cursor_rect.3 = line_height; 510 | } 511 | } 512 | } 513 | 514 | true 515 | } 516 | 517 | /// Sets the cursor to the given position 518 | pub fn set_cursor(&mut self, pos: (usize, usize)) { 519 | self.cursor_pos = pos; 520 | self.cursor_rect.0 = pos.0 * self.advance_width; 521 | self.cursor_rect.1 = (pos.1+1) * self.advance_height; 522 | self.set_cursor_offset_from_pos((self.cursor_rect.0, self.cursor_rect.1)); 523 | } 524 | 525 | /// Copies the given range and returns it 526 | fn copy_range(&self, start: Option<(usize, usize)>, end: Option<(usize, usize)>) -> String { 527 | let mut s = "".to_string(); 528 | 529 | let mut x = 0; 530 | let mut y = 0; 531 | 532 | let mut inside = false; 533 | 534 | let mut chars = self.text.chars(); 535 | 536 | while let Some(c) = chars.next() { 537 | 538 | if inside == false { 539 | if let Some(start) = start { 540 | if y > start.1 || (y == start.1 && x >= start.0) { 541 | inside = true; 542 | } 543 | } else { 544 | inside = true; 545 | } 546 | } 547 | 548 | if inside { 549 | if let Some(end) = end { 550 | if y > end.1 || (y == end.1 && x >= end.0) { 551 | break; 552 | } 553 | } 554 | } 555 | 556 | if inside { 557 | s.push(c); 558 | } 559 | 560 | if c == '\n' { 561 | x = 0; 562 | y += 1; 563 | } else { 564 | x += 1; 565 | } 566 | } 567 | s 568 | } 569 | 570 | /// Copies the given range and returns it 571 | fn copy_range_incl(&self, start: Option<(usize, usize)>, end: Option<(usize, usize)>) -> String { 572 | let mut s = "".to_string(); 573 | 574 | let mut x = 0; 575 | let mut y = 0; 576 | 577 | let mut inside = false; 578 | 579 | let mut chars = self.text.chars(); 580 | 581 | while let Some(c) = chars.next() { 582 | 583 | if inside == false { 584 | if let Some(start) = start { 585 | if y > start.1 || (y == start.1 && x >= start.0) { 586 | inside = true; 587 | } 588 | } else { 589 | inside = true; 590 | } 591 | } 592 | 593 | if inside { 594 | if let Some(end) = end { 595 | if y > end.1 || (y == end.1 && x > end.0) { 596 | break; 597 | } 598 | } 599 | } 600 | 601 | if inside { 602 | s.push(c); 603 | } 604 | 605 | if c == '\n' { 606 | x = 0; 607 | y += 1; 608 | } else { 609 | x += 1; 610 | } 611 | } 612 | s 613 | } 614 | 615 | pub fn key_down(&mut self, char: Option, key: Option) -> bool { 616 | 617 | let undo = self.text.clone(); 618 | let undo_pos = self.cursor_pos; 619 | 620 | if self.logo || self.ctrl { 621 | use copypasta::{ClipboardContext, ClipboardProvider}; 622 | 623 | // Copy 624 | if char == Some('c') || char == Some('C') { 625 | let clip = self.copy_range_incl(self.range_start, self.range_end); 626 | //println!("{}", clip); 627 | 628 | let mut ctx = ClipboardContext::new().unwrap(); 629 | _ = ctx.set_contents(clip.to_owned()); 630 | 631 | return true; 632 | } 633 | 634 | // Cut 635 | if char == Some('x') || char == Some('X') { 636 | let clip = self.copy_range_incl(self.range_start, self.range_end); 637 | 638 | let mut ctx = ClipboardContext::new().unwrap(); 639 | _ = ctx.set_contents(clip.to_owned()); 640 | 641 | if let Some(start) = self.range_start { 642 | if let Some(end) = self.range_end { 643 | let first_half = self.copy_range(None, Some((std::cmp::max(start.0, 0), start.1))); 644 | let second_half = self.copy_range(Some((end.0 + 1, end.1)), None); 645 | let text = first_half + second_half.as_str(); 646 | self.text = text; 647 | 648 | self.range_start = None; 649 | self.range_end = None; 650 | self.process_text(); 651 | 652 | self.set_cursor((start.0, start.1)); 653 | } 654 | } 655 | 656 | return true; 657 | } 658 | 659 | // Paste 660 | if char == Some('v') || char == Some('V') { 661 | let mut ctx = ClipboardContext::new().unwrap(); 662 | if let Some(text) = ctx.get_contents().ok() { 663 | self.paste(text); 664 | } 665 | return true; 666 | } 667 | } 668 | 669 | if let Some(key) = key { 670 | match key { 671 | WidgetKey::Delete => { 672 | let mut handled = false; 673 | if let Some(start) = self.range_start { 674 | if let Some(end) = self.range_end { 675 | let first_half = self.copy_range(None, Some((std::cmp::max(start.0, 0), start.1))); 676 | let second_half = self.copy_range(Some((end.0 + 1, end.1)), None); 677 | let text = first_half + second_half.as_str(); 678 | self.text = text; 679 | self.range_start = None; 680 | self.range_end = None; 681 | self.process_text(); 682 | handled = true; 683 | 684 | self.set_cursor(start); 685 | } 686 | } 687 | if handled == false && self.text.is_empty() == false && self.cursor_offset >= 1 { 688 | let mut number_of_chars_on_prev_line = 0_usize; 689 | let delete_line; 690 | if self.cursor_pos.0 == 0 { 691 | delete_line = true; 692 | if let Some(prev_line) = self.text.lines().nth(self.cursor_pos.1 - 1) { 693 | number_of_chars_on_prev_line = prev_line.len(); 694 | } 695 | } else { 696 | delete_line = false; 697 | } 698 | 699 | let index = (self.cursor_offset - 1).clamp(0, self.text.len() - 1); 700 | _ = self.text.remove(index); 701 | self.process_text(); 702 | 703 | if delete_line == false { 704 | let x = if self.cursor_rect.0 > self.advance_width { self.cursor_rect.0 - self.advance_width } else { 0 }; 705 | self.set_cursor_offset_from_pos((x, self.cursor_rect.1 + 10)); 706 | } else { 707 | if number_of_chars_on_prev_line == 0 { 708 | self.set_cursor_offset_from_pos((0, self.cursor_rect.1 - 5)); 709 | } else { 710 | self.set_cursor_offset_from_pos((number_of_chars_on_prev_line * self.advance_width - 2, self.cursor_rect.1 - 5)); 711 | } 712 | } 713 | } 714 | self.undo_stack.add(undo, undo_pos, self.text.clone(), self.cursor_pos); 715 | return true; 716 | }, 717 | 718 | WidgetKey::Tab => { 719 | self.text.insert(self.cursor_offset.min(self.text.len()), ' '); 720 | self.text.insert((self.cursor_offset + 1).min(self.text.len()), ' '); 721 | self.process_text(); 722 | self.set_cursor_offset_from_pos((self.cursor_rect.0 + self.advance_width * 2, self.cursor_rect.1 + 10)); 723 | self.undo_stack.add(undo, undo_pos, self.text.clone(), self.cursor_pos); 724 | return true; 725 | }, 726 | 727 | WidgetKey::Return => { 728 | self.text.insert(self.cursor_offset.min(self.text.len()), '\n'); 729 | self.process_text(); 730 | self.set_cursor_offset_from_pos((0, self.cursor_rect.1 + 30)); 731 | self.undo_stack.add(undo, undo_pos, self.text.clone(), self.cursor_pos); 732 | return true; 733 | }, 734 | 735 | WidgetKey::Up => { 736 | if self.cursor_rect.1 >= 5 { 737 | self.set_cursor_offset_from_pos((self.cursor_rect.0, self.cursor_rect.1 - 5)); 738 | } 739 | return true; 740 | }, 741 | 742 | WidgetKey::Down => { 743 | self.set_cursor_offset_from_pos((self.cursor_rect.0, self.cursor_rect.1 + 30)); 744 | return true; 745 | }, 746 | 747 | WidgetKey::Left => { 748 | 749 | if self.logo || self.ctrl { 750 | self.set_cursor_offset_from_pos((0, self.cursor_rect.1 + 10)); 751 | } else { 752 | if self.cursor_pos.0 > 0 {//&& self.cursor_rect.0 >= 5 { 753 | // Go one left 754 | if self.cursor_rect.0 > self.advance_width { 755 | self.set_cursor_offset_from_pos((self.cursor_rect.0 - self.advance_width, self.cursor_rect.1 + 10)); 756 | } else { 757 | self.set_cursor_offset_from_pos((0, self.cursor_rect.1 + 10)); 758 | } 759 | } else { 760 | // Go one up 761 | if self.cursor_rect.1 >= 5 { 762 | self.set_cursor_offset_from_pos((100000, self.cursor_rect.1 - 5)); 763 | } 764 | } 765 | } 766 | return true; 767 | }, 768 | 769 | WidgetKey::Right => { 770 | if self.logo || self.ctrl { 771 | self.set_cursor_offset_from_pos((100000, self.cursor_rect.1 + 10)); 772 | } else { 773 | if let Some(c) = self.text.chars().nth(self.cursor_offset) { 774 | if c == '\n' { 775 | // Go down 776 | self.set_cursor_offset_from_pos((0, self.cursor_rect.1 + 30)); 777 | } else { 778 | // Go Right 779 | self.set_cursor_offset_from_pos((self.cursor_rect.0 + self.advance_width, self.cursor_rect.1 + 10)); 780 | } 781 | } 782 | } 783 | return true; 784 | }, 785 | _ => {} 786 | } 787 | } 788 | 789 | if let Some(c) = char { 790 | if c.is_ascii() && c.is_control() == false { 791 | 792 | let mut handled = false; 793 | if let Some(start) = self.range_start { 794 | if let Some(end) = self.range_end { 795 | let first_half = self.copy_range(None, Some((std::cmp::max(start.0, 0), start.1))); 796 | let second_half = self.copy_range(Some((end.0 + 1, end.1)), None); 797 | let text = first_half + c.to_string().as_str() + second_half.as_str(); 798 | self.text = text; 799 | self.range_start = None; 800 | self.range_end = None; 801 | self.process_text(); 802 | handled = true; 803 | 804 | self.set_cursor((start.0 + 1, start.1)); 805 | } 806 | } 807 | 808 | if handled == false { 809 | if self.text.is_empty() { 810 | self.text.push(c); 811 | } else { 812 | self.text.insert(self.cursor_offset.min(self.text.len()), c); 813 | } 814 | self.process_text(); 815 | self.set_cursor_offset_from_pos((self.cursor_rect.0 + self.advance_width, self.cursor_rect.1 + 10)); 816 | } 817 | 818 | self.undo_stack.add(undo, undo_pos, self.text.clone(), self.cursor_pos); 819 | 820 | return true; 821 | } 822 | } 823 | false 824 | } 825 | 826 | pub fn mouse_down(&mut self, p: (usize, usize)) -> bool { 827 | 828 | let mut pos = p.clone(); 829 | pos.0 = pos.0.max(self.settings.line_number_width); 830 | 831 | let time = self.get_time(); 832 | 833 | if time - self.last_click > 500 { 834 | let consumed = self.set_cursor_offset_from_pos((pos.0 - self.settings.line_number_width + self.offset.0 as usize * self.advance_width as usize, pos.1 + self.offset.1 as usize * self.advance_height as usize)); 835 | self.range_buffer = self.cursor_pos.clone(); 836 | self.range_start = Some(self.cursor_pos.clone()); 837 | self.range_end = None; 838 | self.needs_update = true; 839 | self.last_click = time; 840 | self.click_stage = 0; 841 | return consumed; 842 | } else { 843 | 844 | if self.click_stage == 0 { 845 | 846 | let line = self.cursor_pos.1; 847 | 848 | let mut left = self.cursor_pos.0; 849 | let mut right = self.cursor_pos.0; 850 | 851 | let mut range_start = Some((left, line)); 852 | let mut range_end = Some((right, line)); 853 | 854 | let t = self.copy_range_incl(range_start, range_end); 855 | let c = t.chars().next(); 856 | 857 | if let Some(c) = c { 858 | if c.is_alphanumeric() { 859 | 860 | // Go left 861 | 862 | while left > 0 { 863 | left -= 1; 864 | let r_start = Some((left, line)); 865 | let t = self.copy_range_incl(r_start, range_end); 866 | let c = t.chars().next(); 867 | 868 | if let Some(c) = c { 869 | if c.is_alphanumeric() == false { 870 | left += 1; 871 | range_start = Some((left, line)); 872 | break; 873 | } else { 874 | range_start = Some((left, line)); 875 | } 876 | } 877 | } 878 | 879 | // Go Right 880 | 881 | let mut last_t = "".to_string(); 882 | 883 | loop { 884 | right += 1; 885 | let r_end = Some((right, line)); 886 | let t = self.copy_range_incl(range_start, r_end); 887 | let c = t.chars().last(); 888 | 889 | if t.chars().count() <= 1 { 890 | break; 891 | } 892 | 893 | if let Some(c) = c { 894 | if c.is_alphanumeric() == false { 895 | right -= 1; 896 | range_end = Some((right, line)); 897 | break; 898 | } else { 899 | if t == last_t { 900 | range_end = Some((100000, line)); 901 | break; 902 | } 903 | } 904 | } else { 905 | range_end = Some((100000, line)); 906 | break; 907 | } 908 | last_t = t.clone(); 909 | } 910 | 911 | self.range_start = range_start; 912 | self.range_end = range_end; 913 | self.needs_update = true; 914 | 915 | self.last_click = time; 916 | self.click_stage = 1; 917 | 918 | return true; 919 | } 920 | } 921 | } else { 922 | let line = self.cursor_pos.1; 923 | self.range_start = Some((0, line)); 924 | self.range_end = Some((100000, line)); 925 | self.needs_update = true; 926 | self.click_stage = 2; 927 | return true; 928 | } 929 | } 930 | false 931 | } 932 | 933 | pub fn mouse_up(&mut self, _pos: (usize, usize)) -> bool { 934 | if self.range_start.is_none() || self.range_end.is_none() { 935 | self.range_start = None; 936 | self.range_end = None; 937 | self.needs_update = true; 938 | } 939 | self.drag_pos = None; 940 | false 941 | } 942 | 943 | pub fn mouse_dragged(&mut self, mut pos: (usize, usize)) -> bool { 944 | if pos.0 < self.settings.line_number_width { 945 | pos.0 = self.settings.line_number_width; 946 | } 947 | 948 | let consumed = self.set_cursor_offset_from_pos((pos.0 - self.settings.line_number_width + self.offset.0 as usize * self.advance_width as usize, pos.1 + self.offset.1 as usize * self.advance_height as usize)); 949 | 950 | if (self.cursor_pos.1 == self.range_buffer.1 && self.cursor_pos.0 <= self.range_buffer.0) || self.cursor_pos.1 < self.range_buffer.1 { 951 | self.range_start = Some(self.cursor_pos.clone()); 952 | let mut end = self.range_buffer.clone(); 953 | if end.0 > 0 { end.0 -= 1; } 954 | self.range_end = Some(end); 955 | } else { 956 | if self.range_end.is_some() { 957 | self.range_start = Some(self.range_buffer); 958 | let mut end = self.cursor_pos.clone(); 959 | if end.0 > 0 { end.0 -= 1; } 960 | self.range_end = Some(end); 961 | } else { 962 | let mut end = self.cursor_pos.clone(); 963 | if end.0 > 0 { end.0 -= 1; } 964 | self.range_end = Some(end); 965 | } 966 | } 967 | 968 | self.drag_pos = Some(pos); 969 | 970 | self.needs_update = true; 971 | consumed 972 | } 973 | 974 | pub fn mouse_hover(&mut self, _pos: (usize, usize)) -> bool { 975 | false 976 | } 977 | 978 | pub fn mouse_wheel(&mut self, delta: (isize, isize)) -> bool { 979 | self.mouse_wheel_delta.0 += delta.0; 980 | self.mouse_wheel_delta.1 += delta.1; 981 | self.offset.0 -= self.mouse_wheel_delta.0 / (self.advance_width as isize * 6); 982 | self.offset.1 -= self.mouse_wheel_delta.1 / (self.advance_height as isize * 1); 983 | self.offset.0 = self.offset.0.clamp(0, self.max_offset.0 as isize); 984 | self.offset.1 = self.offset.1.clamp(0, self.max_offset.1 as isize); 985 | self.mouse_wheel_delta.0 -= (self.mouse_wheel_delta.0 / (self.advance_width as isize * 6)) * self.advance_width as isize; 986 | self.mouse_wheel_delta.1 -= (self.mouse_wheel_delta.1 / (self.advance_height as isize * 1)) * self.advance_height as isize; 987 | 988 | self.offset_sanity_check(); 989 | 990 | true 991 | } 992 | 993 | /// Makes sure that the offset is within a reasonable range. 994 | pub fn offset_sanity_check(&mut self) { 995 | // If the editors width is larger than the text width dont scroll. 996 | if self.code_safe_rect.2 >= self.text_buffer_size.0 { 997 | self.offset.0 = 0; 998 | } else { 999 | // Make sure only to scroll as much as needed 1000 | let max_scroll_x = (self.text_buffer_size.0 - self.code_safe_rect.2) / self.advance_width; 1001 | if self.offset.0 > max_scroll_x as isize { 1002 | self.offset.0 = max_scroll_x as isize; 1003 | } 1004 | } 1005 | 1006 | let y_height = self.text_buffer_size.1;// - self.settings.line_number_width; 1007 | 1008 | // If the editors height is larger than the text height dont scroll. 1009 | if self.code_safe_rect.3 >= y_height { 1010 | self.offset.1 = 0; 1011 | } else { 1012 | // Make sure only to scroll as much as needed 1013 | let max_scroll_y = (y_height - self.code_safe_rect.3) / self.advance_height; 1014 | if self.offset.1 > max_scroll_y as isize { 1015 | self.offset.1 = max_scroll_y as isize; 1016 | } 1017 | } 1018 | } 1019 | 1020 | pub fn modifier_changed(&mut self, shift: bool, ctrl: bool, alt: bool, logo: bool) -> bool { 1021 | self.shift = shift; 1022 | self.ctrl = ctrl; 1023 | self.alt = alt; 1024 | self.logo = logo; 1025 | false 1026 | } 1027 | 1028 | /// Gets the current time in milliseconds 1029 | fn get_time(&self) -> u128 { 1030 | use std::time::{SystemTime, UNIX_EPOCH}; 1031 | let stop = SystemTime::now() 1032 | .duration_since(UNIX_EPOCH) 1033 | .expect("Time went backwards"); 1034 | stop.as_millis() 1035 | } 1036 | 1037 | /// Cut 1038 | pub fn cut(&mut self) -> String { 1039 | let undo = self.text.clone(); 1040 | let undo_pos = self.cursor_pos; 1041 | let text = self.copy_range_incl(self.range_start, self.range_end); 1042 | 1043 | if let Some(start) = self.range_start { 1044 | if let Some(end) = self.range_end { 1045 | let first_half = self.copy_range(None, Some((std::cmp::max(start.0, 0), start.1))); 1046 | let second_half = self.copy_range(Some((end.0 + 1, end.1)), None); 1047 | let text = first_half + second_half.as_str(); 1048 | self.text = text; 1049 | 1050 | self.range_start = None; 1051 | self.range_end = None; 1052 | self.process_text(); 1053 | 1054 | self.set_cursor((start.0, start.1)); 1055 | } 1056 | } 1057 | self.undo_stack.add(undo, undo_pos, self.text.clone(), self.cursor_pos); 1058 | text 1059 | } 1060 | 1061 | /// Copy 1062 | pub fn copy(&mut self) -> String { 1063 | self.copy_range_incl(self.range_start, self.range_end) 1064 | } 1065 | 1066 | /// Paste 1067 | pub fn paste(&mut self, text: String) { 1068 | let undo = self.text.clone(); 1069 | let undo_pos = self.cursor_pos; 1070 | 1071 | let mut handled = false; 1072 | if let Some(start) = self.range_start { 1073 | if let Some(end) = self.range_end { 1074 | let first_half = self.copy_range(None, Some((std::cmp::max(start.0, 0), start.1))); 1075 | let second_half = self.copy_range(Some((end.0 + 1, end.1)), None); 1076 | let text = first_half + text.to_string().as_str() + second_half.as_str(); 1077 | 1078 | self.text = text; 1079 | self.range_start = None; 1080 | self.range_end = None; 1081 | self.process_text(); 1082 | handled = true; 1083 | 1084 | self.set_cursor((start.0, start.1)); 1085 | } 1086 | } 1087 | 1088 | if handled == false { 1089 | let half = self.cursor_pos.clone(); 1090 | 1091 | let first_half = self.copy_range(None, Some(half)); 1092 | let second_half = self.copy_range(Some(self.cursor_pos), None); 1093 | 1094 | let new_text = first_half + text.as_str(); 1095 | 1096 | self.text = new_text.clone(); 1097 | self.process_text(); 1098 | self.set_cursor(self.last_pos); 1099 | 1100 | self.text = new_text + second_half.as_str(); 1101 | self.needs_update = true; 1102 | } 1103 | 1104 | self.undo_stack.add(undo, undo_pos, self.text.clone(), self.cursor_pos); 1105 | } 1106 | 1107 | /// Has Undo 1108 | pub fn has_undo(&self) -> bool { 1109 | self.undo_stack.has_undo() 1110 | } 1111 | 1112 | /// Has Redo 1113 | pub fn has_redo(&self) -> bool { 1114 | self.undo_stack.has_redo() 1115 | } 1116 | 1117 | /// Undo 1118 | pub fn undo(&mut self) { 1119 | if self.undo_stack.has_undo() { 1120 | let rc = self.undo_stack.undo(); 1121 | self.text = rc.0; 1122 | self.process_text(); 1123 | self.set_cursor(rc.1); 1124 | self.needs_update = true; 1125 | } 1126 | } 1127 | 1128 | /// Redo 1129 | pub fn redo(&mut self) { 1130 | if self.undo_stack.has_redo() { 1131 | let rc = self.undo_stack.redo(); 1132 | self.text = rc.0; 1133 | self.process_text(); 1134 | self.set_cursor(rc.1); 1135 | self.needs_update = true; 1136 | } 1137 | } 1138 | 1139 | } -------------------------------------------------------------------------------- /code_editor/src/draw2d.rs: -------------------------------------------------------------------------------- 1 | use fontdue::layout::{ Layout, LayoutSettings, CoordinateSystem, TextStyle, VerticalAlign, HorizontalAlign }; 2 | use fontdue::Font; 3 | 4 | #[derive(PartialEq)] 5 | pub enum TextAlignment { 6 | Left, 7 | Center, 8 | Right 9 | } 10 | 11 | pub struct Draw2D { 12 | } 13 | 14 | impl Draw2D { 15 | 16 | /// Draws the mask 17 | pub fn blend_mask(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, mask_frame: &[u8], mask_size: &(usize, usize), color: &[u8; 4]) { 18 | for y in 0..mask_size.1 { 19 | for x in 0..mask_size.0 { 20 | let i = (x+rect.0) * 4 + (y+rect.1) * stride * 4; 21 | let m = mask_frame[x + y * mask_size.0]; 22 | let c : [u8;4] = [color[0], color[1], color[2], m]; 23 | 24 | let background = &[frame[i], frame[i+1], frame[i+2], frame[i+3]]; 25 | frame[i..i + 4].copy_from_slice(&self.mix_color(&background, &c, m as f64 / 255.0)); 26 | } 27 | } 28 | } 29 | 30 | /// Draws the given rectangle 31 | pub fn draw_rect(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, color: &[u8; 4]) { 32 | for y in rect.1..rect.1+rect.3 { 33 | for x in rect.0..rect.0+rect.2 { 34 | let i = x * 4 + y * stride * 4; 35 | frame[i..i + 4].copy_from_slice(color); 36 | } 37 | } 38 | } 39 | 40 | /// Draws the given rectangle 41 | pub fn draw_rect_safe(&self, frame: &mut [u8], rect: &(isize, isize, usize, usize), stride: usize, color: &[u8; 4], safe_rect: &(usize, usize, usize, usize)) { 42 | let dest_stride_isize = stride as isize; 43 | for y in rect.1..rect.1+rect.3 as isize { 44 | if y >= safe_rect.1 as isize && y < (safe_rect.1 + safe_rect.3) as isize { 45 | for x in rect.0..rect.0+rect.2 as isize{ 46 | if x >= safe_rect.0 as isize && x < (safe_rect.0 + safe_rect.2) as isize { 47 | let i = (x * 4 + y * dest_stride_isize * 4) as usize; 48 | frame[i..i + 4].copy_from_slice(color); 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | /// Blend the given rectangle 56 | pub fn blend_rect(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, color: &[u8; 4]) { 57 | for y in rect.1..rect.1+rect.3 { 58 | for x in rect.0..rect.0+rect.2 { 59 | let i = x * 4 + y * stride * 4; 60 | 61 | let background = &[frame[i], frame[i+1], frame[i+2], frame[i+3]]; 62 | frame[i..i + 4].copy_from_slice(&self.mix_color(&background, &color, color[3] as f64 / 255.0)); 63 | } 64 | } 65 | } 66 | 67 | /// Draws the outline of a given rectangle 68 | pub fn draw_rect_outline(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, color: [u8; 4]) { 69 | 70 | let y = rect.1; 71 | for x in rect.0..rect.0+rect.2 { 72 | let mut i = x * 4 + y * stride * 4; 73 | frame[i..i + 4].copy_from_slice(&color); 74 | 75 | i = x * 4 + (y + rect.3- 1) * stride * 4; 76 | frame[i..i + 4].copy_from_slice(&color); 77 | } 78 | 79 | let x = rect.0; 80 | for y in rect.1..rect.1+rect.3 { 81 | let mut i = x * 4 + y * stride * 4; 82 | frame[i..i + 4].copy_from_slice(&color); 83 | 84 | i = (x + rect.2 - 1) * 4 + y * stride * 4; 85 | frame[i..i + 4].copy_from_slice(&color); 86 | } 87 | } 88 | 89 | /// Draws a circle 90 | pub fn draw_circle(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, color: &[u8; 4], radius: f64) { 91 | let center = (rect.0 as f64 + rect.2 as f64 / 2.0, rect.1 as f64 + rect.3 as f64 / 2.0); 92 | for y in rect.1..rect.1+rect.3 { 93 | for x in rect.0..rect.0+rect.2 { 94 | let i = x * 4 + y * stride * 4; 95 | 96 | let mut d = (x as f64 - center.0).powf(2.0) + (y as f64 - center.1).powf(2.0); 97 | d = d.sqrt() - radius; 98 | 99 | if d < 0.0 { 100 | let t = self.fill_mask(d); 101 | //let t = self.smoothstep(0.0, -2.0, r); 102 | 103 | let background = &[frame[i], frame[i+1], frame[i+2], 255]; 104 | let mixed_color = self.mix_color(&background, &color, t); 105 | 106 | frame[i..i + 4].copy_from_slice(&mixed_color); 107 | } 108 | } 109 | } 110 | } 111 | 112 | /// Draws a circle with a border of a given size 113 | pub fn _draw_circle_with_border(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, color: &[u8; 4], radius: f64, border_color: &[u8; 4], border_size: f64) { 114 | let center = (rect.0 as f64 + rect.2 as f64 / 2.0, rect.1 as f64 + rect.3 as f64 / 2.0); 115 | for y in rect.1..rect.1+rect.3 { 116 | for x in rect.0..rect.0+rect.2 { 117 | let i = x * 4 + y * stride * 4; 118 | 119 | let mut d = (x as f64 - center.0).powf(2.0) + (y as f64 - center.1).powf(2.0); 120 | d = d.sqrt() - radius; 121 | 122 | if d < 1.0 { 123 | let t = self.fill_mask(d); 124 | 125 | let background = &[frame[i], frame[i+1], frame[i+2], 255]; 126 | let mut mixed_color = self.mix_color(&background, &color, t); 127 | 128 | let b = self.border_mask(d, border_size); 129 | mixed_color = self.mix_color(&mixed_color, &border_color, b); 130 | 131 | frame[i..i + 4].copy_from_slice(&mixed_color); 132 | } 133 | } 134 | } 135 | } 136 | 137 | /// Draws a rounded rect 138 | pub fn draw_rounded_rect(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, size: &(f64, f64), color: &[u8; 4], rounding: &(f64, f64, f64, f64)) { 139 | let center = (rect.0 as f64 + size.0 / 2.0, rect.1 as f64 + size.1 / 2.0 + (rect.3 as f64 - size.1) / 2.0); 140 | for y in rect.1..rect.1+rect.3 { 141 | for x in rect.0..rect.0+rect.2 { 142 | let i = x * 4 + y * stride * 4; 143 | 144 | let p = (x as f64 - center.0, y as f64 - center.1); 145 | let mut r : (f64, f64); 146 | 147 | if p.0 > 0.0 { 148 | r = (rounding.0, rounding.1); 149 | } else { 150 | r = (rounding.2, rounding.3); 151 | } 152 | 153 | if p.1 <= 0.0 { 154 | r.0 = r.1; 155 | } 156 | 157 | let q : (f64, f64) = (p.0.abs() - size.0 / 2.0 + r.0, p.1.abs() - size.1 / 2.0 + r.0); 158 | let d = f64::min(f64::max(q.0, q.1), 0.0) + self.length((f64::max(q.0, 0.0), f64::max(q.1, 0.0))) - r.0; 159 | 160 | if d < 0.0 { 161 | let t = self.fill_mask(d); 162 | 163 | let background = &[frame[i], frame[i+1], frame[i+2], 255]; 164 | let mut mixed_color = self.mix_color(&background, &color, t * (color[3] as f64 / 255.0)); 165 | mixed_color[3] = (mixed_color[3] as f64 * (color[3] as f64 / 255.0)) as u8; 166 | frame[i..i + 4].copy_from_slice(&mixed_color); 167 | } 168 | } 169 | } 170 | } 171 | 172 | /// Blends a rounded rect 173 | pub fn blend_rounded_rect(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, size: &(f64, f64), color: &[u8; 4], rounding: &(f64, f64, f64, f64)) { 174 | let center = (rect.0 as f64 + size.0 / 2.0, rect.1 as f64 + size.1 / 2.0 + (rect.3 as f64 - size.1) / 2.0); 175 | for y in rect.1..rect.1+rect.3 { 176 | for x in rect.0..rect.0+rect.2 { 177 | let i = x * 4 + y * stride * 4; 178 | 179 | let p = (x as f64 - center.0, y as f64 - center.1); 180 | let mut r : (f64, f64); 181 | 182 | if p.0 > 0.0 { 183 | r = (rounding.0, rounding.1); 184 | } else { 185 | r = (rounding.2, rounding.3); 186 | } 187 | 188 | if p.1 <= 0.0 { 189 | r.0 = r.1; 190 | } 191 | 192 | let q : (f64, f64) = (p.0.abs() - size.0 / 2.0 + r.0, p.1.abs() - size.1 / 2.0 + r.0); 193 | let d = f64::min(f64::max(q.0, q.1), 0.0) + self.length((f64::max(q.0, 0.0), f64::max(q.1, 0.0))) - r.0; 194 | 195 | if d < 0.0 { 196 | let t = self.fill_mask(d); 197 | 198 | let background = &[frame[i], frame[i+1], frame[i+2], 255]; 199 | let mut mixed_color = self.mix_color(&background, &color, t * 0.5 * (color[3] as f64 / 255.0)); 200 | mixed_color[3] = (mixed_color[3] as f64 * (color[3] as f64 / 255.0)) as u8; 201 | frame[i..i + 4].copy_from_slice(&mixed_color); 202 | } 203 | } 204 | } 205 | } 206 | 207 | /// Draws a rounded rect with a border 208 | pub fn draw_rounded_rect_with_border(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, size: &(f64, f64), color: &[u8; 4], rounding: &(f64, f64, f64, f64), border_color: &[u8; 4], border_size: f64) { 209 | let center = ((rect.0 as f64 + size.0 / 2.0).round(), (rect.1 as f64 + size.1 / 2.0 + (rect.3 as f64 - size.1) / 2.0).round()); 210 | for y in rect.1..rect.1+rect.3 { 211 | for x in rect.0..rect.0+rect.2 { 212 | let i = x * 4 + y * stride * 4; 213 | 214 | let p = (x as f64 - center.0, y as f64 - center.1); 215 | let mut r : (f64, f64); 216 | 217 | if p.0 > 0.0 { 218 | r = (rounding.0, rounding.1); 219 | } else { 220 | r = (rounding.2, rounding.3); 221 | } 222 | 223 | if p.1 <= 0.0 { 224 | r.0 = r.1; 225 | } 226 | 227 | let q : (f64, f64) = (p.0.abs() - size.0 / 2.0 + r.0, p.1.abs() - size.1 / 2.0 + r.0); 228 | let d = f64::min(f64::max(q.0, q.1), 0.0) + self.length((f64::max(q.0, 0.0), f64::max(q.1, 0.0))) - r.0; 229 | 230 | if d < 1.0 { 231 | let t = self.fill_mask(d); 232 | 233 | let background = &[frame[i], frame[i+1], frame[i+2], 255]; 234 | let mut mixed_color = self.mix_color(&background, &color, t * (color[3] as f64 / 255.0)); 235 | 236 | let b = self.border_mask(d, border_size); 237 | mixed_color = self.mix_color(&mixed_color, &border_color, b); 238 | 239 | frame[i..i + 4].copy_from_slice(&mixed_color); 240 | } 241 | } 242 | } 243 | } 244 | 245 | /// Draws the given rectangle 246 | pub fn draw_square_pattern(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, color: &[u8; 4], line_color: &[u8; 4], pattern_size: usize) { 247 | for y in rect.1..rect.1+rect.3 { 248 | for x in rect.0..rect.0+rect.2 { 249 | let i = x * 4 + y * stride * 4; 250 | 251 | if x % pattern_size == 0 || y % pattern_size == 0 { 252 | frame[i..i + 4].copy_from_slice(line_color); 253 | } else { 254 | frame[i..i + 4].copy_from_slice(color); 255 | } 256 | } 257 | } 258 | } 259 | 260 | /// Draws a text aligned inside a rect 261 | pub fn draw_text_rect(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, font: &Font, size: f32, text: &str, color: &[u8; 4], background: &[u8;4], align: TextAlignment) { 262 | 263 | let mut text_to_use = text.trim_end().to_string().clone(); 264 | text_to_use = text_to_use.replace('\n', ""); 265 | if text_to_use.trim_end().is_empty() { return; } 266 | 267 | let mut text_size = self.get_text_size(font, size, text_to_use.as_str()); 268 | 269 | let mut add_trail = false; 270 | // Text is too long ?? 271 | while text_size.0 >= rect.2 { 272 | text_to_use.pop(); 273 | text_size = self.get_text_size(font, size, (text_to_use.clone() + "...").as_str()); 274 | add_trail = true; 275 | } 276 | 277 | if add_trail { 278 | text_to_use = text_to_use + "..."; 279 | } 280 | 281 | let fonts = &[font]; 282 | 283 | let mut layout = Layout::new(CoordinateSystem::PositiveYDown); 284 | layout.reset(&LayoutSettings { 285 | max_width : Some(rect.2 as f32), 286 | max_height : Some(rect.3 as f32), 287 | horizontal_align : if align == TextAlignment::Left { HorizontalAlign::Left } else { if align == TextAlignment::Right {HorizontalAlign::Right} else { HorizontalAlign::Center } }, 288 | vertical_align : VerticalAlign::Middle, 289 | ..LayoutSettings::default() 290 | }); 291 | layout.append(fonts, &TextStyle::new(text_to_use.as_str(), size, 0)); 292 | 293 | for glyph in layout.glyphs() { 294 | let (metrics, alphamap) = font.rasterize(glyph.parent, glyph.key.px); 295 | //println!("Metrics: {:?}", glyph); 296 | 297 | for y in 0..metrics.height { 298 | for x in 0..metrics.width { 299 | let i = (x+rect.0+glyph.x as usize) * 4 + (y + rect.1 + glyph.y as usize) * stride * 4; 300 | let m = alphamap[x + y * metrics.width]; 301 | 302 | frame[i..i + 4].copy_from_slice(&self.mix_color(&background, &color, m as f64 / 255.0)); 303 | } 304 | } 305 | } 306 | } 307 | 308 | /// Blends a text aligned inside a rect and blends it with the existing background 309 | pub fn blend_text_rect(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, font: &Font, size: f32, text: &str, color: &[u8; 4], align: TextAlignment) { 310 | 311 | let mut text_to_use = text.trim_end().to_string().clone(); 312 | if text_to_use.trim_end().is_empty() { return; } 313 | 314 | let mut text_size = self.get_text_size(font, size, text_to_use.as_str()); 315 | 316 | let mut add_trail = false; 317 | // Text is too long ?? 318 | while text_size.0 >= rect.2 { 319 | text_to_use.pop(); 320 | text_size = self.get_text_size(font, size, (text_to_use.clone() + "...").as_str()); 321 | add_trail = true; 322 | } 323 | 324 | if add_trail { 325 | text_to_use = text_to_use + "..."; 326 | } 327 | 328 | let fonts = &[font]; 329 | 330 | let mut layout = Layout::new(CoordinateSystem::PositiveYDown); 331 | layout.reset(&LayoutSettings { 332 | max_width : Some(rect.2 as f32), 333 | max_height : Some(rect.3 as f32), 334 | horizontal_align : if align == TextAlignment::Left { HorizontalAlign::Left } else { if align == TextAlignment::Right {HorizontalAlign::Right} else { HorizontalAlign::Center } }, 335 | vertical_align : VerticalAlign::Middle, 336 | ..LayoutSettings::default() 337 | }); 338 | layout.append(fonts, &TextStyle::new(&text_to_use.as_str(), size, 0)); 339 | 340 | for glyph in layout.glyphs() { 341 | let (metrics, alphamap) = font.rasterize(glyph.parent, glyph.key.px); 342 | //println!("Metrics: {:?}", glyph); 343 | 344 | for y in 0..metrics.height { 345 | for x in 0..metrics.width { 346 | let i = (x+rect.0+glyph.x as usize) * 4 + (y + rect.1 + glyph.y as usize) * stride * 4; 347 | let m = alphamap[x + y * metrics.width]; 348 | 349 | let background = &[frame[i], frame[i+1], frame[i+2], frame[i+3]]; 350 | frame[i..i + 4].copy_from_slice(&self.mix_color(&background, &color, m as f64 / 255.0)); 351 | } 352 | } 353 | } 354 | } 355 | 356 | /// Draws the given text 357 | pub fn draw_text(&self, frame: &mut [u8], pos: &(usize, usize), stride: usize, font: &Font, size: f32, text: &str, color: &[u8; 4], background: &[u8; 4]) { 358 | if text.is_empty() { return; } 359 | 360 | let fonts = &[font]; 361 | 362 | let mut layout = Layout::new(CoordinateSystem::PositiveYDown); 363 | layout.reset(&LayoutSettings { 364 | ..LayoutSettings::default() 365 | }); 366 | layout.append(fonts, &TextStyle::new(text, size, 0)); 367 | 368 | for glyph in layout.glyphs() { 369 | let (metrics, alphamap) = font.rasterize(glyph.parent, glyph.key.px); 370 | //println!("Metrics: {:?}", glyph); 371 | 372 | for y in 0..metrics.height { 373 | for x in 0..metrics.width { 374 | let i = (x+pos.0+glyph.x as usize) * 4 + (y + pos.1 + glyph.y as usize) * stride * 4; 375 | let m = alphamap[x + y * metrics.width]; 376 | 377 | frame[i..i + 4].copy_from_slice(&self.mix_color(&background, &color, m as f64 / 255.0)); 378 | } 379 | } 380 | } 381 | } 382 | 383 | /// Returns the size of the given text 384 | pub fn get_text_size(&self, font: &Font, size: f32, text: &str) -> (usize, usize) { 385 | let fonts = &[font]; 386 | 387 | let mut layout = Layout::new(CoordinateSystem::PositiveYDown); 388 | layout.reset(&LayoutSettings { 389 | ..LayoutSettings::default() 390 | }); 391 | layout.append(fonts, &TextStyle::new(text, size, 0)); 392 | 393 | let x = layout.glyphs()[layout.glyphs().len()-1].x.ceil() as usize + layout.glyphs()[layout.glyphs().len()-1].width + 1; 394 | (x, layout.height() as usize) 395 | } 396 | 397 | /// Copies rect from the source frame into the dest frame 398 | pub fn copy_slice(&self, dest: &mut [u8], source: &[u8], rect: &(usize, usize, usize, usize), dest_stride: usize) { 399 | for y in 0..rect.3 { 400 | let d = rect.0 * 4 + (y + rect.1) * dest_stride * 4; 401 | let s = y * rect.2 * 4; 402 | dest[d..d + rect.2 * 4].copy_from_slice(&source[s..s + rect.2 * 4]); 403 | } 404 | } 405 | 406 | /// Blends rect from the source frame into the dest frame 407 | pub fn blend_slice(&self, dest: &mut [u8], source: &[u8], rect: &(usize, usize, usize, usize), dest_stride: usize) { 408 | for y in 0..rect.3 { 409 | let d = rect.0 * 4 + (y + rect.1) * dest_stride * 4; 410 | let s = y * rect.2 * 4; 411 | 412 | for x in 0..rect.2 { 413 | 414 | let dd = d + x * 4; 415 | let ss = s + x * 4; 416 | 417 | let background = &[dest[dd], dest[dd+1], dest[dd+2], dest[dd+3]]; 418 | let color = &[source[ss], source[ss+1], source[ss+2], source[ss+3]]; 419 | dest[dd..dd + 4].copy_from_slice(&self.mix_color(&background, &color, (color[3] as f64) / 255.0)); 420 | } 421 | } 422 | } 423 | 424 | /// Blends rect from the source frame into the dest frame and honors the safe rect 425 | pub fn blend_slice_safe(&self, dest: &mut [u8], source: &[u8], rect: &(isize, isize, usize, usize), dest_stride: usize, safe_rect: &(usize, usize, usize, usize)) { 426 | let dest_stride_isize = dest_stride as isize; 427 | for y in 0..rect.3 as isize { 428 | let d = rect.0 * 4 + (y + rect.1) * dest_stride_isize * 4; 429 | let s = y * (rect.2 as isize) * 4; 430 | 431 | // TODO: Make this faster 432 | 433 | if (y + rect.1 as isize) >= safe_rect.1 as isize && (y + rect.1 as isize) < (safe_rect.1 + safe_rect.3) as isize { 434 | for x in 0..rect.2 as isize { 435 | 436 | if (x + rect.0 as isize) >= safe_rect.0 as isize && (x + rect.0 as isize) < (safe_rect.0 + safe_rect.2) as isize { 437 | let dd = (d + x * 4) as usize; 438 | let ss = (s + x * 4) as usize; 439 | 440 | let background = &[dest[dd], dest[dd+1], dest[dd+2], dest[dd+3]]; 441 | let color = &[source[ss], source[ss+1], source[ss+2], source[ss+3]]; 442 | dest[dd..dd + 4].copy_from_slice(&self.mix_color(&background, &color, (color[3] as f64) / 255.0)); 443 | } 444 | } 445 | } 446 | } 447 | } 448 | 449 | /// Scale a chunk to the destination size 450 | pub fn _scale_chunk(&self, frame: &mut [u8], rect: &(usize, usize, usize, usize), stride: usize, source_frame: &[u8], source_size: &(usize, usize)) { 451 | 452 | let x_ratio = source_size.0 as f32 / rect.2 as f32; 453 | let y_ratio = source_size.1 as f32 / rect.3 as f32; 454 | 455 | for sy in 0..rect.3 { 456 | let y = (sy as f32 * y_ratio) as usize; 457 | 458 | for sx in 0..rect.2 { 459 | let x = (sx as f32 * x_ratio) as usize; 460 | 461 | let d = (rect.0 + sx) * 4 + (sy + rect.1) * stride * 4; 462 | let s = x * 4 + y * source_size.0 * 4; 463 | 464 | frame[d..d + 4].copy_from_slice(&[source_frame[s], source_frame[s+1], source_frame[s+2], source_frame[s+3]]); 465 | } 466 | } 467 | } 468 | 469 | /// The fill mask for an SDF distance 470 | fn fill_mask(&self, dist : f64) -> f64 { 471 | (-dist).clamp(0.0, 1.0) 472 | } 473 | 474 | /// The border mask for an SDF distance 475 | fn border_mask(&self, dist : f64, width: f64) -> f64 { 476 | (dist + width).clamp(0.0, 1.0) - dist.clamp(0.0, 1.0) 477 | } 478 | 479 | /// Smoothstep for f64 480 | pub fn _smoothstep(&self, e0: f64, e1: f64, x: f64) -> f64 { 481 | let t = ((x - e0) / (e1 - e0)). clamp(0.0, 1.0); 482 | return t * t * (3.0 - 2.0 * t); 483 | } 484 | 485 | /// Mixes two colors based on v 486 | pub fn mix_color(&self, a: &[u8;4], b: &[u8;4], v: f64) -> [u8; 4] { 487 | [ (((1.0 - v) * (a[0] as f64 / 255.0) + b[0] as f64 / 255.0 * v) * 255.0) as u8, 488 | (((1.0 - v) * (a[1] as f64 / 255.0) + b[1] as f64 / 255.0 * v) * 255.0) as u8, 489 | (((1.0 - v) * (a[2] as f64 / 255.0) + b[2] as f64 / 255.0 * v) * 255.0) as u8, 490 | 255] 491 | } 492 | 493 | // Length of a 2d vector 494 | pub fn length(&self, v: (f64, f64)) -> f64 { 495 | ((v.0).powf(2.0) + (v.1).powf(2.0)).sqrt() 496 | } 497 | 498 | /// Draw hover help 499 | pub fn draw_hover_help(&self, frame: &mut [u8], pos: (usize, usize), font: &Font, title: Option, text: String, safe_rect: (usize, usize, usize, usize) ) { 500 | 501 | let mut rect = (pos.0, pos.1, 300, 200); 502 | let stride = safe_rect.2; 503 | 504 | let mut title_space = 10; 505 | if title.is_some() { title_space = 20; }; 506 | let font_size_title = 16.0; 507 | let font_size_text = 14.0; 508 | 509 | let mut vert_size = 30 + title_space; 510 | if title.is_none() { vert_size -= 20; }; 511 | 512 | let background = [40, 40, 40, 255]; 513 | let border_color = [128, 128, 128, 255]; 514 | let title_color = [255, 255, 255, 255]; 515 | let text_color = [240, 240, 240, 255]; 516 | 517 | let fonts = &[font]; 518 | 519 | let mut layout = Layout::new(CoordinateSystem::PositiveYDown); 520 | layout.reset(&LayoutSettings { 521 | max_width : Some(rect.2 as f32 - 20.0), 522 | max_height : None,//Some(rect.3 as f32 - vert_size as f32), 523 | horizontal_align : HorizontalAlign::Left, 524 | vertical_align : VerticalAlign::Top, 525 | ..LayoutSettings::default() 526 | }); 527 | layout.append(fonts, &TextStyle::new(text.as_str(), font_size_text, 0)); 528 | 529 | rect.3 = layout.height().ceil() as usize + vert_size; 530 | 531 | if rect.0 + rect.2 > safe_rect.2 { 532 | rect.0 -= rect.0 + rect.2 - safe_rect.2 - 10; 533 | } 534 | 535 | if rect.1 + rect.3 > safe_rect.3 { 536 | rect.1 -= rect.1 + rect.3 - safe_rect.3 - 10; 537 | } 538 | 539 | self.draw_rect(frame, &rect, stride, &background); 540 | self.draw_rect_outline(frame, &rect, stride, border_color); 541 | 542 | if let Some(title) = title { 543 | self.draw_text(frame, &(rect.0 + 10, rect.1 + 10), stride, font, font_size_title, title.as_str(), &title_color, &background); 544 | rect.1 += title_space; 545 | rect.1 += 10; 546 | } 547 | 548 | rect.0 += 10; 549 | rect.1 += 10; 550 | 551 | for glyph in layout.glyphs() { 552 | let (metrics, alphamap) = font.rasterize(glyph.parent, glyph.key.px); 553 | 554 | for y in 0..metrics.height { 555 | for x in 0..metrics.width { 556 | let i = (x+rect.0+glyph.x as usize) * 4 + (y + rect.1 + glyph.y as usize) * stride * 4; 557 | let m = alphamap[x + y * metrics.width]; 558 | 559 | frame[i..i + 4].copy_from_slice(&self.mix_color(&background, &text_color, m as f64 / 255.0)); 560 | } 561 | } 562 | } 563 | 564 | } 565 | 566 | } -------------------------------------------------------------------------------- /code_editor/src/error.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | #[derive(Eq, PartialEq, Debug, Clone)] 4 | struct CodeError { 5 | pub message : String, 6 | pub line : Option 7 | } -------------------------------------------------------------------------------- /code_editor/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod scanner; 2 | mod draw2d; 3 | pub mod theme; 4 | pub mod settings; 5 | pub mod codeeditor; 6 | pub mod error; 7 | pub mod undo; 8 | 9 | #[derive(PartialEq, Debug, Copy, Clone)] 10 | pub enum WidgetKey { 11 | Escape, 12 | Return, 13 | Delete, 14 | Up, 15 | Right, 16 | Down, 17 | Left, 18 | Space, 19 | Tab 20 | } 21 | 22 | pub mod prelude { 23 | pub use crate::scanner::*; 24 | pub use crate::settings::*; 25 | pub use crate::theme::*; 26 | pub use crate::WidgetKey; 27 | pub use crate::draw2d::*; 28 | pub use crate::codeeditor::*; 29 | pub use crate::error::*; 30 | } 31 | -------------------------------------------------------------------------------- /code_editor/src/scanner.rs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/ceronman/loxido/blob/master/src/scanner.rs 2 | // Licensed under the MIT license of Manuel Cerón. 3 | 4 | use std::collections::HashMap; 5 | 6 | #[allow(dead_code)] 7 | #[derive(Eq, PartialEq, Debug, Copy, Clone, Hash)] 8 | pub enum TokenType { 9 | LeftParen, 10 | RightParen, 11 | LeftBrace, 12 | RightBrace, 13 | Comma, 14 | Dot, 15 | Minus, 16 | Plus, 17 | Semicolon, 18 | Slash, 19 | Star, 20 | Dollar, 21 | 22 | LineFeed, 23 | Space, 24 | Quotation, 25 | Unknown, 26 | SingeLineComment, 27 | HexColor, 28 | 29 | // One or two character tokens. 30 | Bang, 31 | BangEqual, 32 | Equal, 33 | EqualEqual, 34 | Greater, 35 | GreaterEqual, 36 | Less, 37 | LessEqual, 38 | 39 | // Literals. 40 | Identifier, 41 | String, 42 | Number, 43 | 44 | // Keywords. 45 | And, 46 | Class, 47 | Else, 48 | False, 49 | For, 50 | Fun, 51 | If, 52 | Nil, 53 | Or, 54 | Print, 55 | Return, 56 | Super, 57 | This, 58 | True, 59 | Let, 60 | While, 61 | 62 | Error, 63 | Eof, 64 | } 65 | 66 | #[derive(Copy, Clone, Debug)] 67 | pub struct Token<'sourcecode> { 68 | pub kind: TokenType, 69 | pub line: usize, 70 | pub lexeme: &'sourcecode str, 71 | } 72 | 73 | #[allow(dead_code)] 74 | impl<'sourcecode> Token<'sourcecode> { 75 | pub fn synthetic(text: &'sourcecode str) -> Token<'sourcecode> { 76 | Token { 77 | kind: TokenType::Error, 78 | lexeme: text, 79 | line: 0, 80 | } 81 | } 82 | } 83 | 84 | #[allow(dead_code)] 85 | pub struct Scanner<'sourcecode> { 86 | keywords: HashMap<&'static str, TokenType>, 87 | code: &'sourcecode str, 88 | start: usize, 89 | current: usize, 90 | line: usize, 91 | } 92 | 93 | #[allow(dead_code)] 94 | impl<'sourcecode> Scanner<'sourcecode> { 95 | pub fn new(code: &'sourcecode str) -> Scanner { 96 | let mut keywords = HashMap::with_capacity(16); 97 | keywords.insert("and", TokenType::And); 98 | keywords.insert("class", TokenType::Class); 99 | keywords.insert("else", TokenType::Else); 100 | keywords.insert("false", TokenType::False); 101 | keywords.insert("for", TokenType::For); 102 | keywords.insert("fn", TokenType::Fun); 103 | keywords.insert("if", TokenType::If); 104 | keywords.insert("nil", TokenType::Nil); 105 | keywords.insert("or", TokenType::Or); 106 | keywords.insert("print", TokenType::Print); 107 | keywords.insert("return", TokenType::Return); 108 | keywords.insert("super", TokenType::Super); 109 | keywords.insert("this", TokenType::This); 110 | keywords.insert("true", TokenType::True); 111 | keywords.insert("let", TokenType::Let); 112 | keywords.insert("while", TokenType::While); 113 | 114 | Scanner { 115 | keywords, 116 | code, 117 | start: 0, 118 | current: 0, 119 | line: 1, 120 | } 121 | } 122 | 123 | pub fn scan_token(&mut self) -> Token<'sourcecode> { 124 | //self.skip_whitespace(); 125 | self.start = self.current; 126 | if self.is_at_end() { 127 | return self.make_token(TokenType::Eof); 128 | } 129 | 130 | match self.advance() { 131 | b' ' => self.make_token(TokenType::Space), 132 | b'\n' => self.make_token(TokenType::LineFeed), 133 | b'(' => self.make_token(TokenType::LeftParen), 134 | b')' => self.make_token(TokenType::RightParen), 135 | b'{' => self.make_token(TokenType::LeftBrace), 136 | b'}' => self.make_token(TokenType::RightBrace), 137 | b'$' => self.make_token(TokenType::Dollar), 138 | b';' => self.make_token(TokenType::Semicolon), 139 | b',' => self.make_token(TokenType::Comma), 140 | b'.' => self.make_token(TokenType::Dot), 141 | b'-' => self.make_token(TokenType::Minus), 142 | b'+' => self.make_token(TokenType::Plus), 143 | b'/' if self.matches(b'/') => self.single_line_comment(), 144 | b'#' => self.hex_color(), 145 | b'/' => self.make_token(TokenType::Slash), 146 | b'*' => self.make_token(TokenType::Star), 147 | b'!' if self.matches(b'=') => self.make_token(TokenType::BangEqual), 148 | b'!' => self.make_token(TokenType::Bang), 149 | b'=' if self.matches(b'=') => self.make_token(TokenType::EqualEqual), 150 | b'=' => self.make_token(TokenType::Equal), 151 | b'<' if self.matches(b'=') => self.make_token(TokenType::LessEqual), 152 | b'<' => self.make_token(TokenType::Less), 153 | b'>' if self.matches(b'=') => self.make_token(TokenType::GreaterEqual), 154 | b'>' => self.make_token(TokenType::Greater), 155 | b'"' => self.string(), 156 | b'`' => self.string2(), 157 | c if is_digit(c) => self.number(), 158 | c if is_alpha(c) => self.identifier(), 159 | _ => self.make_token(TokenType::Unknown),//self.error_token("Unexpected character."), 160 | } 161 | } 162 | 163 | fn is_at_end(&self) -> bool { 164 | self.current == self.code.len() 165 | } 166 | 167 | fn lexeme(&self) -> &'sourcecode str { 168 | &self.code[self.start..self.current] 169 | } 170 | 171 | fn make_token(&self, kind: TokenType) -> Token<'sourcecode> { 172 | Token { 173 | kind, 174 | lexeme: self.lexeme(), 175 | line: self.line, 176 | } 177 | } 178 | 179 | fn peek(&self) -> u8 { 180 | if self.is_at_end() { 181 | 0 182 | } else { 183 | self.code.as_bytes()[self.current] 184 | } 185 | } 186 | fn peek_next(&self) -> u8 { 187 | if self.current > self.code.len() - 2 { 188 | b'\0' 189 | } else { 190 | self.code.as_bytes()[self.current + 1] 191 | } 192 | } 193 | 194 | fn error_token(&self, message: &'static str) -> Token<'static> { 195 | Token { 196 | kind: TokenType::Error, 197 | lexeme: message, 198 | line: self.line, 199 | } 200 | } 201 | 202 | fn advance(&mut self) -> u8 { 203 | let char = self.peek(); 204 | self.current += 1; 205 | char 206 | } 207 | 208 | fn matches(&mut self, expected: u8) -> bool { 209 | if self.is_at_end() || self.peek() != expected { 210 | false 211 | } else { 212 | self.current += 1; 213 | true 214 | } 215 | } 216 | 217 | fn skip_whitespace(&mut self) { 218 | while !self.is_at_end() { 219 | match self.peek() { 220 | b' ' | b'\r' | b'\t' => { 221 | self.advance(); 222 | } 223 | b'\n' => { 224 | self.line += 1; 225 | self.advance(); 226 | } 227 | b'/' if self.peek_next() == b'/' => { 228 | while self.peek() != b'\n' && !self.is_at_end() { 229 | self.advance(); 230 | } 231 | } 232 | _ => return, 233 | } 234 | } 235 | } 236 | 237 | fn string(&mut self) -> Token<'sourcecode> { 238 | let b_current = self.current; 239 | 240 | while self.peek() != b'"' && !self.is_at_end() { 241 | if self.peek() == b'\n' { 242 | //self.line += 1; 243 | self.current = b_current; 244 | return self.make_token(TokenType::Quotation); 245 | } 246 | self.advance(); 247 | } 248 | 249 | if self.is_at_end() { 250 | //self.error_token("Unterminated string.") 251 | self.current = b_current; 252 | self.make_token(TokenType::Quotation) 253 | } else { 254 | self.advance(); 255 | self.make_token(TokenType::String) 256 | } 257 | } 258 | 259 | fn string2(&mut self) -> Token<'sourcecode> { 260 | let b_current = self.current; 261 | 262 | while self.peek() != b'`' && !self.is_at_end() { 263 | if self.peek() == b'\n' { 264 | //self.line += 1; 265 | self.current = b_current; 266 | return self.make_token(TokenType::Quotation); 267 | } 268 | self.advance(); 269 | } 270 | 271 | if self.is_at_end() { 272 | //self.error_token("Unterminated string.") 273 | self.current = b_current; 274 | self.make_token(TokenType::Quotation) 275 | } else { 276 | self.advance(); 277 | self.make_token(TokenType::String) 278 | } 279 | } 280 | 281 | fn number(&mut self) -> Token<'sourcecode> { 282 | while is_digit(self.peek()) { 283 | self.advance(); 284 | } 285 | 286 | if self.peek() == b'.' && is_digit(self.peek_next()) { 287 | self.advance(); 288 | while is_digit(self.peek()) { 289 | self.advance(); 290 | } 291 | } 292 | 293 | self.make_token(TokenType::Number) 294 | } 295 | 296 | fn hex_color(&mut self) -> Token<'sourcecode> { 297 | while self.is_at_end() == false && self.peek() != b'\n' { 298 | self.advance(); 299 | } 300 | self.make_token(TokenType::HexColor) 301 | } 302 | 303 | fn single_line_comment(&mut self) -> Token<'sourcecode> { 304 | while self.is_at_end() == false && self.peek() != b'\n' { 305 | self.advance(); 306 | } 307 | self.make_token(TokenType::SingeLineComment) 308 | } 309 | 310 | fn identifier(&mut self) -> Token<'sourcecode> { 311 | while is_alpha(self.peek()) || is_digit(self.peek()) { 312 | self.advance(); 313 | } 314 | self.make_token(self.identifier_type()) 315 | } 316 | 317 | fn identifier_type(&self) -> TokenType { 318 | self.keywords 319 | .get(self.lexeme()) 320 | .cloned() 321 | .unwrap_or(TokenType::Identifier) 322 | } 323 | } 324 | 325 | fn is_digit(c: u8) -> bool { 326 | c.is_ascii_digit() 327 | } 328 | 329 | fn is_alpha(c: u8) -> bool { 330 | c.is_ascii_alphabetic() || c == b'_' 331 | } -------------------------------------------------------------------------------- /code_editor/src/settings.rs: -------------------------------------------------------------------------------- 1 | pub struct Settings { 2 | 3 | pub line_number_width : usize, 4 | 5 | } 6 | 7 | impl Settings { 8 | 9 | pub fn new() -> Self { 10 | Self { 11 | line_number_width : 100, 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /code_editor/src/theme.rs: -------------------------------------------------------------------------------- 1 | 2 | pub struct Theme { 3 | 4 | pub background : [u8;4], 5 | pub line_numbers : [u8;4], 6 | pub line_numbers_bg : [u8;4], 7 | 8 | pub text : [u8;4], 9 | pub cursor : [u8;4], 10 | 11 | pub identifier : [u8;4], 12 | pub number : [u8;4], 13 | pub keywords : [u8;4], 14 | pub brackets : [u8;4], 15 | pub comments : [u8;4], 16 | pub string : [u8;4], 17 | 18 | pub error : [u8;4], 19 | } 20 | 21 | impl Theme { 22 | 23 | pub fn new() -> Self { 24 | Self { 25 | background : [34, 34, 36, 255], 26 | line_numbers : [160, 160, 160, 255], 27 | line_numbers_bg : [30, 30, 32, 255], 28 | 29 | text : [255, 255, 255, 255], 30 | cursor : [170, 170, 170, 255], 31 | 32 | identifier : [120, 214, 255, 255], 33 | number : [159, 197, 146, 255], 34 | keywords : [45, 133, 200, 255], 35 | brackets : [226, 73, 146, 212], 36 | comments : [69, 128, 56, 212], 37 | string : [197, 117, 92, 212], 38 | 39 | error : [237, 55, 54, 255], 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /code_editor/src/undo.rs: -------------------------------------------------------------------------------- 1 | pub struct Undo { 2 | pub undo_data : String, 3 | pub undo_pos : (usize, usize), 4 | 5 | pub redo_data : String, 6 | pub redo_pos : (usize, usize), 7 | 8 | pub time_stamp : u128, 9 | } 10 | 11 | pub struct UndoStack { 12 | pub stack : Vec, 13 | 14 | pub index : isize, 15 | } 16 | 17 | impl UndoStack { 18 | pub fn new() -> Self { 19 | Self { 20 | stack : vec![], 21 | index : -1, 22 | } 23 | } 24 | 25 | pub fn has_undo(&self) -> bool { 26 | self.index >= 0 27 | } 28 | 29 | pub fn has_redo(&self) -> bool { 30 | if self.index >= -1 && self.index < self.stack.len() as isize - 1 { 31 | return true; 32 | } 33 | false 34 | } 35 | 36 | pub fn undo(&mut self) -> (String, (usize, usize)) { 37 | let rc = (self.stack[self.index as usize].undo_data.clone(), self.stack[self.index as usize].undo_pos.clone()); 38 | self.index -= 1; 39 | rc 40 | } 41 | 42 | pub fn redo(&mut self) -> (String, (usize, usize)) { 43 | self.index += 1; 44 | let rc = (self.stack[self.index as usize].redo_data.clone(), self.stack[self.index as usize].redo_pos.clone()); 45 | rc 46 | } 47 | 48 | pub fn add(&mut self, undo: String, undo_pos: (usize, usize), redo: String, redo_pos: (usize, usize)) { 49 | 50 | if self.index >= 0 { 51 | 52 | let time = self.get_time(); 53 | 54 | // If the last item is less than 2s old, replace it 55 | if time < self.stack[(self.index) as usize].time_stamp + 2000 { 56 | 57 | self.stack[(self.index) as usize].redo_data = redo; 58 | self.stack[(self.index) as usize].redo_pos = redo_pos; 59 | self.stack[(self.index) as usize].time_stamp = time; 60 | 61 | return; 62 | } 63 | } 64 | 65 | let to_remove = self.stack.len() as isize - self.index - 1; 66 | for _i in 0..to_remove { 67 | self.stack.pop(); 68 | } 69 | 70 | self.stack.push(Undo { 71 | undo_data : undo, 72 | undo_pos : undo_pos, 73 | redo_data : redo, 74 | redo_pos : redo_pos, 75 | time_stamp : self.get_time(), 76 | }); 77 | 78 | self.index += 1; 79 | } 80 | 81 | fn get_time(&self) -> u128 { 82 | let stop = std::time::SystemTime::now() 83 | .duration_since(std::time::UNIX_EPOCH) 84 | .expect("Time went backwards"); 85 | stop.as_millis() 86 | } 87 | } -------------------------------------------------------------------------------- /example_app/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "aho-corasick" 18 | version = "0.7.18" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 21 | dependencies = [ 22 | "memchr", 23 | ] 24 | 25 | [[package]] 26 | name = "arrayvec" 27 | version = "0.7.2" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 30 | 31 | [[package]] 32 | name = "ash" 33 | version = "0.34.0+1.2.203" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "b0f780da53d0063880d45554306489f09dd8d1bda47688b4a57bc579119356df" 36 | dependencies = [ 37 | "libloading", 38 | ] 39 | 40 | [[package]] 41 | name = "atty" 42 | version = "0.2.14" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 45 | dependencies = [ 46 | "hermit-abi", 47 | "libc", 48 | "winapi", 49 | ] 50 | 51 | [[package]] 52 | name = "autocfg" 53 | version = "1.1.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 56 | 57 | [[package]] 58 | name = "bit-set" 59 | version = "0.5.3" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" 62 | dependencies = [ 63 | "bit-vec", 64 | ] 65 | 66 | [[package]] 67 | name = "bit-vec" 68 | version = "0.6.3" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 71 | 72 | [[package]] 73 | name = "bitflags" 74 | version = "1.3.2" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 77 | 78 | [[package]] 79 | name = "block" 80 | version = "0.1.6" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 83 | 84 | [[package]] 85 | name = "bumpalo" 86 | version = "3.11.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" 89 | 90 | [[package]] 91 | name = "bytemuck" 92 | version = "1.12.1" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" 95 | 96 | [[package]] 97 | name = "byteorder" 98 | version = "1.4.3" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 101 | 102 | [[package]] 103 | name = "calloop" 104 | version = "0.9.3" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82" 107 | dependencies = [ 108 | "log", 109 | "nix 0.22.3", 110 | ] 111 | 112 | [[package]] 113 | name = "cc" 114 | version = "1.0.73" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 117 | 118 | [[package]] 119 | name = "cfg-if" 120 | version = "0.1.10" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 123 | 124 | [[package]] 125 | name = "cfg-if" 126 | version = "1.0.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 129 | 130 | [[package]] 131 | name = "cfg_aliases" 132 | version = "0.1.1" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 135 | 136 | [[package]] 137 | name = "cocoa" 138 | version = "0.24.0" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" 141 | dependencies = [ 142 | "bitflags", 143 | "block", 144 | "cocoa-foundation", 145 | "core-foundation 0.9.3", 146 | "core-graphics 0.22.3", 147 | "foreign-types", 148 | "libc", 149 | "objc", 150 | ] 151 | 152 | [[package]] 153 | name = "cocoa-foundation" 154 | version = "0.1.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" 157 | dependencies = [ 158 | "bitflags", 159 | "block", 160 | "core-foundation 0.9.3", 161 | "core-graphics-types", 162 | "foreign-types", 163 | "libc", 164 | "objc", 165 | ] 166 | 167 | [[package]] 168 | name = "codespan-reporting" 169 | version = "0.11.1" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 172 | dependencies = [ 173 | "termcolor", 174 | "unicode-width", 175 | ] 176 | 177 | [[package]] 178 | name = "copyless" 179 | version = "0.1.5" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" 182 | 183 | [[package]] 184 | name = "core-foundation" 185 | version = "0.7.0" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" 188 | dependencies = [ 189 | "core-foundation-sys 0.7.0", 190 | "libc", 191 | ] 192 | 193 | [[package]] 194 | name = "core-foundation" 195 | version = "0.9.3" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 198 | dependencies = [ 199 | "core-foundation-sys 0.8.3", 200 | "libc", 201 | ] 202 | 203 | [[package]] 204 | name = "core-foundation-sys" 205 | version = "0.7.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" 208 | 209 | [[package]] 210 | name = "core-foundation-sys" 211 | version = "0.8.3" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 214 | 215 | [[package]] 216 | name = "core-graphics" 217 | version = "0.19.2" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" 220 | dependencies = [ 221 | "bitflags", 222 | "core-foundation 0.7.0", 223 | "foreign-types", 224 | "libc", 225 | ] 226 | 227 | [[package]] 228 | name = "core-graphics" 229 | version = "0.22.3" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" 232 | dependencies = [ 233 | "bitflags", 234 | "core-foundation 0.9.3", 235 | "core-graphics-types", 236 | "foreign-types", 237 | "libc", 238 | ] 239 | 240 | [[package]] 241 | name = "core-graphics-types" 242 | version = "0.1.1" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" 245 | dependencies = [ 246 | "bitflags", 247 | "core-foundation 0.9.3", 248 | "foreign-types", 249 | "libc", 250 | ] 251 | 252 | [[package]] 253 | name = "core-video-sys" 254 | version = "0.1.4" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" 257 | dependencies = [ 258 | "cfg-if 0.1.10", 259 | "core-foundation-sys 0.7.0", 260 | "core-graphics 0.19.2", 261 | "libc", 262 | "objc", 263 | ] 264 | 265 | [[package]] 266 | name = "cty" 267 | version = "0.2.2" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" 270 | 271 | [[package]] 272 | name = "d3d12" 273 | version = "0.4.1" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c" 276 | dependencies = [ 277 | "bitflags", 278 | "libloading", 279 | "winapi", 280 | ] 281 | 282 | [[package]] 283 | name = "darling" 284 | version = "0.13.4" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" 287 | dependencies = [ 288 | "darling_core", 289 | "darling_macro", 290 | ] 291 | 292 | [[package]] 293 | name = "darling_core" 294 | version = "0.13.4" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" 297 | dependencies = [ 298 | "fnv", 299 | "ident_case", 300 | "proc-macro2", 301 | "quote", 302 | "strsim", 303 | "syn", 304 | ] 305 | 306 | [[package]] 307 | name = "darling_macro" 308 | version = "0.13.4" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" 311 | dependencies = [ 312 | "darling_core", 313 | "quote", 314 | "syn", 315 | ] 316 | 317 | [[package]] 318 | name = "dispatch" 319 | version = "0.2.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" 322 | 323 | [[package]] 324 | name = "dlib" 325 | version = "0.5.0" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" 328 | dependencies = [ 329 | "libloading", 330 | ] 331 | 332 | [[package]] 333 | name = "downcast-rs" 334 | version = "1.2.0" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 337 | 338 | [[package]] 339 | name = "env_logger" 340 | version = "0.9.0" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" 343 | dependencies = [ 344 | "atty", 345 | "humantime", 346 | "log", 347 | "regex", 348 | "termcolor", 349 | ] 350 | 351 | [[package]] 352 | name = "example_app" 353 | version = "0.1.0" 354 | dependencies = [ 355 | "env_logger", 356 | "log", 357 | "pixels", 358 | "winit", 359 | "winit_input_helper", 360 | ] 361 | 362 | [[package]] 363 | name = "fnv" 364 | version = "1.0.7" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 367 | 368 | [[package]] 369 | name = "foreign-types" 370 | version = "0.3.2" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 373 | dependencies = [ 374 | "foreign-types-shared", 375 | ] 376 | 377 | [[package]] 378 | name = "foreign-types-shared" 379 | version = "0.1.1" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 382 | 383 | [[package]] 384 | name = "fxhash" 385 | version = "0.2.1" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 388 | dependencies = [ 389 | "byteorder", 390 | ] 391 | 392 | [[package]] 393 | name = "getrandom" 394 | version = "0.2.7" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 397 | dependencies = [ 398 | "cfg-if 1.0.0", 399 | "libc", 400 | "wasi", 401 | ] 402 | 403 | [[package]] 404 | name = "glow" 405 | version = "0.11.2" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919" 408 | dependencies = [ 409 | "js-sys", 410 | "slotmap", 411 | "wasm-bindgen", 412 | "web-sys", 413 | ] 414 | 415 | [[package]] 416 | name = "gpu-alloc" 417 | version = "0.5.3" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d" 420 | dependencies = [ 421 | "bitflags", 422 | "gpu-alloc-types", 423 | ] 424 | 425 | [[package]] 426 | name = "gpu-alloc-types" 427 | version = "0.2.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5" 430 | dependencies = [ 431 | "bitflags", 432 | ] 433 | 434 | [[package]] 435 | name = "gpu-descriptor" 436 | version = "0.2.3" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a" 439 | dependencies = [ 440 | "bitflags", 441 | "gpu-descriptor-types", 442 | "hashbrown", 443 | ] 444 | 445 | [[package]] 446 | name = "gpu-descriptor-types" 447 | version = "0.1.1" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126" 450 | dependencies = [ 451 | "bitflags", 452 | ] 453 | 454 | [[package]] 455 | name = "hashbrown" 456 | version = "0.12.3" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 459 | dependencies = [ 460 | "ahash", 461 | ] 462 | 463 | [[package]] 464 | name = "hermit-abi" 465 | version = "0.1.19" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 468 | dependencies = [ 469 | "libc", 470 | ] 471 | 472 | [[package]] 473 | name = "hexf-parse" 474 | version = "0.2.1" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" 477 | 478 | [[package]] 479 | name = "humantime" 480 | version = "2.1.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 483 | 484 | [[package]] 485 | name = "ident_case" 486 | version = "1.0.1" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 489 | 490 | [[package]] 491 | name = "indexmap" 492 | version = "1.9.1" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 495 | dependencies = [ 496 | "autocfg", 497 | "hashbrown", 498 | ] 499 | 500 | [[package]] 501 | name = "inplace_it" 502 | version = "0.3.4" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "67f0347836f3f6362c1e7efdadde2b1c4b4556d211310b70631bae7eb692070b" 505 | 506 | [[package]] 507 | name = "instant" 508 | version = "0.1.12" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 511 | dependencies = [ 512 | "cfg-if 1.0.0", 513 | "js-sys", 514 | "wasm-bindgen", 515 | "web-sys", 516 | ] 517 | 518 | [[package]] 519 | name = "jni-sys" 520 | version = "0.3.0" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" 523 | 524 | [[package]] 525 | name = "js-sys" 526 | version = "0.3.59" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" 529 | dependencies = [ 530 | "wasm-bindgen", 531 | ] 532 | 533 | [[package]] 534 | name = "khronos-egl" 535 | version = "4.1.0" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" 538 | dependencies = [ 539 | "libc", 540 | "libloading", 541 | ] 542 | 543 | [[package]] 544 | name = "lazy_static" 545 | version = "1.4.0" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 548 | 549 | [[package]] 550 | name = "libc" 551 | version = "0.2.132" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" 554 | 555 | [[package]] 556 | name = "libloading" 557 | version = "0.7.3" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" 560 | dependencies = [ 561 | "cfg-if 1.0.0", 562 | "winapi", 563 | ] 564 | 565 | [[package]] 566 | name = "lock_api" 567 | version = "0.4.7" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" 570 | dependencies = [ 571 | "autocfg", 572 | "scopeguard", 573 | ] 574 | 575 | [[package]] 576 | name = "log" 577 | version = "0.4.17" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 580 | dependencies = [ 581 | "cfg-if 1.0.0", 582 | ] 583 | 584 | [[package]] 585 | name = "malloc_buf" 586 | version = "0.0.6" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 589 | dependencies = [ 590 | "libc", 591 | ] 592 | 593 | [[package]] 594 | name = "memchr" 595 | version = "2.5.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 598 | 599 | [[package]] 600 | name = "memmap2" 601 | version = "0.3.1" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" 604 | dependencies = [ 605 | "libc", 606 | ] 607 | 608 | [[package]] 609 | name = "memoffset" 610 | version = "0.6.5" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 613 | dependencies = [ 614 | "autocfg", 615 | ] 616 | 617 | [[package]] 618 | name = "metal" 619 | version = "0.23.1" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "e0514f491f4cc03632ab399ee01e2c1c1b12d3e1cf2d667c1ff5f87d6dcd2084" 622 | dependencies = [ 623 | "bitflags", 624 | "block", 625 | "core-graphics-types", 626 | "foreign-types", 627 | "log", 628 | "objc", 629 | ] 630 | 631 | [[package]] 632 | name = "minimal-lexical" 633 | version = "0.2.1" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 636 | 637 | [[package]] 638 | name = "mio" 639 | version = "0.8.4" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" 642 | dependencies = [ 643 | "libc", 644 | "log", 645 | "wasi", 646 | "windows-sys", 647 | ] 648 | 649 | [[package]] 650 | name = "naga" 651 | version = "0.8.5" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "3012f2dbcc79e8e0b5825a4836a7106a75dd9b2fe42c528163be0f572538c705" 654 | dependencies = [ 655 | "bit-set", 656 | "bitflags", 657 | "codespan-reporting", 658 | "hexf-parse", 659 | "indexmap", 660 | "log", 661 | "num-traits", 662 | "rustc-hash", 663 | "spirv", 664 | "thiserror", 665 | ] 666 | 667 | [[package]] 668 | name = "ndk" 669 | version = "0.5.0" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" 672 | dependencies = [ 673 | "bitflags", 674 | "jni-sys", 675 | "ndk-sys", 676 | "num_enum", 677 | "thiserror", 678 | ] 679 | 680 | [[package]] 681 | name = "ndk-context" 682 | version = "0.1.1" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" 685 | 686 | [[package]] 687 | name = "ndk-glue" 688 | version = "0.5.2" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "c71bee8ea72d685477e28bd004cfe1bf99c754d688cd78cad139eae4089484d4" 691 | dependencies = [ 692 | "lazy_static", 693 | "libc", 694 | "log", 695 | "ndk", 696 | "ndk-context", 697 | "ndk-macro", 698 | "ndk-sys", 699 | ] 700 | 701 | [[package]] 702 | name = "ndk-macro" 703 | version = "0.3.0" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" 706 | dependencies = [ 707 | "darling", 708 | "proc-macro-crate", 709 | "proc-macro2", 710 | "quote", 711 | "syn", 712 | ] 713 | 714 | [[package]] 715 | name = "ndk-sys" 716 | version = "0.2.2" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" 719 | 720 | [[package]] 721 | name = "nix" 722 | version = "0.22.3" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" 725 | dependencies = [ 726 | "bitflags", 727 | "cc", 728 | "cfg-if 1.0.0", 729 | "libc", 730 | "memoffset", 731 | ] 732 | 733 | [[package]] 734 | name = "nix" 735 | version = "0.24.2" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" 738 | dependencies = [ 739 | "bitflags", 740 | "cfg-if 1.0.0", 741 | "libc", 742 | "memoffset", 743 | ] 744 | 745 | [[package]] 746 | name = "nom" 747 | version = "7.1.1" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" 750 | dependencies = [ 751 | "memchr", 752 | "minimal-lexical", 753 | ] 754 | 755 | [[package]] 756 | name = "num-traits" 757 | version = "0.2.15" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 760 | dependencies = [ 761 | "autocfg", 762 | ] 763 | 764 | [[package]] 765 | name = "num_enum" 766 | version = "0.5.7" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" 769 | dependencies = [ 770 | "num_enum_derive", 771 | ] 772 | 773 | [[package]] 774 | name = "num_enum_derive" 775 | version = "0.5.7" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" 778 | dependencies = [ 779 | "proc-macro-crate", 780 | "proc-macro2", 781 | "quote", 782 | "syn", 783 | ] 784 | 785 | [[package]] 786 | name = "objc" 787 | version = "0.2.7" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 790 | dependencies = [ 791 | "malloc_buf", 792 | "objc_exception", 793 | ] 794 | 795 | [[package]] 796 | name = "objc_exception" 797 | version = "0.1.2" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" 800 | dependencies = [ 801 | "cc", 802 | ] 803 | 804 | [[package]] 805 | name = "once_cell" 806 | version = "1.13.1" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" 809 | 810 | [[package]] 811 | name = "parking_lot" 812 | version = "0.11.2" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 815 | dependencies = [ 816 | "instant", 817 | "lock_api", 818 | "parking_lot_core", 819 | ] 820 | 821 | [[package]] 822 | name = "parking_lot_core" 823 | version = "0.8.5" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 826 | dependencies = [ 827 | "cfg-if 1.0.0", 828 | "instant", 829 | "libc", 830 | "redox_syscall", 831 | "smallvec", 832 | "winapi", 833 | ] 834 | 835 | [[package]] 836 | name = "percent-encoding" 837 | version = "2.1.0" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 840 | 841 | [[package]] 842 | name = "pixels" 843 | version = "0.9.0" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "a3be4951b24b374c72b0aaaeb59e51e1acb38b8e680b11f43c1d0049b8550891" 846 | dependencies = [ 847 | "bytemuck", 848 | "pollster", 849 | "raw-window-handle", 850 | "thiserror", 851 | "ultraviolet", 852 | "wgpu", 853 | ] 854 | 855 | [[package]] 856 | name = "pkg-config" 857 | version = "0.3.25" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 860 | 861 | [[package]] 862 | name = "pollster" 863 | version = "0.2.5" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" 866 | 867 | [[package]] 868 | name = "proc-macro-crate" 869 | version = "1.2.1" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" 872 | dependencies = [ 873 | "once_cell", 874 | "thiserror", 875 | "toml", 876 | ] 877 | 878 | [[package]] 879 | name = "proc-macro2" 880 | version = "1.0.43" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" 883 | dependencies = [ 884 | "unicode-ident", 885 | ] 886 | 887 | [[package]] 888 | name = "profiling" 889 | version = "1.0.6" 890 | source = "registry+https://github.com/rust-lang/crates.io-index" 891 | checksum = "2f61dcf0b917cd75d4521d7343d1ffff3d1583054133c9b5cbea3375c703c40d" 892 | 893 | [[package]] 894 | name = "quote" 895 | version = "1.0.21" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 898 | dependencies = [ 899 | "proc-macro2", 900 | ] 901 | 902 | [[package]] 903 | name = "range-alloc" 904 | version = "0.1.2" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6" 907 | 908 | [[package]] 909 | name = "raw-window-handle" 910 | version = "0.4.3" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" 913 | dependencies = [ 914 | "cty", 915 | ] 916 | 917 | [[package]] 918 | name = "redox_syscall" 919 | version = "0.2.16" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 922 | dependencies = [ 923 | "bitflags", 924 | ] 925 | 926 | [[package]] 927 | name = "regex" 928 | version = "1.6.0" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 931 | dependencies = [ 932 | "aho-corasick", 933 | "memchr", 934 | "regex-syntax", 935 | ] 936 | 937 | [[package]] 938 | name = "regex-syntax" 939 | version = "0.6.27" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 942 | 943 | [[package]] 944 | name = "renderdoc-sys" 945 | version = "0.7.1" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" 948 | 949 | [[package]] 950 | name = "rustc-hash" 951 | version = "1.1.0" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 954 | 955 | [[package]] 956 | name = "safe_arch" 957 | version = "0.5.2" 958 | source = "registry+https://github.com/rust-lang/crates.io-index" 959 | checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" 960 | dependencies = [ 961 | "bytemuck", 962 | ] 963 | 964 | [[package]] 965 | name = "scoped-tls" 966 | version = "1.0.0" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 969 | 970 | [[package]] 971 | name = "scopeguard" 972 | version = "1.1.0" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 975 | 976 | [[package]] 977 | name = "serde" 978 | version = "1.0.144" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" 981 | 982 | [[package]] 983 | name = "slotmap" 984 | version = "1.0.6" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" 987 | dependencies = [ 988 | "version_check", 989 | ] 990 | 991 | [[package]] 992 | name = "smallvec" 993 | version = "1.9.0" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" 996 | 997 | [[package]] 998 | name = "smithay-client-toolkit" 999 | version = "0.15.4" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3" 1002 | dependencies = [ 1003 | "bitflags", 1004 | "calloop", 1005 | "dlib", 1006 | "lazy_static", 1007 | "log", 1008 | "memmap2", 1009 | "nix 0.22.3", 1010 | "pkg-config", 1011 | "wayland-client", 1012 | "wayland-cursor", 1013 | "wayland-protocols", 1014 | ] 1015 | 1016 | [[package]] 1017 | name = "spirv" 1018 | version = "0.2.0+1.5.4" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" 1021 | dependencies = [ 1022 | "bitflags", 1023 | "num-traits", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "strsim" 1028 | version = "0.10.0" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1031 | 1032 | [[package]] 1033 | name = "syn" 1034 | version = "1.0.99" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" 1037 | dependencies = [ 1038 | "proc-macro2", 1039 | "quote", 1040 | "unicode-ident", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "termcolor" 1045 | version = "1.1.3" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 1048 | dependencies = [ 1049 | "winapi-util", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "thiserror" 1054 | version = "1.0.32" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" 1057 | dependencies = [ 1058 | "thiserror-impl", 1059 | ] 1060 | 1061 | [[package]] 1062 | name = "thiserror-impl" 1063 | version = "1.0.32" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" 1066 | dependencies = [ 1067 | "proc-macro2", 1068 | "quote", 1069 | "syn", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "toml" 1074 | version = "0.5.9" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" 1077 | dependencies = [ 1078 | "serde", 1079 | ] 1080 | 1081 | [[package]] 1082 | name = "ultraviolet" 1083 | version = "0.8.1" 1084 | source = "registry+https://github.com/rust-lang/crates.io-index" 1085 | checksum = "16b9e3507eba17043af05c8a72fce3ec2c24b58945f45732e71dbc6646d904a7" 1086 | dependencies = [ 1087 | "wide", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "unicode-ident" 1092 | version = "1.0.3" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" 1095 | 1096 | [[package]] 1097 | name = "unicode-width" 1098 | version = "0.1.9" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 1101 | 1102 | [[package]] 1103 | name = "version_check" 1104 | version = "0.9.4" 1105 | source = "registry+https://github.com/rust-lang/crates.io-index" 1106 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1107 | 1108 | [[package]] 1109 | name = "wasi" 1110 | version = "0.11.0+wasi-snapshot-preview1" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1113 | 1114 | [[package]] 1115 | name = "wasm-bindgen" 1116 | version = "0.2.82" 1117 | source = "registry+https://github.com/rust-lang/crates.io-index" 1118 | checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" 1119 | dependencies = [ 1120 | "cfg-if 1.0.0", 1121 | "wasm-bindgen-macro", 1122 | ] 1123 | 1124 | [[package]] 1125 | name = "wasm-bindgen-backend" 1126 | version = "0.2.82" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" 1129 | dependencies = [ 1130 | "bumpalo", 1131 | "log", 1132 | "once_cell", 1133 | "proc-macro2", 1134 | "quote", 1135 | "syn", 1136 | "wasm-bindgen-shared", 1137 | ] 1138 | 1139 | [[package]] 1140 | name = "wasm-bindgen-futures" 1141 | version = "0.4.32" 1142 | source = "registry+https://github.com/rust-lang/crates.io-index" 1143 | checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" 1144 | dependencies = [ 1145 | "cfg-if 1.0.0", 1146 | "js-sys", 1147 | "wasm-bindgen", 1148 | "web-sys", 1149 | ] 1150 | 1151 | [[package]] 1152 | name = "wasm-bindgen-macro" 1153 | version = "0.2.82" 1154 | source = "registry+https://github.com/rust-lang/crates.io-index" 1155 | checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" 1156 | dependencies = [ 1157 | "quote", 1158 | "wasm-bindgen-macro-support", 1159 | ] 1160 | 1161 | [[package]] 1162 | name = "wasm-bindgen-macro-support" 1163 | version = "0.2.82" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" 1166 | dependencies = [ 1167 | "proc-macro2", 1168 | "quote", 1169 | "syn", 1170 | "wasm-bindgen-backend", 1171 | "wasm-bindgen-shared", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "wasm-bindgen-shared" 1176 | version = "0.2.82" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" 1179 | 1180 | [[package]] 1181 | name = "wayland-client" 1182 | version = "0.29.5" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" 1185 | dependencies = [ 1186 | "bitflags", 1187 | "downcast-rs", 1188 | "libc", 1189 | "nix 0.24.2", 1190 | "scoped-tls", 1191 | "wayland-commons", 1192 | "wayland-scanner", 1193 | "wayland-sys", 1194 | ] 1195 | 1196 | [[package]] 1197 | name = "wayland-commons" 1198 | version = "0.29.5" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" 1201 | dependencies = [ 1202 | "nix 0.24.2", 1203 | "once_cell", 1204 | "smallvec", 1205 | "wayland-sys", 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "wayland-cursor" 1210 | version = "0.29.5" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" 1213 | dependencies = [ 1214 | "nix 0.24.2", 1215 | "wayland-client", 1216 | "xcursor", 1217 | ] 1218 | 1219 | [[package]] 1220 | name = "wayland-protocols" 1221 | version = "0.29.5" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" 1224 | dependencies = [ 1225 | "bitflags", 1226 | "wayland-client", 1227 | "wayland-commons", 1228 | "wayland-scanner", 1229 | ] 1230 | 1231 | [[package]] 1232 | name = "wayland-scanner" 1233 | version = "0.29.5" 1234 | source = "registry+https://github.com/rust-lang/crates.io-index" 1235 | checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" 1236 | dependencies = [ 1237 | "proc-macro2", 1238 | "quote", 1239 | "xml-rs", 1240 | ] 1241 | 1242 | [[package]] 1243 | name = "wayland-sys" 1244 | version = "0.29.5" 1245 | source = "registry+https://github.com/rust-lang/crates.io-index" 1246 | checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" 1247 | dependencies = [ 1248 | "dlib", 1249 | "lazy_static", 1250 | "pkg-config", 1251 | ] 1252 | 1253 | [[package]] 1254 | name = "web-sys" 1255 | version = "0.3.57" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" 1258 | dependencies = [ 1259 | "js-sys", 1260 | "wasm-bindgen", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "wgpu" 1265 | version = "0.12.0" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "b97cd781ff044d6d697b632a2e212032c2e957d1afaa21dbf58069cbb8f78567" 1268 | dependencies = [ 1269 | "arrayvec", 1270 | "js-sys", 1271 | "log", 1272 | "naga", 1273 | "parking_lot", 1274 | "raw-window-handle", 1275 | "smallvec", 1276 | "wasm-bindgen", 1277 | "wasm-bindgen-futures", 1278 | "web-sys", 1279 | "wgpu-core", 1280 | "wgpu-hal", 1281 | "wgpu-types", 1282 | ] 1283 | 1284 | [[package]] 1285 | name = "wgpu-core" 1286 | version = "0.12.2" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "c4688c000eb841ca55f7b35db659b78d6e1cd77d7caf8fb929f4e181f754047d" 1289 | dependencies = [ 1290 | "arrayvec", 1291 | "bitflags", 1292 | "cfg_aliases", 1293 | "codespan-reporting", 1294 | "copyless", 1295 | "fxhash", 1296 | "log", 1297 | "naga", 1298 | "parking_lot", 1299 | "profiling", 1300 | "raw-window-handle", 1301 | "smallvec", 1302 | "thiserror", 1303 | "wgpu-hal", 1304 | "wgpu-types", 1305 | ] 1306 | 1307 | [[package]] 1308 | name = "wgpu-hal" 1309 | version = "0.12.5" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "d684ea6a34974a2fc19f1dfd183d11a62e22d75c4f187a574bb1224df8e056c2" 1312 | dependencies = [ 1313 | "arrayvec", 1314 | "ash", 1315 | "bit-set", 1316 | "bitflags", 1317 | "block", 1318 | "core-graphics-types", 1319 | "d3d12", 1320 | "foreign-types", 1321 | "fxhash", 1322 | "glow", 1323 | "gpu-alloc", 1324 | "gpu-descriptor", 1325 | "inplace_it", 1326 | "js-sys", 1327 | "khronos-egl", 1328 | "libloading", 1329 | "log", 1330 | "metal", 1331 | "naga", 1332 | "objc", 1333 | "parking_lot", 1334 | "profiling", 1335 | "range-alloc", 1336 | "raw-window-handle", 1337 | "renderdoc-sys", 1338 | "thiserror", 1339 | "wasm-bindgen", 1340 | "web-sys", 1341 | "wgpu-types", 1342 | "winapi", 1343 | ] 1344 | 1345 | [[package]] 1346 | name = "wgpu-types" 1347 | version = "0.12.0" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "549533d9e1cdd4b4cda7718d33ff500fc4c34b5467b71d76b547ae0324f3b2a2" 1350 | dependencies = [ 1351 | "bitflags", 1352 | ] 1353 | 1354 | [[package]] 1355 | name = "wide" 1356 | version = "0.6.5" 1357 | source = "registry+https://github.com/rust-lang/crates.io-index" 1358 | checksum = "46bbe7c604a27ca0b05c5503221e76da628225b568e6f1280b42dbad3b72d89b" 1359 | dependencies = [ 1360 | "bytemuck", 1361 | "safe_arch", 1362 | ] 1363 | 1364 | [[package]] 1365 | name = "winapi" 1366 | version = "0.3.9" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1369 | dependencies = [ 1370 | "winapi-i686-pc-windows-gnu", 1371 | "winapi-x86_64-pc-windows-gnu", 1372 | ] 1373 | 1374 | [[package]] 1375 | name = "winapi-i686-pc-windows-gnu" 1376 | version = "0.4.0" 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" 1378 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1379 | 1380 | [[package]] 1381 | name = "winapi-util" 1382 | version = "0.1.5" 1383 | source = "registry+https://github.com/rust-lang/crates.io-index" 1384 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1385 | dependencies = [ 1386 | "winapi", 1387 | ] 1388 | 1389 | [[package]] 1390 | name = "winapi-x86_64-pc-windows-gnu" 1391 | version = "0.4.0" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1394 | 1395 | [[package]] 1396 | name = "windows-sys" 1397 | version = "0.36.1" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 1400 | dependencies = [ 1401 | "windows_aarch64_msvc", 1402 | "windows_i686_gnu", 1403 | "windows_i686_msvc", 1404 | "windows_x86_64_gnu", 1405 | "windows_x86_64_msvc", 1406 | ] 1407 | 1408 | [[package]] 1409 | name = "windows_aarch64_msvc" 1410 | version = "0.36.1" 1411 | source = "registry+https://github.com/rust-lang/crates.io-index" 1412 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 1413 | 1414 | [[package]] 1415 | name = "windows_i686_gnu" 1416 | version = "0.36.1" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 1419 | 1420 | [[package]] 1421 | name = "windows_i686_msvc" 1422 | version = "0.36.1" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 1425 | 1426 | [[package]] 1427 | name = "windows_x86_64_gnu" 1428 | version = "0.36.1" 1429 | source = "registry+https://github.com/rust-lang/crates.io-index" 1430 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 1431 | 1432 | [[package]] 1433 | name = "windows_x86_64_msvc" 1434 | version = "0.36.1" 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" 1436 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 1437 | 1438 | [[package]] 1439 | name = "winit" 1440 | version = "0.26.1" 1441 | source = "registry+https://github.com/rust-lang/crates.io-index" 1442 | checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a" 1443 | dependencies = [ 1444 | "bitflags", 1445 | "cocoa", 1446 | "core-foundation 0.9.3", 1447 | "core-graphics 0.22.3", 1448 | "core-video-sys", 1449 | "dispatch", 1450 | "instant", 1451 | "lazy_static", 1452 | "libc", 1453 | "log", 1454 | "mio", 1455 | "ndk", 1456 | "ndk-glue", 1457 | "ndk-sys", 1458 | "objc", 1459 | "parking_lot", 1460 | "percent-encoding", 1461 | "raw-window-handle", 1462 | "smithay-client-toolkit", 1463 | "wasm-bindgen", 1464 | "wayland-client", 1465 | "wayland-protocols", 1466 | "web-sys", 1467 | "winapi", 1468 | "x11-dl", 1469 | ] 1470 | 1471 | [[package]] 1472 | name = "winit_input_helper" 1473 | version = "0.12.0" 1474 | source = "registry+https://github.com/rust-lang/crates.io-index" 1475 | checksum = "f114b4fe1f38c80027543eb8a0711634fb9136a5cd18e8e990122aaea21f9302" 1476 | dependencies = [ 1477 | "winit", 1478 | ] 1479 | 1480 | [[package]] 1481 | name = "x11-dl" 1482 | version = "2.20.0" 1483 | source = "registry+https://github.com/rust-lang/crates.io-index" 1484 | checksum = "0c83627bc137605acc00bb399c7b908ef460b621fc37c953db2b09f88c449ea6" 1485 | dependencies = [ 1486 | "lazy_static", 1487 | "libc", 1488 | "pkg-config", 1489 | ] 1490 | 1491 | [[package]] 1492 | name = "xcursor" 1493 | version = "0.3.4" 1494 | source = "registry+https://github.com/rust-lang/crates.io-index" 1495 | checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" 1496 | dependencies = [ 1497 | "nom", 1498 | ] 1499 | 1500 | [[package]] 1501 | name = "xml-rs" 1502 | version = "0.8.4" 1503 | source = "registry+https://github.com/rust-lang/crates.io-index" 1504 | checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" 1505 | -------------------------------------------------------------------------------- /example_app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example_app" 3 | version = "0.2.1" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | env_logger = "0.10" 10 | log = "0.4" 11 | pixels = "0.12.0" 12 | winit = "0.28.3" 13 | winit_input_helper = "0.14" 14 | code_editor = { path = "../code_editor" } 15 | -------------------------------------------------------------------------------- /example_app/src/main.rs: -------------------------------------------------------------------------------- 1 | const TICK_IN_MS : u128 = 250; 2 | 3 | use log::error; 4 | use pixels::{Error, Pixels, SurfaceTexture}; 5 | use winit::dpi::LogicalSize; 6 | use winit::event::{Event, DeviceEvent, WindowEvent}; 7 | use winit::event_loop::{ControlFlow, EventLoop}; 8 | use winit::window::WindowBuilder; 9 | use winit_input_helper::WinitInputHelper; 10 | use winit::event::KeyboardInput; 11 | 12 | use std::time::{Duration, SystemTime, UNIX_EPOCH}; 13 | 14 | use code_editor::prelude::*; 15 | 16 | /// Gets the current time in milliseconds 17 | fn get_time() -> u128 { 18 | let stop = SystemTime::now() 19 | .duration_since(UNIX_EPOCH) 20 | .expect("Time went backwards"); 21 | stop.as_millis() 22 | } 23 | 24 | fn main() -> Result<(), Error> { 25 | 26 | let mut width : usize = 600; 27 | let mut height : usize = 400; 28 | 29 | env_logger::init(); 30 | 31 | let event_loop = EventLoop::new(); 32 | let mut input = WinitInputHelper::new(); 33 | let window = { 34 | let size = LogicalSize::new(width as f64, height as f64); 35 | 36 | WindowBuilder::new() 37 | .with_title("CodeEditor") 38 | .with_inner_size(size) 39 | .with_min_inner_size(size) 40 | 41 | .build(&event_loop) 42 | .unwrap() 43 | }; 44 | 45 | let mut pixels = { 46 | let window_size = window.inner_size(); 47 | let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window); 48 | Pixels::new(width as u32, height as u32, surface_texture)? 49 | }; 50 | 51 | // Init the code editor 52 | 53 | let mut code_editor = CodeEditor::new(); 54 | code_editor.set_font("fonts/Source_Code_Pro/static/SourceCodePro-Regular.ttf"); 55 | code_editor.set_mode(CodeEditorMode::Rhai); 56 | code_editor.set_font_size(17.0); 57 | 58 | // Taken from https://github.com/rhaiscript/rhai/blob/main/scripts/fibonacci.rhai 59 | code_editor.set_text( 60 | r#"//! This script calculates the n-th Fibonacci number using a really dumb algorithm 61 | //! to test the speed of the scripting engine. 62 | 63 | const TARGET = 28; 64 | const REPEAT = 5; 65 | const ANSWER = 317811; 66 | 67 | fn fib(n) { 68 | if n < 2 { 69 | n 70 | } else { 71 | fib(n-1) + fib(n-2) 72 | } 73 | } 74 | 75 | print(`Running Fibonacci(${TARGET}) x ${REPEAT} times...`); 76 | print("Ready... Go!"); 77 | 78 | let result; 79 | let now = timestamp(); 80 | 81 | for n in 0..REPEAT { 82 | result = fib(TARGET); 83 | } 84 | 85 | print(`Finished. Run time = ${now.elapsed} seconds.`); 86 | 87 | print(`Fibonacci number #${TARGET} = ${result}`); 88 | 89 | if result != ANSWER { 90 | print(`The answer is WRONG! Should be ${ANSWER}!`); 91 | } 92 | "#.to_string()); 93 | 94 | let mut timer : u128 = 0; 95 | 96 | event_loop.run(move |event, _, control_flow| { 97 | use winit::event::{ElementState, VirtualKeyCode}; 98 | 99 | if let Event::RedrawRequested(_) = event { 100 | 101 | let frame = pixels.frame_mut(); 102 | code_editor.draw(frame, (0, 0, width, height), width); 103 | 104 | if pixels 105 | .render() 106 | .map_err(|e| error!("pixels.render() failed: {}", e)) 107 | .is_err() 108 | { 109 | *control_flow = ControlFlow::Exit; 110 | return; 111 | } 112 | } 113 | 114 | match &event { 115 | 116 | Event::WindowEvent { event, .. } => match event { 117 | WindowEvent::ReceivedCharacter(char ) => match char { 118 | _ => { 119 | if code_editor.key_down(Some(*char), None) { 120 | window.request_redraw(); 121 | } 122 | } 123 | }, 124 | 125 | WindowEvent::ModifiersChanged(state) => match state { 126 | _ => { 127 | if code_editor.modifier_changed(state.shift(), state.ctrl(), state.alt(), state.logo()) { 128 | window.request_redraw(); 129 | } 130 | } 131 | }, 132 | 133 | WindowEvent::KeyboardInput { 134 | input: 135 | KeyboardInput { 136 | virtual_keycode: Some(virtual_code), 137 | state: ElementState::Pressed, 138 | .. 139 | }, 140 | .. 141 | } => match virtual_code { 142 | VirtualKeyCode::Delete => { 143 | if code_editor.key_down(None, Some(WidgetKey::Delete)) { 144 | window.request_redraw(); 145 | } 146 | }, 147 | VirtualKeyCode::Back => { 148 | if code_editor.key_down(None, Some(WidgetKey::Delete)) { 149 | window.request_redraw(); 150 | } 151 | }, 152 | VirtualKeyCode::Up => { 153 | if code_editor.key_down(None, Some(WidgetKey::Up)) { 154 | window.request_redraw(); 155 | } 156 | }, 157 | VirtualKeyCode::Right => { 158 | if code_editor.key_down(None, Some(WidgetKey::Right)) { 159 | window.request_redraw(); 160 | } 161 | }, 162 | VirtualKeyCode::Down => { 163 | if code_editor.key_down(None, Some(WidgetKey::Down)) { 164 | window.request_redraw(); 165 | } 166 | }, 167 | VirtualKeyCode::Left => { 168 | if code_editor.key_down(None, Some(WidgetKey::Left)) { 169 | window.request_redraw(); 170 | } 171 | }, 172 | VirtualKeyCode::Space => { 173 | if code_editor.key_down(None, Some(WidgetKey::Space)) { 174 | window.request_redraw(); 175 | } 176 | }, 177 | VirtualKeyCode::Tab => { 178 | if code_editor.key_down(None, Some(WidgetKey::Tab)) { 179 | window.request_redraw(); 180 | } 181 | }, 182 | VirtualKeyCode::Return => { 183 | if code_editor.key_down(None, Some(WidgetKey::Return)) { 184 | window.request_redraw(); 185 | } 186 | }, 187 | VirtualKeyCode::Escape => { 188 | if code_editor.key_down(None, Some(WidgetKey::Escape)) { 189 | window.request_redraw(); 190 | } 191 | } 192 | _ => (), 193 | }, 194 | _ => (), 195 | }, 196 | 197 | Event::DeviceEvent { event, .. } => match event { 198 | DeviceEvent::Text { codepoint } => { 199 | println!("text: ({})", codepoint); 200 | } 201 | DeviceEvent::MouseWheel { delta } => match delta { 202 | winit::event::MouseScrollDelta::LineDelta(x, y) => { 203 | println!("mouse wheel Line Delta: ({},{})", x, y); 204 | } 205 | winit::event::MouseScrollDelta::PixelDelta(p) => { 206 | //println!("mouse wheel Pixel Delta: ({},{})", p.x, p.y); 207 | if code_editor.mouse_wheel((p.x as isize, p.y as isize)) { 208 | window.request_redraw(); 209 | //mouse_wheel_ongoing = true; 210 | } 211 | 212 | if p.x == 0.0 && p.y == 0.0 { 213 | //mouse_wheel_ongoing = false; 214 | } 215 | } 216 | }, 217 | _ => (), 218 | }, 219 | _ => (), 220 | } 221 | 222 | // Handle input events 223 | if input.update(&event) { 224 | // Close events 225 | if /*input.key_pressed(VirtualKeyCode::Escape) ||*/ input.close_requested() { 226 | *control_flow = ControlFlow::Exit; 227 | return; 228 | } 229 | 230 | if input.mouse_pressed(0) { 231 | let coords = input.mouse().unwrap(); 232 | let pixel_pos: (usize, usize) = pixels.window_pos_to_pixel(coords) 233 | .unwrap_or_else(|pos| pixels.clamp_pixel_pos(pos)); 234 | 235 | if code_editor.mouse_down(pixel_pos) { 236 | window.request_redraw(); 237 | } 238 | } 239 | 240 | if input.mouse_released(0) { 241 | let coords = input.mouse().unwrap(); 242 | let pixel_pos: (usize, usize) = pixels.window_pos_to_pixel(coords) 243 | .unwrap_or_else(|pos| pixels.clamp_pixel_pos(pos)); 244 | 245 | if code_editor.mouse_up(pixel_pos) { 246 | window.request_redraw(); 247 | } 248 | } 249 | 250 | if input.mouse_held(0) { 251 | let diff = input.mouse_diff(); 252 | if diff.0 != 0.0 || diff.1 != 0.0 { 253 | let coords = input.mouse().unwrap(); 254 | let pixel_pos: (usize, usize) = pixels.window_pos_to_pixel(coords) 255 | .unwrap_or_else(|pos| pixels.clamp_pixel_pos(pos)); 256 | 257 | if code_editor.mouse_dragged(pixel_pos) { 258 | window.request_redraw(); 259 | } 260 | } 261 | } else { 262 | let diff = input.mouse_diff(); 263 | if diff.0 != 0.0 || diff.1 != 0.0 { 264 | let coords = input.mouse().unwrap(); 265 | let pixel_pos: (usize, usize) = pixels.window_pos_to_pixel(coords) 266 | .unwrap_or_else(|pos| pixels.clamp_pixel_pos(pos)); 267 | 268 | if code_editor.mouse_hover(pixel_pos) { 269 | window.request_redraw(); 270 | } 271 | } 272 | } 273 | 274 | // Resize the window 275 | if let Some(size) = input.window_resized() { 276 | _ = pixels.resize_surface(size.width, size.height); 277 | let scale = window.scale_factor() as u32; 278 | _ = pixels.resize_buffer(size.width / scale, size.height / scale); 279 | width = size.width as usize / scale as usize; 280 | height = size.height as usize / scale as usize; 281 | window.request_redraw(); 282 | } 283 | 284 | let curr_time = get_time(); 285 | 286 | // We update the screen 4 times a second, change TICK_IN_MS to change the refresh rate 287 | 288 | if curr_time > timer + TICK_IN_MS { 289 | window.request_redraw(); 290 | timer = curr_time; 291 | } else { 292 | let t = (timer + TICK_IN_MS - curr_time) as u64; 293 | if t > 10 { 294 | std::thread::sleep(Duration::from_millis(10)); 295 | } 296 | } 297 | } 298 | }); 299 | } 300 | -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/README.txt: -------------------------------------------------------------------------------- 1 | Source Code Pro Variable Font 2 | ============================= 3 | 4 | This download contains Source Code Pro as both variable fonts and static fonts. 5 | 6 | Source Code Pro is a variable font with this axis: 7 | wght 8 | 9 | This means all the styles are contained in these files: 10 | SourceCodePro-VariableFont_wght.ttf 11 | SourceCodePro-Italic-VariableFont_wght.ttf 12 | 13 | If your app fully supports variable fonts, you can now pick intermediate styles 14 | that aren’t available as static fonts. Not all apps support variable fonts, and 15 | in those cases you can use the static font files for Source Code Pro: 16 | static/SourceCodePro-ExtraLight.ttf 17 | static/SourceCodePro-Light.ttf 18 | static/SourceCodePro-Regular.ttf 19 | static/SourceCodePro-Medium.ttf 20 | static/SourceCodePro-SemiBold.ttf 21 | static/SourceCodePro-Bold.ttf 22 | static/SourceCodePro-ExtraBold.ttf 23 | static/SourceCodePro-Black.ttf 24 | static/SourceCodePro-ExtraLightItalic.ttf 25 | static/SourceCodePro-LightItalic.ttf 26 | static/SourceCodePro-Italic.ttf 27 | static/SourceCodePro-MediumItalic.ttf 28 | static/SourceCodePro-SemiBoldItalic.ttf 29 | static/SourceCodePro-BoldItalic.ttf 30 | static/SourceCodePro-ExtraBoldItalic.ttf 31 | static/SourceCodePro-BlackItalic.ttf 32 | 33 | Get started 34 | ----------- 35 | 36 | 1. Install the font files you want to use 37 | 38 | 2. Use your app's font picker to view the font family and all the 39 | available styles 40 | 41 | Learn more about variable fonts 42 | ------------------------------- 43 | 44 | https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts 45 | https://variablefonts.typenetwork.com 46 | https://medium.com/variable-fonts 47 | 48 | In desktop apps 49 | 50 | https://theblog.adobe.com/can-variable-fonts-illustrator-cc 51 | https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts 52 | 53 | Online 54 | 55 | https://developers.google.com/fonts/docs/getting_started 56 | https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide 57 | https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts 58 | 59 | Installing fonts 60 | 61 | MacOS: https://support.apple.com/en-us/HT201749 62 | Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux 63 | Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows 64 | 65 | Android Apps 66 | 67 | https://developers.google.com/fonts/docs/android 68 | https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts 69 | 70 | License 71 | ------- 72 | Please read the full license text (OFL.txt) to understand the permissions, 73 | restrictions and requirements for usage, redistribution, and modification. 74 | 75 | You can use them in your products & projects - print or digital, 76 | commercial or otherwise. 77 | 78 | This isn't legal advice, please consider consulting a lawyer and see the full 79 | license for all details. 80 | -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/SourceCodePro-Italic-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/SourceCodePro-Italic-VariableFont_wght.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/SourceCodePro-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/SourceCodePro-VariableFont_wght.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-Black.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-BlackItalic.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-Bold.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-ExtraBold.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-ExtraLight.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-Italic.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-Light.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-Medium.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-MediumItalic.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-SemiBold.ttf -------------------------------------------------------------------------------- /fonts/Source_Code_Pro/static/SourceCodePro-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/fonts/Source_Code_Pro/static/SourceCodePro-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusmoenig/code_editor/e684beee9eb92ea0972877b7940eda5090573759/images/screenshot.png --------------------------------------------------------------------------------