From ee3cc21eceefb99c7df206a4fbac44a24d3a6677 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 20 Dec 2023 14:26:31 -0700 Subject: [PATCH] Add scrollbar and scrolling --- src/main.rs | 4 ++-- src/terminal.rs | 35 ++++++++++++++++++++++++++-------- src/terminal_box.rs | 46 ++++++++++++++++++++++++++++++++------------- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6a2fdce..577ff83 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,7 @@ use cosmic::{ use std::{any::TypeId, sync::Mutex}; use tokio::sync::mpsc; -use self::terminal::Terminal; +use self::terminal::{Terminal, TerminalScroll}; mod terminal; use self::terminal_box::terminal_box; @@ -179,7 +179,7 @@ impl cosmic::Application for App { self.tab_model.text_set(entity, title); return self.update_title(); } - TermEvent::Wakeup => { + TermEvent::MouseCursorDirty | TermEvent::Wakeup => { if let Some(terminal) = self.tab_model.data::>(entity) { let mut terminal = terminal.lock().unwrap(); terminal.update(); diff --git a/src/terminal.rs b/src/terminal.rs index bbe123e..9e2a0a9 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -1,5 +1,5 @@ use alacritty_terminal::{ - ansi::{Color, NamedColor}, + ansi::{Color, Handler, NamedColor}, config::{Config, PtyConfig}, event::{Event, EventListener, Notify, OnResize, WindowSize}, event_loop::{EventLoop, Msg, Notifier, State}, @@ -25,6 +25,8 @@ use std::{ }; use tokio::sync::mpsc; +pub use alacritty_terminal::grid::Scroll as TerminalScroll; + #[derive(Clone, Copy, Debug)] pub struct Size { width: u32, @@ -318,6 +320,22 @@ impl Terminal { } } + pub fn scroll(&self, scroll: TerminalScroll) { + self.term.lock().scroll_display(scroll); + } + + pub fn scrollbar(&self) -> (f32, f32) { + let term = self.term.lock(); + let grid = term.grid(); + let total = grid.history_size() + grid.screen_lines(); + let start = total - grid.display_offset(); + let end = total - (grid.display_offset() + grid.screen_lines()); + ( + (start as f32) / (total as f32), + (end as f32) / (total as f32), + ) + } + pub fn update(&mut self) -> bool { let instant = Instant::now(); @@ -326,15 +344,15 @@ impl Terminal { { let mut buffer = Arc::make_mut(&mut self.buffer); - let mut last_point = Point::new(Line(0), Column(0)); + let mut line_i = 0; + let mut last_point = None; let mut text = String::new(); let mut attrs_list = AttrsList::new(self.default_attrs); { let term_guard = self.term.lock(); let grid = term_guard.grid(); for indexed in grid.display_iter() { - if indexed.point.line != last_point.line { - let line_i = last_point.line.0 as usize; + if indexed.point.line != last_point.unwrap_or(indexed.point).line { while line_i >= buffer.lines.len() { buffer.lines.push(BufferLine::new( "", @@ -347,6 +365,7 @@ impl Terminal { if buffer.lines[line_i].set_text(text.clone(), attrs_list.clone()) { buffer.set_redraw(true); } + line_i += 1; text.clear(); attrs_list.clear_spans(); @@ -389,12 +408,11 @@ impl Terminal { attrs_list.add_span(start..end, attrs); } - last_point = indexed.point; + last_point = Some(indexed.point); } } //TODO: do not repeat! - let line_i = last_point.line.0 as usize; while line_i >= buffer.lines.len() { buffer.lines.push(BufferLine::new( "", @@ -407,9 +425,10 @@ impl Terminal { if buffer.lines[line_i].set_text(text, attrs_list) { buffer.set_redraw(true); } + line_i += 1; - if buffer.lines.len() != line_i + 1 { - buffer.lines.truncate(line_i + 1); + if buffer.lines.len() != line_i { + buffer.lines.truncate(line_i); buffer.set_redraw(true); } diff --git a/src/terminal_box.rs b/src/terminal_box.rs index c8b8f90..e286e93 100644 --- a/src/terminal_box.rs +++ b/src/terminal_box.rs @@ -26,7 +26,7 @@ use std::{ time::{Duration, Instant}, }; -use crate::Terminal; +use crate::{Terminal, TerminalScroll}; pub struct TerminalBox<'a, Message> { terminal: &'a Mutex, @@ -183,7 +183,7 @@ where let mut terminal = self.terminal.lock().unwrap(); //TODO: make this configurable - let scrollbar_w = 0.0; + let scrollbar_w = 8.0; let view_position = layout.position() + [self.padding.left as f32, self.padding.top as f32].into(); @@ -266,22 +266,26 @@ where clip_bounds: Rectangle::new(view_position, Size::new(view_w as f32, view_h as f32)), }); - /*TODO // Draw scrollbar + let (start, end) = terminal.scrollbar(); + let scrollbar_y = start * view_h as f32; + let scrollbar_h = end * view_h as f32 - scrollbar_y; let scrollbar_alpha = match &state.dragging { Some(Dragging::Scrollbar { .. }) => 0.5, _ => 0.25, }; renderer.fill_quad( Quad { - bounds: state.scrollbar_rect.get() + Vector::new(view_position.x, view_position.y), + bounds: Rectangle::new( + view_position + Vector::new(view_w as f32, scrollbar_y), + Size::new(scrollbar_w, scrollbar_h), + ), border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, Color::new(1.0, 1.0, 1.0, scrollbar_alpha), ); - */ let duration = instant.elapsed(); log::debug!("redraw {}, {}: {:?}", view_w, view_h, duration); @@ -349,11 +353,19 @@ where status = Status::Captured; } KeyCode::End => { - terminal.input(b"\x1B[F".as_slice()); + if modifiers.shift() { + terminal.scroll(TerminalScroll::Bottom); + } else { + terminal.input(b"\x1B[F".as_slice()); + } status = Status::Captured; } KeyCode::Home => { - terminal.input(b"\x1B[H".as_slice()); + if modifiers.shift() { + terminal.scroll(TerminalScroll::Top); + } else { + terminal.input(b"\x1B[H".as_slice()); + } status = Status::Captured; } KeyCode::Insert => { @@ -365,11 +377,19 @@ where status = Status::Captured; } KeyCode::PageUp => { - terminal.input(b"\x1B[5~".as_slice()); + if modifiers.shift() { + terminal.scroll(TerminalScroll::PageUp); + } else { + terminal.input(b"\x1B[5~".as_slice()); + } status = Status::Captured; } KeyCode::PageDown => { - terminal.input(b"\x1B[6~".as_slice()); + if modifiers.shift() { + terminal.scroll(TerminalScroll::PageDown); + } else { + terminal.input(b"\x1B[6~".as_slice()); + } status = Status::Captured; } //TODO: F1-F12 keys @@ -530,6 +550,7 @@ where status = Status::Captured; } } + */ Event::Mouse(MouseEvent::WheelScrolled { delta }) => { if let Some(_p) = cursor_position.position_in(layout.bounds()) { match delta { @@ -538,7 +559,7 @@ where state.scroll_pixels = 0.0; let lines = (-y * 6.0) as i32; if lines != 0 { - editor.action(Action::Scroll { lines }); + terminal.scroll(TerminalScroll::Delta(-lines)); } status = Status::Captured; } @@ -546,7 +567,7 @@ where //TODO: this adjustment is just a guess! state.scroll_pixels -= y * 6.0; let mut lines = 0; - let metrics = editor.with_buffer(|buffer| buffer.metrics()); + let metrics = terminal.with_buffer(|buffer| buffer.metrics()); while state.scroll_pixels <= -metrics.line_height { lines -= 1; state.scroll_pixels += metrics.line_height; @@ -556,14 +577,13 @@ where state.scroll_pixels -= metrics.line_height; } if lines != 0 { - editor.action(Action::Scroll { lines }); + terminal.scroll(TerminalScroll::Delta(-lines)); } status = Status::Captured; } } } } - */ _ => (), }