this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

caret blinking

alice e65d5298 1262a28c

+39 -4
+39 -4
tic80_rust/src/editor/code.rs
··· 19 19 // Undo/redo stacks (each EditOp can be a batch of atomic edits) 20 20 undo: Vec<EditOp>, 21 21 redo: Vec<EditOp>, 22 + // Caret blink state 23 + tick_counter: u32, 24 + cursor_delay: i32, 22 25 } 23 26 24 27 #[derive(Clone, Debug)] ··· 44 47 sel_anchor: None, 45 48 undo: Vec::new(), 46 49 redo: Vec::new(), 50 + tick_counter: 0, 51 + cursor_delay: 0, 47 52 } 48 53 } 49 54 ··· 55 60 } 56 61 57 62 pub fn insert_char(&mut self, ch: char) { 63 + self.reset_caret_blink(); 58 64 if ch == '\r' { 59 65 return; 60 66 } ··· 78 84 } 79 85 80 86 pub fn insert_tab(&mut self) { 87 + self.reset_caret_blink(); 81 88 // Default to a single space for compact 240x136 layout 82 89 if self.has_selection() { 83 90 self.replace_selection_with(" "); ··· 94 101 } 95 102 96 103 pub fn insert_newline(&mut self) { 104 + self.reset_caret_blink(); 97 105 if self.has_selection() { 98 106 self.replace_selection_with("\n"); 99 107 return; ··· 109 117 } 110 118 111 119 pub fn backspace(&mut self) { 120 + self.reset_caret_blink(); 112 121 if self.has_selection() { 113 122 if let Some((start, end)) = self.selection_range_idx() { 114 123 let deleted = self.delete_range(start, end); ··· 150 159 } 151 160 152 161 pub fn delete_forward(&mut self) { 162 + self.reset_caret_blink(); 153 163 if self.has_selection() { 154 164 if let Some((start, end)) = self.selection_range_idx() { 155 165 let deleted = self.delete_range(start, end); ··· 178 188 179 189 #[allow(clippy::missing_const_for_fn)] 180 190 pub fn home(&mut self) { 191 + self.reset_caret_blink(); 181 192 self.caret_col = 0; 182 193 } 183 194 184 195 #[allow(clippy::missing_const_for_fn)] 185 196 pub fn end(&mut self) { 197 + self.reset_caret_blink(); 186 198 self.caret_col = self.line_len(self.caret_line); 187 199 } 188 200 ··· 201 213 } 202 214 203 215 pub fn move_left(&mut self) { 216 + self.reset_caret_blink(); 204 217 if self.caret_col > 0 { 205 218 self.caret_col -= 1; 206 219 } else if self.caret_line > 0 { ··· 210 223 } 211 224 212 225 pub fn move_right(&mut self) { 226 + self.reset_caret_blink(); 213 227 let len = self.line_len(self.caret_line); 214 228 if self.caret_col < len { 215 229 self.caret_col += 1; ··· 220 234 } 221 235 222 236 pub fn move_up(&mut self) { 237 + self.reset_caret_blink(); 223 238 if self.caret_line > 0 { 224 239 self.caret_line -= 1; 225 240 let len = self.line_len(self.caret_line); ··· 230 245 } 231 246 232 247 pub fn move_down(&mut self) { 248 + self.reset_caret_blink(); 233 249 if self.caret_line + 1 < self.line_count() { 234 250 self.caret_line += 1; 235 251 let len = self.line_len(self.caret_line); ··· 259 275 260 276 #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] 261 277 pub fn draw(&mut self, fb: &mut crate::gfx::framebuffer::Framebuffer, area: Area) { 278 + const TEXT_CURSOR_BLINK_PERIOD: u32 = 60; // ~60 FPS 262 279 // Gutter width: 3 digits * 6px = 18px, plus a 1px gap before code 263 280 let gutter_w = 18i32; 264 281 let gap = 1i32; ··· 342 359 let col = i32::try_from(self.caret_col.saturating_sub(self.scroll_col)).unwrap_or(0); 343 360 let cell_x = area.x + gutter_w + gap + col * 6; 344 361 let cell_y = area.y + row * line_pitch; 345 - // TIC-80 caret style: drop shadow rect (black) then caret rect (cursor color, default 2), both 7x7, offset by 1px 346 - fb.rect(cell_x, cell_y, 7, 7, 0); 347 - fb.rect(cell_x - 1, cell_y - 1, 7, 7, 2); 362 + // Blink logic parity: visible during delay or first half of blink period 363 + let half = TEXT_CURSOR_BLINK_PERIOD / 2; 364 + let show = self.cursor_delay > 0 || (self.tick_counter % TEXT_CURSOR_BLINK_PERIOD) < half; 365 + if show { 366 + // TIC-80 caret style: drop shadow rect (black) then caret rect (cursor color, default 2), both 7x7, offset by 1px 367 + fb.rect(cell_x, cell_y, 7, 7, 0); 368 + fb.rect(cell_x - 1, cell_y - 1, 7, 7, 2); 369 + } 348 370 349 371 // Draw the underlying glyph in dark color to simulate inversion 350 372 let line_idx = self.caret_line; ··· 360 382 let ch = full.chars().nth(idx).unwrap_or(' '); 361 383 let s = ch.to_string(); 362 384 // Render underlying glyph in background color to simulate inversion 363 - let _ = fb.print_text(&s, cell_x, cell_y, 15, true, 1, true); 385 + if show { 386 + let _ = fb.print_text(&s, cell_x, cell_y, 15, true, 1, true); 387 + } 364 388 } 365 389 } 390 + } 391 + 392 + // Advance blink timers 393 + self.tick_counter = self.tick_counter.wrapping_add(1); 394 + if self.cursor_delay > 0 { 395 + self.cursor_delay -= 1; 366 396 } 367 397 368 398 fb.clip_reset(); ··· 554 584 } else { 555 585 self.paste_text(text); 556 586 } 587 + } 588 + 589 + #[allow(clippy::missing_const_for_fn)] 590 + fn reset_caret_blink(&mut self) { 591 + self.cursor_delay = 30; // ~0.5s at 60 FPS 557 592 } 558 593 }