this repo has no description
0
fork

Configure Feed

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

editor woo

alice 81f6e812 349e8195

+174 -7
+100 -1
tic80_rust/src/editor/code.rs
··· 28 28 } 29 29 } 30 30 31 + #[must_use] 31 32 pub fn line_count(&self) -> usize { 32 33 self.rope.len_lines() 33 34 } 34 35 36 + #[must_use] 35 37 pub fn line_len(&self, line: usize) -> usize { 36 38 if line >= self.rope.len_lines() { 37 39 0 ··· 78 80 } 79 81 } 80 82 } 81 - } 83 + 84 + #[allow(clippy::missing_const_for_fn)] 85 + fn ensure_visible(&mut self, lines_visible: usize, cols_visible: usize) { 86 + if self.caret_line < self.scroll_line { 87 + self.scroll_line = self.caret_line; 88 + } else if self.caret_line >= self.scroll_line + lines_visible { 89 + self.scroll_line = self 90 + .caret_line 91 + .saturating_sub(lines_visible.saturating_sub(1)); 92 + } 93 + if self.caret_col < self.scroll_col { 94 + self.scroll_col = self.caret_col; 95 + } else if self.caret_col >= self.scroll_col + cols_visible { 96 + self.scroll_col = self 97 + .caret_col 98 + .saturating_sub(cols_visible.saturating_sub(1)); 99 + } 100 + } 101 + 102 + pub fn draw(&mut self, fb: &mut crate::gfx::framebuffer::Framebuffer, area: Area) { 103 + let gutter_w = 24i32; 104 + let lines_vis = (area.h / 8).max(1) as usize; 105 + let cols_vis = ((area.w - gutter_w) / 6).max(1) as usize; 106 + self.ensure_visible(lines_vis, cols_vis); 107 + 108 + // Clip to drawing area 109 + fb.clip(area.x, area.y, area.w, area.h); 110 + 111 + // Background is already drawn by UI; render gutter and text 112 + for i in 0..lines_vis { 113 + let line_idx = self.scroll_line + i; 114 + if line_idx >= self.line_count() { 115 + break; 116 + } 117 + // Gutter (1-based line numbers) 118 + let gutter_y = area.y + i32::try_from(i).unwrap_or(0) * 8; 119 + let ln = line_idx + 1; 120 + let label = format!("{ln:>3}"); 121 + let _ = fb.print_text(&label, area.x + 2, gutter_y, 6, true, 1, false); 122 + 123 + // Text slice 124 + let mut line = self.rope.line(line_idx).to_string(); 125 + if line.ends_with('\n') { 126 + line.pop(); 127 + } 128 + let start = self.scroll_col.min(line.chars().count()); 129 + let mut iter = line.chars().skip(start); 130 + let vis: String = iter.by_ref().take(cols_vis).collect(); 131 + // Monospace rendering for alignment (fixed=true) 132 + let _ = fb.print_text(&vis, area.x + gutter_w, gutter_y, 12, true, 1, false); 133 + } 82 134 135 + // Caret (TIC-80 style: red box slightly larger than glyph, with 1px drop shadow; underlying glyph drawn dark) 136 + if self.caret_line >= self.scroll_line && self.caret_line < self.scroll_line + lines_vis { 137 + let row = i32::try_from(self.caret_line - self.scroll_line).unwrap_or(0); 138 + let col = i32::try_from(self.caret_col.saturating_sub(self.scroll_col)).unwrap_or(0); 139 + let cell_x = area.x + gutter_w + col * 6; 140 + let cell_y = area.y + row * 8; 141 + // Target a box that is ~1px larger than the glyph horizontally and vertically 142 + // within the 6x8 cell: width 7 (left-extended by 1), height 7, leaving room for a 1px bottom shadow. 143 + let mut fill_x = cell_x - 1; // extend 1px to the left 144 + let fill_y = (cell_y - 1).max(area.y); // shift up by 1px 145 + let mut fill_w = 7; // cover 6px cell width + 1px extra on left 146 + let fill_h = 7; // leave 1px bottom for shadow 147 + // Clamp fill to the code pane (don’t bleed into gutter if at first column) 148 + let code_left = area.x + gutter_w; 149 + if fill_x < code_left { 150 + let delta = code_left - fill_x; 151 + fill_x = code_left; 152 + fill_w = (fill_w - delta).max(1); 153 + } 154 + // Fill: palette 8 (red) 155 + fb.rect(fill_x, fill_y, fill_w, fill_h, 8); 156 + // Shadow (palette 0) to the right and along bottom 157 + fb.rect(fill_x + fill_w, fill_y, 1, fill_h, 0); 158 + fb.rect(fill_x + 1, fill_y + fill_h, fill_w, 1, 0); 159 + 160 + // Draw the underlying glyph in dark color to simulate inversion 161 + let line_idx = self.caret_line; 162 + if line_idx < self.line_count() { 163 + let mut full = self.rope.line(line_idx).to_string(); 164 + if full.ends_with('\n') { 165 + full.pop(); 166 + } 167 + let total = full.chars().count(); 168 + let start = self.scroll_col.min(total); 169 + let idx = start + (col as usize); 170 + if idx < total { 171 + let ch = full.chars().nth(idx).unwrap_or(' '); 172 + let s = ch.to_string(); 173 + // Render in color 0 (dark) monospaced aligned to cell; this simulates inversion 174 + let _ = fb.print_text(&s, cell_x, cell_y, 0, true, 1, false); 175 + } 176 + } 177 + } 178 + 179 + fb.clip_reset(); 180 + } 181 + }
-2
tic80_rust/src/editor/ui.rs
··· 208 208 match self.active { 209 209 Tab::Code => { 210 210 fb.rect(0, 12, 240, 124, 1); 211 - let _ = fb.print_text("[CODE]", 6, 16, 12, true, 1, false); 212 211 } 213 212 Tab::Console => { 214 213 fb.rect(0, 12, 240, 124, 2); 215 - let _ = fb.print_text("[CONSOLE]", 6, 16, 12, true, 1, false); 216 214 } 217 215 } 218 216 }
+2 -1
tic80_rust/src/gfx/framebuffer.rs
··· 517 517 } 518 518 519 519 let (start_col, width_cols) = if fixed { 520 - (0, GLYPH_W) 520 + // Fixed-width: TIC-80 advances 6 px per glyph; render leftmost 6 columns 521 + (0, 6) 521 522 } else { 522 523 // Variable-width: trim empty columns using LSB-left orientation 523 524 let mut left = GLYPH_W;
+1
tic80_rust/src/lib.rs
··· 34 34 } 35 35 36 36 pub mod editor { 37 + pub mod code; 37 38 pub mod ui; 38 39 }
+33 -3
tic80_rust/src/main.rs
··· 42 42 use tic80_rust::audio::capture as audio_cap; 43 43 use tic80_rust::audio::fft::{set_global_fft, FFTState}; 44 44 use tic80_rust::core::memory::Memory; 45 + use tic80_rust::editor::code::{Area as CodeArea, CodeBuffer}; 45 46 use tic80_rust::editor::ui::EditorUi; 46 47 use tic80_rust::gfx::framebuffer::{dimensions, Framebuffer}; 47 48 use tic80_rust::script::lua_runner::LuaRunner; ··· 299 300 return Ok(()); 300 301 } 301 302 tic80_rust::script::lua_runner::set_quiet(args.quiet); 302 - let (lua_runner, mut editor_ui) = if args.editor { 303 - (None, Some(EditorUi::new(window_scale))) 303 + let (lua_runner, mut editor_ui, mut code_buf) = if args.editor { 304 + let initial = load_script(args.script_path.as_ref()); 305 + ( 306 + None, 307 + Some(EditorUi::new(window_scale)), 308 + Some(CodeBuffer::from_text(&initial)), 309 + ) 304 310 } else { 305 311 let script = load_script(args.script_path.as_ref()); 306 312 let lr = match LuaRunner::new(fb.clone(), mem.clone(), &script) { ··· 310 316 None 311 317 } 312 318 }; 313 - (lr, None) 319 + (lr, None, None) 314 320 }; 315 321 let mut audio_state = init_audio(&args); 316 322 let mut warned_no_tic = false; 317 323 let mut last_cursor_fb: Option<(i32, i32)> = None; 318 324 325 + #[allow(clippy::cognitive_complexity)] 319 326 event_loop.run(move |event, _, control_flow| { 320 327 *control_flow = ControlFlow::Poll; 321 328 match event { ··· 341 348 }, 342 349 .. 343 350 } => *control_flow = ControlFlow::Exit, 351 + WindowEvent::KeyboardInput { input, .. } => { 352 + if let (Some(ui), Some(cb)) = (editor_ui.as_ref(), code_buf.as_mut()) { 353 + if ui.active == tic80_rust::editor::ui::Tab::Code 354 + && input.state == ElementState::Pressed 355 + { 356 + if let Some(key) = input.virtual_keycode { 357 + match key { 358 + VirtualKeyCode::Left => cb.move_left(), 359 + VirtualKeyCode::Right => cb.move_right(), 360 + VirtualKeyCode::Up => cb.move_up(), 361 + VirtualKeyCode::Down => cb.move_down(), 362 + _ => {} 363 + } 364 + } 365 + } 366 + } 367 + } 344 368 WindowEvent::Resized(size) => { 345 369 let _ = pixels.resize_surface(size.width, size.height); 346 370 } ··· 468 492 if let Some(ui) = editor_ui.as_ref() { 469 493 let mut fbb = fb.borrow_mut(); 470 494 ui.draw(&mut fbb); 495 + if ui.active == tic80_rust::editor::ui::Tab::Code { 496 + if let Some(cb) = code_buf.as_mut() { 497 + let area = CodeArea { x: 0, y: 12, w: 240, h: 124 }; 498 + cb.draw(&mut fbb, area); 499 + } 500 + } 471 501 } 472 502 fb.borrow().blit_to_rgba(frame); 473 503 if let Err(err) = pixels.render() {
+38
tic80_rust/tests/editor_code_view_tests.rs
··· 1 + use std::cell::RefCell; 2 + use std::rc::Rc; 3 + 4 + use tic80_rust::editor::code::{Area, CodeBuffer}; 5 + use tic80_rust::gfx::framebuffer::Framebuffer; 6 + 7 + #[test] 8 + fn code_view_renders_lines_and_gutter() { 9 + let text = "line1\nline2\nline3\n"; 10 + let mut cb = CodeBuffer::from_text(text); 11 + let fb = Rc::new(RefCell::new(Framebuffer::new())); 12 + let mut fbb = fb.borrow_mut(); 13 + let area = Area { 14 + x: 0, 15 + y: 12, 16 + w: 240, 17 + h: 40, 18 + }; 19 + cb.draw(&mut fbb, area); 20 + // Expect some non-zero pixels in gutter (left side) 21 + let mut gutter_ink = 0; 22 + for y in 12..20 { 23 + for x in 2..22 { 24 + if fbb.pix(x, y, None).unwrap_or(0) != 0 { 25 + gutter_ink += 1; 26 + } 27 + } 28 + } 29 + assert!(gutter_ink > 0); 30 + // Expect some text pixels in the first line area after gutter (x >= 24) 31 + let mut line_ink = 0; 32 + for x in 24..60 { 33 + if fbb.pix(x, 12, None).unwrap_or(0) != 0 { 34 + line_ink += 1; 35 + } 36 + } 37 + assert!(line_ink > 0); 38 + }