diff options
author | Aram Drevekenin <aram@poor.dev> | 2022-02-18 21:10:06 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-18 21:10:06 +0100 |
commit | 821e7cbc5a9711173061a272dc0378fff1fe5596 (patch) | |
tree | d7806a3d88dbf70d27996d9f85f66e93135670b2 /zellij-server/src/panes/terminal_pane.rs | |
parent | 10a22c479ffb4d76627eadbee2dc4b53ae4309c3 (diff) |
feat(ui): add floating panes (#1066)
* basic functionality
* close and reopen scratch terminal working
* embed/float and resize whole tab for floating and static floating panes
* move focus working
* fix focus change in floating panes
* move pane with mouse
* floating z indices
* tests and better resize algorithm
* starting to work on performance
* some performance experimentations
* new render engine
* reverse painters algorithm for floating panes
* fix frame buffering
* improve ux situation
* handle multiple new panes on screen without overlap
* adjust keybindings
* adjust key hints
* fix multiuser frame ui
* fix various floating/multiuser bugs
* remove stuff
* wide characters under floating panes
* fix wide character frame override
* fix non-frame boundaries interactions with floating panes
* fix selection character width
* fix title frame wide char overflow
* fix existing tests
* add tests
* refactor output out of tab
* refactor floating panes out of tab
* refactor tab
* moar refactoring
* refactorings and bring back terminal window title setting
* add frame vte output
* remove more unused stuff
* remove even more unused stuff
* you know the drill
* refactor floating panes and remove more stuffs
* refactor pane grids
* remove unused output caching
* refactor output
* remove unused stuff
* rustfmt
* some formatting
* rustfmt
* reduce clippy to normal
* remove comment
* remove unused
* fix closign pane
* fix tests
Diffstat (limited to 'zellij-server/src/panes/terminal_pane.rs')
-rw-r--r-- | zellij-server/src/panes/terminal_pane.rs | 146 |
1 files changed, 47 insertions, 99 deletions
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index 728716b29..ee6fb559e 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -1,16 +1,17 @@ -use crate::panes::AnsiCode; +use crate::output::CharacterChunk; use crate::panes::{ grid::Grid, - terminal_character::{ - CharacterStyles, CursorShape, TerminalCharacter, EMPTY_TERMINAL_CHARACTER, - }, + terminal_character::{CursorShape, TerminalCharacter, EMPTY_TERMINAL_CHARACTER}, }; +use crate::panes::{AnsiCode, LinkHandler}; use crate::pty::VteBytes; use crate::tab::Pane; use crate::ClientId; +use std::cell::RefCell; use std::collections::{HashMap, HashSet}; -use std::fmt::{Debug, Write}; +use std::fmt::Debug; use std::os::unix::io::RawFd; +use std::rc::Rc; use std::time::{self, Instant}; use zellij_utils::pane_size::Offset; use zellij_utils::{ @@ -184,102 +185,42 @@ impl Pane for TerminalPane { fn set_selectable(&mut self, selectable: bool) { self.selectable = selectable; } - fn render(&mut self, _client_id: Option<ClientId>) -> Option<String> { - // we don't use client_id because terminal panes render the same for all users + fn render( + &mut self, + _client_id: Option<ClientId>, + ) -> Option<(Vec<CharacterChunk>, Option<String>)> { if self.should_render() { - let mut vte_output = String::new(); - let mut character_styles = CharacterStyles::new(); + let mut raw_vte_output = String::new(); let content_x = self.get_content_x(); let content_y = self.get_content_y(); - if self.grid.clear_viewport_before_rendering { - for line_index in 0..self.grid.height { - write!( - &mut vte_output, - "\u{1b}[{};{}H\u{1b}[m", - content_y + line_index + 1, - content_x + 1 - ) - .unwrap(); // goto row/col and reset styles - for _col_index in 0..self.grid.width { - vte_output.push(EMPTY_TERMINAL_CHARACTER.character); - } - } - self.grid.clear_viewport_before_rendering = false; - } - // here we clear the previous cursor locations by adding an empty style-less character - // in their location, this is done before the main rendering logic so that if there - // actually is another character there, it will be overwritten - for (y, x) in self.fake_cursor_locations.drain() { - // we need to make sure to update the line in the line buffer so that if there's - // another character there it'll override it and we won't create holes with our - // empty character - self.grid.update_line_for_rendering(y); - let x = content_x + x; - let y = content_y + y; - write!( - &mut vte_output, - "\u{1b}[{};{}H\u{1b}[m{}", - y + 1, - x + 1, - EMPTY_TERMINAL_CHARACTER.character - ) - .unwrap(); - } - let max_width = self.get_content_columns(); - for character_chunk in self.grid.read_changes() { - let pane_x = self.get_content_x(); - let pane_y = self.get_content_y(); - let chunk_absolute_x = pane_x + character_chunk.x; - let chunk_absolute_y = pane_y + character_chunk.y; - let terminal_characters = character_chunk.terminal_characters; - write!( - &mut vte_output, - "\u{1b}[{};{}H\u{1b}[m", - chunk_absolute_y + 1, - chunk_absolute_x + 1 - ) - .unwrap(); // goto row/col and reset styles - - let mut chunk_width = character_chunk.x; - for mut t_character in terminal_characters { - // adjust the background of currently selected characters - // doing it here is much easier than in grid - if self.grid.selection.contains(character_chunk.y, chunk_width) { - let color = match self.colors.bg { - PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb), - PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col), - }; - t_character.styles = t_character.styles.background(Some(color)); - } - chunk_width += t_character.width; - if chunk_width > max_width { - break; - } - - if let Some(new_styles) = character_styles - .update_and_return_diff(&t_character.styles, self.grid.changed_colors) - { - write!( - &mut vte_output, - "{}{}", - new_styles, - self.grid.link_handler.output_osc8(new_styles.link_anchor) - ) - .unwrap(); - } - - vte_output.push(t_character.character); + let mut character_chunks = self.grid.read_changes(content_x, content_y); + for character_chunk in character_chunks.iter_mut() { + character_chunk.add_changed_colors(self.grid.changed_colors); + if self + .grid + .selection + .contains_row(character_chunk.y.saturating_sub(content_y)) + { + let background_color = match self.colors.bg { + PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb), + PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col), + }; + character_chunk.add_selection_and_background( + self.grid.selection, + background_color, + content_x, + content_y, + ); } - character_styles.clear(); } if self.grid.ring_bell { let ring_bell = '\u{7}'; - vte_output.push(ring_bell); + raw_vte_output.push(ring_bell); self.grid.ring_bell = false; } self.set_should_render(false); - Some(vte_output) + Some((character_chunks, Some(raw_vte_output))) } else { None } @@ -289,9 +230,8 @@ impl Pane for TerminalPane { client_id: ClientId, frame_params: FrameParams, input_mode: InputMode, - ) -> Option<String> { + ) -> Option<(Vec<CharacterChunk>, Option<String>)> { // TODO: remove the cursor stuff from here - let mut vte_output = None; let pane_title = if self.pane_name.is_empty() && input_mode == InputMode::RenamePane && frame_params.is_main_client @@ -316,18 +256,24 @@ impl Pane for TerminalPane { Some(last_frame) => { if &frame != last_frame { if !self.borderless { - vte_output = Some(frame.render()); + let frame_output = frame.render(); + self.frame.insert(client_id, frame); + Some(frame_output) + } else { + None } - self.frame.insert(client_id, frame); + } else { + None } - vte_output } None => { if !self.borderless { - vte_output = Some(frame.render()); + let frame_output = frame.render(); + self.frame.insert(client_id, frame); + Some(frame_output) + } else { + None } - self.frame.insert(client_id, frame); - vte_output } } } @@ -452,7 +398,7 @@ impl Pane for TerminalPane { self.grid.pending_messages_to_pty.drain(..).collect() } - fn start_selection(&mut self, start: &Position, client_id: ClientId) { + fn start_selection(&mut self, start: &Position, _client_id: ClientId) { self.grid.start_selection(start); self.set_should_render(true); } @@ -511,12 +457,14 @@ impl TerminalPane { palette: Palette, pane_index: usize, pane_name: String, + link_handler: Rc<RefCell<LinkHandler>>, ) -> TerminalPane { let initial_pane_title = format!("Pane #{}", pane_index); let grid = Grid::new( position_and_size.rows.as_usize(), position_and_size.cols.as_usize(), palette, + link_handler.clone(), ); TerminalPane { frame: HashMap::new(), |