Add Matrix effect
This commit is contained in:
		
							parent
							
								
									dde1af7a6b
								
							
						
					
					
						commit
						c91ea0db01
					
				
					 4 changed files with 333 additions and 3 deletions
				
			
		
							
								
								
									
										97
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										97
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -332,6 +332,18 @@ dependencies = [
 | 
				
			||||||
 "slab",
 | 
					 "slab",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "getrandom"
 | 
				
			||||||
 | 
					version = "0.3.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "cfg-if",
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "r-efi",
 | 
				
			||||||
 | 
					 "wasi 0.14.2+wasi-0.2.4",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "gimli"
 | 
					name = "gimli"
 | 
				
			||||||
version = "0.31.1"
 | 
					version = "0.31.1"
 | 
				
			||||||
| 
						 | 
					@ -489,7 +501,7 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "wasi",
 | 
					 "wasi 0.11.1+wasi-snapshot-preview1",
 | 
				
			||||||
 "windows-sys",
 | 
					 "windows-sys",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -549,6 +561,15 @@ version = "0.1.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 | 
					checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "ppv-lite86"
 | 
				
			||||||
 | 
					version = "0.2.21"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "zerocopy",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "proc-macro2"
 | 
					name = "proc-macro2"
 | 
				
			||||||
version = "1.0.97"
 | 
					version = "1.0.97"
 | 
				
			||||||
| 
						 | 
					@ -567,6 +588,41 @@ dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "r-efi"
 | 
				
			||||||
 | 
					version = "5.3.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rand"
 | 
				
			||||||
 | 
					version = "0.9.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "rand_chacha",
 | 
				
			||||||
 | 
					 "rand_core",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rand_chacha"
 | 
				
			||||||
 | 
					version = "0.9.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "ppv-lite86",
 | 
				
			||||||
 | 
					 "rand_core",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rand_core"
 | 
				
			||||||
 | 
					version = "0.9.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "getrandom",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "ratatui"
 | 
					name = "ratatui"
 | 
				
			||||||
version = "0.29.0"
 | 
					version = "0.29.0"
 | 
				
			||||||
| 
						 | 
					@ -594,6 +650,7 @@ version = "0.1.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "crossterm 0.29.0",
 | 
					 "crossterm 0.29.0",
 | 
				
			||||||
 "futures",
 | 
					 "futures",
 | 
				
			||||||
 | 
					 "rand",
 | 
				
			||||||
 "ratatui",
 | 
					 "ratatui",
 | 
				
			||||||
 "terminput",
 | 
					 "terminput",
 | 
				
			||||||
 "terminput-crossterm",
 | 
					 "terminput-crossterm",
 | 
				
			||||||
| 
						 | 
					@ -866,6 +923,15 @@ version = "0.11.1+wasi-snapshot-preview1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 | 
					checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "wasi"
 | 
				
			||||||
 | 
					version = "0.14.2+wasi-0.2.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "wit-bindgen-rt",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "winapi"
 | 
					name = "winapi"
 | 
				
			||||||
version = "0.3.9"
 | 
					version = "0.3.9"
 | 
				
			||||||
| 
						 | 
					@ -960,3 +1026,32 @@ name = "windows_x86_64_msvc"
 | 
				
			||||||
version = "0.52.6"
 | 
					version = "0.52.6"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 | 
					checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "wit-bindgen-rt"
 | 
				
			||||||
 | 
					version = "0.39.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "bitflags",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "zerocopy"
 | 
				
			||||||
 | 
					version = "0.8.26"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "zerocopy-derive",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "zerocopy-derive"
 | 
				
			||||||
 | 
					version = "0.8.26"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "syn",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ edition = "2024"
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
crossterm = { version = "0.29.0", features = ["event-stream"] }
 | 
					crossterm = { version = "0.29.0", features = ["event-stream"] }
 | 
				
			||||||
futures = "0.3.31"
 | 
					futures = "0.3.31"
 | 
				
			||||||
 | 
					rand = "0.9.2"
 | 
				
			||||||
ratatui = "0.29.0"
 | 
					ratatui = "0.29.0"
 | 
				
			||||||
terminput = "0.5.3"
 | 
					terminput = "0.5.3"
 | 
				
			||||||
terminput-crossterm = "0.3.3"
 | 
					terminput-crossterm = "0.3.3"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										234
									
								
								src/app_matrix.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								src/app_matrix.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,234 @@
 | 
				
			||||||
 | 
					use ratatui::{layout::Rect, style::{Style, Stylize}, text::{Line, Span}, widgets::{Block, Paragraph}};
 | 
				
			||||||
 | 
					use terminput::KeyCode;
 | 
				
			||||||
 | 
					use rand::prelude::*;
 | 
				
			||||||
 | 
					use rand::rng;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::Demo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct MatrixColumn {
 | 
				
			||||||
 | 
					    x: u16,
 | 
				
			||||||
 | 
					    y: u16,
 | 
				
			||||||
 | 
					    length: u16,
 | 
				
			||||||
 | 
					    speed: u16,
 | 
				
			||||||
 | 
					    chars: Vec<char>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl MatrixColumn {
 | 
				
			||||||
 | 
					    fn new(x: u16, _frame_counter: usize) -> Self {
 | 
				
			||||||
 | 
					        let mut rng = rng();
 | 
				
			||||||
 | 
					        let length = rng.random_range(5..15);
 | 
				
			||||||
 | 
					        let speed = rng.random_range(1..4);
 | 
				
			||||||
 | 
					        let chars = (0..length)
 | 
				
			||||||
 | 
					            .map(|_| Self::random_matrix_char(&mut rng))
 | 
				
			||||||
 | 
					            .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            x,
 | 
				
			||||||
 | 
					            y: 0,
 | 
				
			||||||
 | 
					            length,
 | 
				
			||||||
 | 
					            speed,
 | 
				
			||||||
 | 
					            chars,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn random_matrix_char(rng: &mut ThreadRng) -> char {
 | 
				
			||||||
 | 
					        let matrix_chars = [
 | 
				
			||||||
 | 
					            // Japanese katakana
 | 
				
			||||||
 | 
					            'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ',
 | 
				
			||||||
 | 
					            'サ', 'シ', 'ス', 'セ', 'ソ', 'タ', 'チ', 'ツ', 'テ', 'ト',
 | 
				
			||||||
 | 
					            'ナ', 'ニ', 'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ',
 | 
				
			||||||
 | 
					            'マ', 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ',
 | 
				
			||||||
 | 
					            'ル', 'レ', 'ロ', 'ワ', 'ヲ', 'ン',
 | 
				
			||||||
 | 
					            // Numbers
 | 
				
			||||||
 | 
					            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
 | 
				
			||||||
 | 
					            // Some ASCII symbols
 | 
				
			||||||
 | 
					            ':', '・', '"', '=', '*', '+', '-', '<', '>', '¦', '|', 'Z',
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					        *matrix_chars.choose(rng).unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn update(&mut self, height: u16, frame_counter: usize, speed_multiplier: usize) {
 | 
				
			||||||
 | 
					        // Calculate effective speed with multiplier (higher multiplier = faster)
 | 
				
			||||||
 | 
					        let effective_speed = if speed_multiplier == 0 {
 | 
				
			||||||
 | 
					            self.speed as usize * 4 // Much slower when multiplier is 0
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            (self.speed as usize * 2).saturating_sub(speed_multiplier.saturating_sub(1)).max(1)
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if frame_counter % effective_speed == 0 {
 | 
				
			||||||
 | 
					            self.y = self.y.wrapping_add(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Reset column when it goes off screen
 | 
				
			||||||
 | 
					            if self.y > height + self.length {
 | 
				
			||||||
 | 
					                self.y = 0;
 | 
				
			||||||
 | 
					                let mut rng = rng();
 | 
				
			||||||
 | 
					                self.length = rng.random_range(5..15);
 | 
				
			||||||
 | 
					                self.speed = rng.random_range(1..4);
 | 
				
			||||||
 | 
					                self.chars = (0..self.length)
 | 
				
			||||||
 | 
					                    .map(|_| Self::random_matrix_char(&mut rng))
 | 
				
			||||||
 | 
					                    .collect();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Occasionally change characters (also affected by speed multiplier)
 | 
				
			||||||
 | 
					            let char_change_rate = (10 + (speed_multiplier * 5).min(40)) as f64 / 100.0;
 | 
				
			||||||
 | 
					            if rng().random_bool(char_change_rate) {
 | 
				
			||||||
 | 
					                let mut rng = rng();
 | 
				
			||||||
 | 
					                for ch in &mut self.chars {
 | 
				
			||||||
 | 
					                    if rng.random_bool(0.3) {
 | 
				
			||||||
 | 
					                        *ch = Self::random_matrix_char(&mut rng);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub(crate) struct MyApp {
 | 
				
			||||||
 | 
					    counter: usize,
 | 
				
			||||||
 | 
					    frame_counter: usize,
 | 
				
			||||||
 | 
					    columns: Vec<MatrixColumn>,
 | 
				
			||||||
 | 
					    initialized: bool,
 | 
				
			||||||
 | 
					    density: usize,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Demo for MyApp {
 | 
				
			||||||
 | 
					    fn new() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            counter: 1,
 | 
				
			||||||
 | 
					            frame_counter: 0,
 | 
				
			||||||
 | 
					            columns: Vec::new(),
 | 
				
			||||||
 | 
					            initialized: false,
 | 
				
			||||||
 | 
					            density: 3, // Default density (lower = more dense)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn frame(&mut self, term: &mut crate::Term) -> Result<(), String> {
 | 
				
			||||||
 | 
					        term.draw(|fr| {
 | 
				
			||||||
 | 
					            self.frame_counter = self.frame_counter.wrapping_add(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let style = Style::new().green().on_black();
 | 
				
			||||||
 | 
					            let area = fr.area();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let block = Block::bordered()
 | 
				
			||||||
 | 
					                .style(style)
 | 
				
			||||||
 | 
					                .title("Matrix Rain")
 | 
				
			||||||
 | 
					                .title_bottom("By: Kurocon (Kevin Alberts), kurocon@snt.utwente.nl");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let barea = block.inner(area);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fr.render_widget(block, area);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Initialize columns on first frame or when density changes
 | 
				
			||||||
 | 
					            if !self.initialized {
 | 
				
			||||||
 | 
					                self.columns.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Create multiple layers based on density
 | 
				
			||||||
 | 
					                // Lower density value = more layers = more dense
 | 
				
			||||||
 | 
					                let layers = match self.density {
 | 
				
			||||||
 | 
					                    1 => 12, // Very dense - 12 layers
 | 
				
			||||||
 | 
					                    2 => 8,  // Dense - 8 layers  
 | 
				
			||||||
 | 
					                    3 => 6,  // Normal - 6 layers
 | 
				
			||||||
 | 
					                    4 => 4,  // Sparse - 4 layers
 | 
				
			||||||
 | 
					                    5 => 3,  // More sparse - 3 layers
 | 
				
			||||||
 | 
					                    _ => 2,  // Very sparse - 2 layers
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let base_spacing = (self.density * 2).max(2) as u16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for layer in 0..layers {
 | 
				
			||||||
 | 
					                    let layer_offset = (layer as u16 * base_spacing / layers as u16).max(1);
 | 
				
			||||||
 | 
					                    let spacing = base_spacing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let layer_columns: Vec<MatrixColumn> = (barea.x..(barea.x + barea.width))
 | 
				
			||||||
 | 
					                        .step_by(spacing as usize)
 | 
				
			||||||
 | 
					                        .map(|x| {
 | 
				
			||||||
 | 
					                            let offset_x = (x + layer_offset) % (barea.x + barea.width);
 | 
				
			||||||
 | 
					                            let mut col = MatrixColumn::new(offset_x, self.frame_counter + layer * 100);
 | 
				
			||||||
 | 
					                            // Give each layer slightly different starting positions
 | 
				
			||||||
 | 
					                            col.y = (layer as u16 * barea.height / layers as u16) % (barea.height + col.length);
 | 
				
			||||||
 | 
					                            col
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    self.columns.extend(layer_columns);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self.initialized = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Update and draw columns
 | 
				
			||||||
 | 
					            let buf = fr.buffer_mut();
 | 
				
			||||||
 | 
					            for column in &mut self.columns {
 | 
				
			||||||
 | 
					                column.update(barea.height, self.frame_counter, self.counter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (i, &ch) in column.chars.iter().enumerate() {
 | 
				
			||||||
 | 
					                    let char_y = column.y.saturating_sub(i as u16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if char_y >= barea.y && char_y < barea.y + barea.height {
 | 
				
			||||||
 | 
					                        let style = if i == 0 {
 | 
				
			||||||
 | 
					                            // Head of the stream - bright white
 | 
				
			||||||
 | 
					                            Style::new().white().bold()
 | 
				
			||||||
 | 
					                        } else if i < 3 {
 | 
				
			||||||
 | 
					                            // Near head - bright green
 | 
				
			||||||
 | 
					                            Style::new().light_green().bold()
 | 
				
			||||||
 | 
					                        } else if i < column.length as usize / 2 {
 | 
				
			||||||
 | 
					                            // Middle - regular green
 | 
				
			||||||
 | 
					                            Style::new().green()
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            // Tail - dark green
 | 
				
			||||||
 | 
					                            Style::new().dark_gray()
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        buf.set_span(column.x, char_y, &Span::from(ch.to_string()).style(style), 1);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Center text with instructions and counter
 | 
				
			||||||
 | 
					            let mut lines: Vec<Line> = Vec::new();
 | 
				
			||||||
 | 
					            lines.push("W/S: Speed | E/D: Density | Enter: Exit".into());
 | 
				
			||||||
 | 
					            lines.push(format!("Speed multiplier: {}", self.counter).into());
 | 
				
			||||||
 | 
					            lines.push(format!("Density: {} ({})", self.density, if self.density <= 2 { "Dense" } else if self.density <= 4 { "Normal" } else { "Sparse" }).into());
 | 
				
			||||||
 | 
					            lines.push("".into());
 | 
				
			||||||
 | 
					            lines.push("\"Wake up, Neo...\"".light_green().into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let text = Paragraph::new(lines).centered().style(Style::new().on_black());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let text_width = 45;
 | 
				
			||||||
 | 
					            let text_height = 7;
 | 
				
			||||||
 | 
					            let text_x = (barea.width.saturating_sub(text_width)) / 2 + barea.x;
 | 
				
			||||||
 | 
					            let text_y = (barea.height.saturating_sub(text_height)) / 2 + barea.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fr.render_widget(text, Rect::new(text_x, text_y, text_width, text_height));
 | 
				
			||||||
 | 
					        }).map_err(|e| e.to_string())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn input(&mut self, event: terminput::Event) -> Result<(), String> {
 | 
				
			||||||
 | 
					        if let terminput::Event::Key(kv) = event {
 | 
				
			||||||
 | 
					            match kv.code {
 | 
				
			||||||
 | 
					                KeyCode::Char('w') => {
 | 
				
			||||||
 | 
					                    self.counter = self.counter.wrapping_add(1);
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                KeyCode::Char('s') => {
 | 
				
			||||||
 | 
					                    self.counter = self.counter.saturating_sub(1);
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                KeyCode::Char('e') => {
 | 
				
			||||||
 | 
					                    self.density = self.density.saturating_sub(1).max(1);
 | 
				
			||||||
 | 
					                    self.initialized = false; // Force reinitialization
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                KeyCode::Char('d') => {
 | 
				
			||||||
 | 
					                    self.density = (self.density + 1).min(10);
 | 
				
			||||||
 | 
					                    self.initialized = false; // Force reinitialization
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                _ => {}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn disconnected(&mut self) {
 | 
				
			||||||
 | 
					        ()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,8 +4,8 @@ use crossterm::event::Event;
 | 
				
			||||||
use ratatui::{prelude::CrosstermBackend, Terminal};
 | 
					use ratatui::{prelude::CrosstermBackend, Terminal};
 | 
				
			||||||
use futures::{future::FutureExt, StreamExt};
 | 
					use futures::{future::FutureExt, StreamExt};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod app;
 | 
					mod app_matrix;
 | 
				
			||||||
use app::MyApp;
 | 
					use app_matrix::MyApp;
 | 
				
			||||||
use terminput::KeyCode;
 | 
					use terminput::KeyCode;
 | 
				
			||||||
use terminput_crossterm::to_terminput;
 | 
					use terminput_crossterm::to_terminput;
 | 
				
			||||||
use termion::raw::{IntoRawMode, RawTerminal};
 | 
					use termion::raw::{IntoRawMode, RawTerminal};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue