fix(scroll): support show/hide cursor and fix vim scrolling behaviour (#27)

This commit is contained in:
Aram Drevekenin 2020-11-06 18:12:03 +01:00 committed by GitHub
parent cc347ab24a
commit 13af16b336
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 92 additions and 38 deletions

View file

@ -269,7 +269,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
send_screen_instructions.send(ScreenInstruction::ScrollDown).unwrap();
} else if buffer[0] == 24 { // ctrl-x
send_screen_instructions.send(ScreenInstruction::CloseFocusedPane).unwrap();
} else if buffer[0] == 6 { // ctrl-f
} else if buffer[0] == 5 { // ctrl-e
send_screen_instructions.send(ScreenInstruction::ToggleActiveTerminalFullscreen).unwrap();
} else {
// println!("\r buffer {:?} ", buffer[0]);

View file

@ -264,13 +264,13 @@ impl Screen {
self.os_api.tcdrain(*active_terminal_id).expect("failed to drain terminal");
}
}
fn get_active_terminal_cursor_position(&self) -> (usize, usize) { // (x, y)
fn get_active_terminal_cursor_position(&self) -> Option<(usize, usize)> { // (x, y)
let active_terminal = &self.get_active_terminal().unwrap();
let (x_in_terminal, y_in_terminal) = active_terminal.cursor_coordinates();
let x = active_terminal.get_x() + x_in_terminal;
let y = active_terminal.get_y() + y_in_terminal;
(x, y)
active_terminal.cursor_coordinates().and_then(|(x_in_terminal, y_in_terminal)| {
let x = active_terminal.get_x() + x_in_terminal;
let y = active_terminal.get_y() + y_in_terminal;
Some((x, y))
})
}
pub fn toggle_active_terminal_fullscreen(&mut self) {
if let Some(active_terminal_id) = self.get_active_terminal_id() {
@ -312,10 +312,20 @@ impl Screen {
let vte_output = boundaries.vte_output();
stdout.write_all(&vte_output.as_bytes()).expect("cannot write to stdout");
let (cursor_position_x, cursor_position_y) = self.get_active_terminal_cursor_position();
let goto_cursor_position = format!("\u{1b}[{};{}H\u{1b}[m", cursor_position_y + 1, cursor_position_x + 1); // goto row/col
stdout.write_all(&goto_cursor_position.as_bytes()).expect("cannot write to stdout");
stdout.flush().expect("could not flush");
match self.get_active_terminal_cursor_position() {
Some((cursor_position_x, cursor_position_y)) => {
let show_cursor = "\u{1b}[?25h";
let goto_cursor_position = format!("\u{1b}[{};{}H\u{1b}[m", cursor_position_y + 1, cursor_position_x + 1); // goto row/col
stdout.write_all(&show_cursor.as_bytes()).expect("cannot write to stdout");
stdout.write_all(&goto_cursor_position.as_bytes()).expect("cannot write to stdout");
stdout.flush().expect("could not flush");
},
None => {
let hide_cursor = "\u{1b}[?25l";
stdout.write_all(&hide_cursor.as_bytes()).expect("cannot write to stdout");
stdout.flush().expect("could not flush");
}
}
}
fn terminal_ids_directly_left_of(&self, id: &RawFd) -> Option<Vec<RawFd>> {
let mut ids = vec![];

View file

@ -172,7 +172,8 @@ pub struct Scroll {
total_columns: usize,
lines_in_view: usize,
viewport_bottom_offset: Option<usize>,
scroll_region: Option<(usize, usize)> // start line, end line (if set, this is the area the will scroll)
scroll_region: Option<(usize, usize)>, // start line, end line (if set, this is the area the will scroll)
show_cursor: bool,
}
impl Scroll {
@ -187,6 +188,7 @@ impl Scroll {
cursor_position,
viewport_bottom_offset: None,
scroll_region: None,
show_cursor: true,
}
}
pub fn as_character_lines(&self) -> Vec<Vec<TerminalCharacter>> {
@ -243,21 +245,29 @@ impl Scroll {
self.cursor_position.move_to_beginning_of_linewrap();
}
}
pub fn show_cursor (&mut self) {
self.show_cursor = true;
}
pub fn hide_cursor (&mut self) {
self.show_cursor = false;
}
pub fn add_canonical_line(&mut self) {
let current_canonical_line_index = self.cursor_position.line_index.0;
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
// the scroll region indices start at 1, so we need to adjust them
let scroll_region_top_index = scroll_region_top - 1;
let scroll_region_bottom_index = scroll_region_bottom - 1;
if current_canonical_line_index == scroll_region_bottom_index { // end of scroll region
// when we have a scroll region set and we're at its bottom
// we need to delete its first line, thus shifting all lines in it upwards
// then we add an empty line at its end which will be filled by the application
// controlling the scroll region (presumably filled by whatever comes next in the
// scroll buffer, but that's not something we control)
self.canonical_lines.remove(scroll_region_top_index);
self.canonical_lines.insert(scroll_region_bottom_index, CanonicalLine::new());
return;
if self.show_cursor { // scroll region should be ignored if the cursor is hidden
let scroll_region_top_index = scroll_region_top - 1;
let scroll_region_bottom_index = scroll_region_bottom - 1;
if current_canonical_line_index == scroll_region_bottom_index { // end of scroll region
// when we have a scroll region set and we're at its bottom
// we need to delete its first line, thus shifting all lines in it upwards
// then we add an empty line at its end which will be filled by the application
// controlling the scroll region (presumably filled by whatever comes next in the
// scroll buffer, but that's not something we control)
self.canonical_lines.remove(scroll_region_top_index);
self.canonical_lines.insert(scroll_region_bottom_index, CanonicalLine::new());
return;
}
}
}
if current_canonical_line_index == self.canonical_lines.len() - 1 {
@ -272,7 +282,10 @@ impl Scroll {
panic!("cursor out of bounds, cannot add_canonical_line");
}
}
pub fn cursor_coordinates_on_screen(&self) -> (usize, usize) { // (x, y)
pub fn cursor_coordinates_on_screen(&self) -> Option<(usize, usize)> { // (x, y)
if !self.show_cursor {
return None
}
let (canonical_line_cursor_position, line_wrap_cursor_position) = self.cursor_position.line_index;
let x = self.cursor_position.column_index;
let mut y = 0;
@ -294,10 +307,12 @@ impl Scroll {
let total_lines = self.canonical_lines.iter().fold(0, |total_lines, current_line| total_lines + current_line.wrapped_fragments.len()); // TODO: is this performant enough? should it be cached or kept track of?
let y = if total_lines < self.lines_in_view {
total_lines - y
} else if y > self.lines_in_view {
self.lines_in_view
} else {
self.lines_in_view - y
};
(x, y)
Some((x, y))
}
pub fn move_cursor_forward(&mut self, count: usize) {
let (current_canonical_line_index, current_line_wrap_position) = self.cursor_position.line_index;

View file

@ -223,7 +223,7 @@ impl TerminalPane {
pub fn read_buffer_as_lines (&self) -> Vec<Vec<TerminalCharacter>> {
self.scroll.as_character_lines()
}
pub fn cursor_coordinates (&self) -> (usize, usize) { // (x, y)
pub fn cursor_coordinates (&self) -> Option<(usize, usize)> { // (x, y)
self.scroll.cursor_coordinates_on_screen()
}
pub fn scroll_up(&mut self, count: usize) {
@ -255,8 +255,8 @@ impl TerminalPane {
self.should_render = true;
}
fn add_newline (&mut self) {
self.scroll.add_canonical_line(); // TODO: handle scroll region
self.reset_all_ansi_codes();
self.scroll.add_canonical_line();
// self.reset_all_ansi_codes(); // TODO: find out if we should be resetting here or not
self.should_render = true;
}
fn move_to_beginning_of_line (&mut self) {
@ -557,14 +557,41 @@ impl vte::Perform for TerminalPane {
let move_back_count = if params[0] == 0 { 1 } else { params[0] as usize };
self.scroll.move_cursor_back(move_back_count);
} else if c == 'l' {
// TBD
let first_intermediate_is_questionmark = match _intermediates.get(0) {
Some(b'?') => true,
None => false,
_ => false
};
if first_intermediate_is_questionmark {
match params.get(0) {
Some(25) => {
self.scroll.hide_cursor();
self.should_render = true;
},
_ => {}
};
}
} else if c == 'h' {
// TBD
let first_intermediate_is_questionmark = match _intermediates.get(0) {
Some(b'?') => true,
None => false,
_ => false
};
if first_intermediate_is_questionmark {
match params.get(0) {
Some(25) => {
self.scroll.show_cursor();
self.should_render = true;
},
_ => {}
};
}
} else if c == 'r' {
if params.len() > 1 {
let top_line_index = params[0] as usize;
let bottom_line_index = params[1] as usize;
self.scroll.set_scroll_region(top_line_index, bottom_line_index);
self.scroll.show_cursor();
} else {
self.scroll.clear_scroll_region();
}

View file

@ -170,7 +170,7 @@ pub fn toggle_focused_pane_fullscreen () {
// split-largest_terminal * 3, toggle-fullscreen * 2, ctrl-p, toggle-fullscreen * 2, ctrl-p, toggle-fullscreen * 2, ctrl-p, toggle-fullscreen * 2 and quit
// (ctrl-z + ctrl-z + ctrl-z + ctrl-z + ctrl-f, ctrl-f, ctrl-p, ctrl-f, ctrl-f, ctrl-p, ctrl-f,
// ctrl-f, ctrl-p, ctrl-f, ctrl-f, ctrl-q)
fake_input_output.add_terminal_input(&[26, 26, 26, 6, 6, 16, 6, 6, 16, 6, 6, 16, 6, 6, 17]);
fake_input_output.add_terminal_input(&[26, 26, 26, 5, 5, 16, 5, 5, 16, 5, 5, 16, 5, 5, 17]);
let mut opts = Opt::default();
opts.max_panes = Some(4);
start(Box::new(fake_input_output.clone()), opts);

View file

@ -29,4 +29,4 @@ expression: snapshot
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Press <SPACE> to pause. Use <TAB> to rearrange tables. (DNS queries hidden).
Bye from Mosaic!
Bye from Mosaic!

View file

@ -6,7 +6,7 @@ expression: snapshot
┌Utilization by process name───────────────────────────────────────────────────────────────────────────────────────┐
│Process Connections Up / Down │
│ │
│firefox 3 46Bps / 5Bps │
│firefox 3 46Bps / 57Bps │
│ │
│ │
│ │

View file

@ -14,15 +14,17 @@ pub fn get_output_frame_snapshots(output_frames: &[Vec<u8>], win_size: &Winsize)
vte_parser.advance(&mut terminal_output, *byte);
}
let output_lines = terminal_output.read_buffer_as_lines();
let (cursor_x, cursor_y) = terminal_output.cursor_coordinates();
let cursor_coordinates = terminal_output.cursor_coordinates();
let mut snapshot = String::new();
for (line_index, line) in output_lines.iter().enumerate() {
for (character_index, terminal_character) in line.iter().enumerate() {
if line_index == cursor_y && character_index == cursor_x {
snapshot.push('█');
} else {
snapshot.push(terminal_character.character);
if let Some((cursor_x, cursor_y)) = cursor_coordinates {
if line_index == cursor_y && character_index == cursor_x {
snapshot.push('█');
continue;
}
}
snapshot.push(terminal_character.character);
}
if line_index != output_lines.len() - 1 {
snapshot.push('\n');