fix(performance): output buffer (#567)
* work * work * fix(performance): output buffer * style(import): remove extraneous * style(fmt): make rustfmt happy * fix(performance): minor adjustments to padding and truncating * style(fmt): make rustfmt happy * style(clippy): make clippy happy
This commit is contained in:
parent
4b7fe3ca7b
commit
0bab7c1245
5 changed files with 244 additions and 71 deletions
|
|
@ -95,10 +95,9 @@ fn transfer_rows_down(
|
||||||
next_lines.push(next_line);
|
next_lines.push(next_line);
|
||||||
next_lines.append(&mut top_non_canonical_rows_in_dst);
|
next_lines.append(&mut top_non_canonical_rows_in_dst);
|
||||||
next_lines = match max_dst_width {
|
next_lines = match max_dst_width {
|
||||||
Some(max_row_width) => {
|
Some(max_row_width) => Row::from_rows(next_lines, max_row_width)
|
||||||
Row::from_rows(next_lines).split_to_rows_of_length(max_row_width)
|
.split_to_rows_of_length(max_row_width),
|
||||||
}
|
None => vec![Row::from_rows(next_lines, 0)],
|
||||||
None => vec![Row::from_rows(next_lines)],
|
|
||||||
};
|
};
|
||||||
if next_lines.is_empty() {
|
if next_lines.is_empty() {
|
||||||
// no more lines at source, the line we popped was probably empty
|
// no more lines at source, the line we popped was probably empty
|
||||||
|
|
@ -114,11 +113,12 @@ fn transfer_rows_down(
|
||||||
if !next_lines.is_empty() {
|
if !next_lines.is_empty() {
|
||||||
match max_src_width {
|
match max_src_width {
|
||||||
Some(max_row_width) => {
|
Some(max_row_width) => {
|
||||||
let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_row_width);
|
let excess_rows = Row::from_rows(next_lines, max_row_width)
|
||||||
|
.split_to_rows_of_length(max_row_width);
|
||||||
source.extend(excess_rows);
|
source.extend(excess_rows);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let excess_row = Row::from_rows(next_lines);
|
let excess_row = Row::from_rows(next_lines, 0);
|
||||||
bounded_push(source, excess_row);
|
bounded_push(source, excess_row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -144,10 +144,9 @@ fn transfer_rows_up(
|
||||||
}
|
}
|
||||||
next_lines.push(next_line);
|
next_lines.push(next_line);
|
||||||
next_lines = match max_dst_width {
|
next_lines = match max_dst_width {
|
||||||
Some(max_row_width) => {
|
Some(max_row_width) => Row::from_rows(next_lines, max_row_width)
|
||||||
Row::from_rows(next_lines).split_to_rows_of_length(max_row_width)
|
.split_to_rows_of_length(max_row_width),
|
||||||
}
|
None => vec![Row::from_rows(next_lines, 0)],
|
||||||
None => vec![Row::from_rows(next_lines)],
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
break; // no more rows
|
break; // no more rows
|
||||||
|
|
@ -158,13 +157,14 @@ fn transfer_rows_up(
|
||||||
if !next_lines.is_empty() {
|
if !next_lines.is_empty() {
|
||||||
match max_src_width {
|
match max_src_width {
|
||||||
Some(max_row_width) => {
|
Some(max_row_width) => {
|
||||||
let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_row_width);
|
let excess_rows = Row::from_rows(next_lines, max_row_width)
|
||||||
|
.split_to_rows_of_length(max_row_width);
|
||||||
for row in excess_rows {
|
for row in excess_rows {
|
||||||
source.insert(0, row);
|
source.insert(0, row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let excess_row = Row::from_rows(next_lines);
|
let excess_row = Row::from_rows(next_lines, 0);
|
||||||
source.insert(0, excess_row);
|
source.insert(0, excess_row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,6 +191,107 @@ pub fn create_horizontal_tabstops(columns: usize) -> BTreeSet<usize> {
|
||||||
horizontal_tabstops
|
horizontal_tabstops
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CharacterChunk {
|
||||||
|
pub terminal_characters: Vec<TerminalCharacter>,
|
||||||
|
pub x: usize,
|
||||||
|
pub y: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct OutputBuffer {
|
||||||
|
changed_lines: Vec<usize>, // line index
|
||||||
|
should_update_all_lines: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OutputBuffer {
|
||||||
|
fn default() -> Self {
|
||||||
|
OutputBuffer {
|
||||||
|
changed_lines: vec![],
|
||||||
|
should_update_all_lines: true, // first time we should do a full render
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputBuffer {
|
||||||
|
pub fn update_line(&mut self, line_index: usize) {
|
||||||
|
if !self.should_update_all_lines {
|
||||||
|
self.changed_lines.push(line_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn update_all_lines(&mut self) {
|
||||||
|
self.clear();
|
||||||
|
self.should_update_all_lines = true;
|
||||||
|
}
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.changed_lines.clear();
|
||||||
|
self.should_update_all_lines = false;
|
||||||
|
}
|
||||||
|
pub fn changed_chunks_in_viewport(
|
||||||
|
&self,
|
||||||
|
viewport: &[Row],
|
||||||
|
viewport_width: usize,
|
||||||
|
viewport_height: usize,
|
||||||
|
) -> Vec<CharacterChunk> {
|
||||||
|
if self.should_update_all_lines {
|
||||||
|
let mut changed_chunks = Vec::with_capacity(viewport.len());
|
||||||
|
for line_index in 0..viewport_height {
|
||||||
|
let terminal_characters =
|
||||||
|
self.extract_line_from_viewport(line_index, viewport, viewport_width);
|
||||||
|
changed_chunks.push(CharacterChunk {
|
||||||
|
x: 0,
|
||||||
|
y: line_index,
|
||||||
|
terminal_characters,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
changed_chunks
|
||||||
|
} else {
|
||||||
|
let mut line_changes = self.changed_lines.to_vec();
|
||||||
|
line_changes.sort_unstable();
|
||||||
|
line_changes.dedup();
|
||||||
|
let mut changed_chunks = Vec::with_capacity(line_changes.len());
|
||||||
|
for line_index in line_changes {
|
||||||
|
let terminal_characters =
|
||||||
|
self.extract_line_from_viewport(line_index, viewport, viewport_width);
|
||||||
|
changed_chunks.push(CharacterChunk {
|
||||||
|
x: 0,
|
||||||
|
y: line_index,
|
||||||
|
terminal_characters,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
changed_chunks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn extract_characters_from_row(
|
||||||
|
&self,
|
||||||
|
row: &Row,
|
||||||
|
viewport_width: usize,
|
||||||
|
) -> Vec<TerminalCharacter> {
|
||||||
|
let mut terminal_characters: Vec<TerminalCharacter> = row.columns.iter().copied().collect();
|
||||||
|
// pad row
|
||||||
|
let row_width = row.width();
|
||||||
|
if row_width < viewport_width {
|
||||||
|
let mut padding = vec![EMPTY_TERMINAL_CHARACTER; viewport_width - row_width];
|
||||||
|
terminal_characters.append(&mut padding);
|
||||||
|
}
|
||||||
|
terminal_characters
|
||||||
|
}
|
||||||
|
fn extract_line_from_viewport(
|
||||||
|
&self,
|
||||||
|
line_index: usize,
|
||||||
|
viewport: &[Row],
|
||||||
|
viewport_width: usize,
|
||||||
|
) -> Vec<TerminalCharacter> {
|
||||||
|
match viewport.get(line_index) {
|
||||||
|
// TODO: iterator?
|
||||||
|
Some(row) => self.extract_characters_from_row(row, viewport_width),
|
||||||
|
None => {
|
||||||
|
vec![EMPTY_TERMINAL_CHARACTER; viewport_width]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Grid {
|
pub struct Grid {
|
||||||
lines_above: VecDeque<Row>,
|
lines_above: VecDeque<Row>,
|
||||||
|
|
@ -204,6 +305,7 @@ pub struct Grid {
|
||||||
active_charset: CharsetIndex,
|
active_charset: CharsetIndex,
|
||||||
preceding_char: Option<TerminalCharacter>,
|
preceding_char: Option<TerminalCharacter>,
|
||||||
colors: Palette,
|
colors: Palette,
|
||||||
|
output_buffer: OutputBuffer,
|
||||||
pub should_render: bool,
|
pub should_render: bool,
|
||||||
pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "[D")
|
pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "[D")
|
||||||
pub erasure_mode: bool, // ERM
|
pub erasure_mode: bool, // ERM
|
||||||
|
|
@ -232,7 +334,7 @@ impl Grid {
|
||||||
pub fn new(rows: usize, columns: usize, colors: Palette) -> Self {
|
pub fn new(rows: usize, columns: usize, colors: Palette) -> Self {
|
||||||
Grid {
|
Grid {
|
||||||
lines_above: VecDeque::with_capacity(SCROLL_BACK),
|
lines_above: VecDeque::with_capacity(SCROLL_BACK),
|
||||||
viewport: vec![Row::new().canonical()],
|
viewport: vec![Row::new(columns).canonical()],
|
||||||
lines_below: vec![],
|
lines_below: vec![],
|
||||||
horizontal_tabstops: create_horizontal_tabstops(columns),
|
horizontal_tabstops: create_horizontal_tabstops(columns),
|
||||||
cursor: Cursor::new(0, 0),
|
cursor: Cursor::new(0, 0),
|
||||||
|
|
@ -251,8 +353,12 @@ impl Grid {
|
||||||
active_charset: Default::default(),
|
active_charset: Default::default(),
|
||||||
pending_messages_to_pty: vec![],
|
pending_messages_to_pty: vec![],
|
||||||
colors,
|
colors,
|
||||||
|
output_buffer: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn render_full_viewport(&mut self) {
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
|
}
|
||||||
pub fn advance_to_next_tabstop(&mut self, styles: CharacterStyles) {
|
pub fn advance_to_next_tabstop(&mut self, styles: CharacterStyles) {
|
||||||
let mut next_tabstop = None;
|
let mut next_tabstop = None;
|
||||||
for tabstop in self.horizontal_tabstops.iter() {
|
for tabstop in self.horizontal_tabstops.iter() {
|
||||||
|
|
@ -272,6 +378,7 @@ impl Grid {
|
||||||
let mut empty_character = EMPTY_TERMINAL_CHARACTER;
|
let mut empty_character = EMPTY_TERMINAL_CHARACTER;
|
||||||
empty_character.styles = styles;
|
empty_character.styles = styles;
|
||||||
self.pad_current_line_until(self.cursor.x);
|
self.pad_current_line_until(self.cursor.x);
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
pub fn move_to_previous_tabstop(&mut self) {
|
pub fn move_to_previous_tabstop(&mut self) {
|
||||||
let mut previous_tabstop = None;
|
let mut previous_tabstop = None;
|
||||||
|
|
@ -367,6 +474,7 @@ impl Grid {
|
||||||
let line_to_insert_at_viewport_top = self.lines_above.pop_back().unwrap();
|
let line_to_insert_at_viewport_top = self.lines_above.pop_back().unwrap();
|
||||||
self.viewport.insert(0, line_to_insert_at_viewport_top);
|
self.viewport.insert(0, line_to_insert_at_viewport_top);
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
}
|
}
|
||||||
pub fn scroll_down_one_line(&mut self) {
|
pub fn scroll_down_one_line(&mut self) {
|
||||||
if !self.lines_below.is_empty() && self.viewport.len() == self.height {
|
if !self.lines_below.is_empty() && self.viewport.len() == self.height {
|
||||||
|
|
@ -380,6 +488,7 @@ impl Grid {
|
||||||
}
|
}
|
||||||
let line_to_insert_at_viewport_bottom = self.lines_below.remove(0);
|
let line_to_insert_at_viewport_bottom = self.lines_below.remove(0);
|
||||||
self.viewport.push(line_to_insert_at_viewport_bottom);
|
self.viewport.push(line_to_insert_at_viewport_bottom);
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn change_size(&mut self, new_rows: usize, new_columns: usize) {
|
pub fn change_size(&mut self, new_rows: usize, new_columns: usize) {
|
||||||
|
|
@ -417,7 +526,7 @@ impl Grid {
|
||||||
for mut canonical_line in viewport_canonical_lines {
|
for mut canonical_line in viewport_canonical_lines {
|
||||||
let mut canonical_line_parts: Vec<Row> = vec![];
|
let mut canonical_line_parts: Vec<Row> = vec![];
|
||||||
if canonical_line.columns.is_empty() {
|
if canonical_line.columns.is_empty() {
|
||||||
canonical_line_parts.push(Row::new().canonical());
|
canonical_line_parts.push(Row::new(new_columns).canonical());
|
||||||
}
|
}
|
||||||
while !canonical_line.columns.is_empty() {
|
while !canonical_line.columns.is_empty() {
|
||||||
let next_wrap = if canonical_line.width() > new_columns {
|
let next_wrap = if canonical_line.width() > new_columns {
|
||||||
|
|
@ -515,8 +624,12 @@ impl Grid {
|
||||||
if self.scroll_region.is_some() {
|
if self.scroll_region.is_some() {
|
||||||
self.set_scroll_region_to_viewport_size();
|
self.set_scroll_region_to_viewport_size();
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
}
|
}
|
||||||
pub fn as_character_lines(&self) -> Vec<Vec<TerminalCharacter>> {
|
pub fn as_character_lines(&self) -> Vec<Vec<TerminalCharacter>> {
|
||||||
|
// this is only used in the tests
|
||||||
|
// it's not part of testing the app, but rather is used to interpret the snapshots created
|
||||||
|
// by it
|
||||||
let mut lines: Vec<Vec<TerminalCharacter>> = self
|
let mut lines: Vec<Vec<TerminalCharacter>> = self
|
||||||
.viewport
|
.viewport
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -537,6 +650,13 @@ impl Grid {
|
||||||
}
|
}
|
||||||
lines
|
lines
|
||||||
}
|
}
|
||||||
|
pub fn read_changes(&mut self) -> Vec<CharacterChunk> {
|
||||||
|
let changes =
|
||||||
|
self.output_buffer
|
||||||
|
.changed_chunks_in_viewport(&self.viewport, self.width, self.height);
|
||||||
|
self.output_buffer.clear();
|
||||||
|
changes
|
||||||
|
}
|
||||||
pub fn cursor_coordinates(&self) -> Option<(usize, usize)> {
|
pub fn cursor_coordinates(&self) -> Option<(usize, usize)> {
|
||||||
if self.cursor.is_hidden {
|
if self.cursor.is_hidden {
|
||||||
None
|
None
|
||||||
|
|
@ -548,17 +668,22 @@ impl Grid {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
self.scroll_up_one_line();
|
self.scroll_up_one_line();
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
}
|
}
|
||||||
pub fn move_viewport_down(&mut self, count: usize) {
|
pub fn move_viewport_down(&mut self, count: usize) {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
self.scroll_down_one_line();
|
self.scroll_down_one_line();
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
}
|
}
|
||||||
pub fn reset_viewport(&mut self) {
|
pub fn reset_viewport(&mut self) {
|
||||||
let row_count_below = self.lines_below.len();
|
let row_count_below = self.lines_below.len();
|
||||||
for _ in 0..row_count_below {
|
for _ in 0..row_count_below {
|
||||||
self.scroll_down_one_line();
|
self.scroll_down_one_line();
|
||||||
}
|
}
|
||||||
|
if row_count_below > 0 {
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn rotate_scroll_region_up(&mut self, count: usize) {
|
pub fn rotate_scroll_region_up(&mut self, count: usize) {
|
||||||
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
||||||
|
|
@ -572,6 +697,7 @@ impl Grid {
|
||||||
.insert(scroll_region_top, Row::from_columns(columns).canonical());
|
.insert(scroll_region_top, Row::from_columns(columns).canonical());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines(); // TODO: only update scroll region lines
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn rotate_scroll_region_down(&mut self, count: usize) {
|
pub fn rotate_scroll_region_down(&mut self, count: usize) {
|
||||||
|
|
@ -586,6 +712,7 @@ impl Grid {
|
||||||
self.viewport.push(Row::from_columns(columns).canonical());
|
self.viewport.push(Row::from_columns(columns).canonical());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines(); // TODO: only update scroll region lines
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn fill_viewport(&mut self, character: TerminalCharacter) {
|
pub fn fill_viewport(&mut self, character: TerminalCharacter) {
|
||||||
|
|
@ -594,6 +721,7 @@ impl Grid {
|
||||||
let columns = vec![character; self.width];
|
let columns = vec![character; self.width];
|
||||||
self.viewport.push(Row::from_columns(columns).canonical());
|
self.viewport.push(Row::from_columns(columns).canonical());
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
}
|
}
|
||||||
pub fn add_canonical_line(&mut self) {
|
pub fn add_canonical_line(&mut self) {
|
||||||
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
||||||
|
|
@ -616,6 +744,7 @@ impl Grid {
|
||||||
} else {
|
} else {
|
||||||
self.viewport.push(Row::from_columns(columns).canonical());
|
self.viewport.push(Row::from_columns(columns).canonical());
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines(); // TODO: only update scroll region lines
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -623,7 +752,7 @@ impl Grid {
|
||||||
// FIXME: this should add an empty line with the pad_character
|
// FIXME: this should add an empty line with the pad_character
|
||||||
// but for some reason this breaks rendering in various situations
|
// but for some reason this breaks rendering in various situations
|
||||||
// it needs to be investigated and fixed
|
// it needs to be investigated and fixed
|
||||||
let new_row = Row::new().canonical();
|
let new_row = Row::new(self.width).canonical();
|
||||||
self.viewport.push(new_row);
|
self.viewport.push(new_row);
|
||||||
}
|
}
|
||||||
if self.cursor.y == self.height - 1 {
|
if self.cursor.y == self.height - 1 {
|
||||||
|
|
@ -635,8 +764,10 @@ impl Grid {
|
||||||
Some(self.width),
|
Some(self.width),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
} else {
|
} else {
|
||||||
self.cursor.y += 1;
|
self.cursor.y += 1;
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn move_cursor_to_beginning_of_line(&mut self) {
|
pub fn move_cursor_to_beginning_of_line(&mut self) {
|
||||||
|
|
@ -646,25 +777,27 @@ impl Grid {
|
||||||
match self.viewport.get_mut(self.cursor.y) {
|
match self.viewport.get_mut(self.cursor.y) {
|
||||||
Some(row) => {
|
Some(row) => {
|
||||||
row.insert_character_at(terminal_character, self.cursor.x);
|
row.insert_character_at(terminal_character, self.cursor.x);
|
||||||
if row.len() > self.width {
|
// if row.len() > self.width {
|
||||||
|
if row.width() > self.width {
|
||||||
row.truncate(self.width);
|
row.truncate(self.width);
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// pad lines until cursor if they do not exist
|
// pad lines until cursor if they do not exist
|
||||||
for _ in self.viewport.len()..self.cursor.y {
|
for _ in self.viewport.len()..self.cursor.y {
|
||||||
self.viewport.push(Row::new().canonical());
|
self.viewport.push(Row::new(self.width).canonical());
|
||||||
}
|
}
|
||||||
self.viewport
|
self.viewport.push(
|
||||||
.push(Row::new().with_character(terminal_character).canonical());
|
Row::new(self.width)
|
||||||
|
.with_character(terminal_character)
|
||||||
|
.canonical(),
|
||||||
|
);
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn add_character_at_cursor_position(
|
pub fn add_character_at_cursor_position(&mut self, terminal_character: TerminalCharacter) {
|
||||||
&mut self,
|
|
||||||
terminal_character: TerminalCharacter,
|
|
||||||
max_width: usize,
|
|
||||||
) {
|
|
||||||
match self.viewport.get_mut(self.cursor.y) {
|
match self.viewport.get_mut(self.cursor.y) {
|
||||||
Some(row) => {
|
Some(row) => {
|
||||||
if self.insert_mode {
|
if self.insert_mode {
|
||||||
|
|
@ -672,15 +805,19 @@ impl Grid {
|
||||||
} else {
|
} else {
|
||||||
row.add_character_at(terminal_character, self.cursor.x);
|
row.add_character_at(terminal_character, self.cursor.x);
|
||||||
}
|
}
|
||||||
row.truncate(max_width);
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// pad lines until cursor if they do not exist
|
// pad lines until cursor if they do not exist
|
||||||
for _ in self.viewport.len()..self.cursor.y {
|
for _ in self.viewport.len()..self.cursor.y {
|
||||||
self.viewport.push(Row::new().canonical());
|
self.viewport.push(Row::new(self.width).canonical());
|
||||||
}
|
}
|
||||||
self.viewport
|
self.viewport.push(
|
||||||
.push(Row::new().with_character(terminal_character).canonical());
|
Row::new(self.width)
|
||||||
|
.with_character(terminal_character)
|
||||||
|
.canonical(),
|
||||||
|
);
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -702,17 +839,19 @@ impl Grid {
|
||||||
Some(self.width),
|
Some(self.width),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let wrapped_row = Row::new();
|
let wrapped_row = Row::new(self.width);
|
||||||
self.viewport.push(wrapped_row);
|
self.viewport.push(wrapped_row);
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
} else {
|
} else {
|
||||||
self.cursor.y += 1;
|
self.cursor.y += 1;
|
||||||
if self.viewport.len() <= self.cursor.y {
|
if self.viewport.len() <= self.cursor.y {
|
||||||
let line_wrapped_row = Row::new();
|
let line_wrapped_row = Row::new(self.width);
|
||||||
self.viewport.push(line_wrapped_row);
|
self.viewport.push(line_wrapped_row);
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.add_character_at_cursor_position(terminal_character, self.width);
|
self.add_character_at_cursor_position(terminal_character);
|
||||||
self.move_cursor_forward_until_edge(character_width);
|
self.move_cursor_forward_until_edge(character_width);
|
||||||
}
|
}
|
||||||
pub fn move_cursor_forward_until_edge(&mut self, count: usize) {
|
pub fn move_cursor_forward_until_edge(&mut self, count: usize) {
|
||||||
|
|
@ -724,10 +863,12 @@ impl Grid {
|
||||||
.get_mut(self.cursor.y)
|
.get_mut(self.cursor.y)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.replace_and_pad_end(self.cursor.x, self.width, replace_with);
|
.replace_and_pad_end(self.cursor.x, self.width, replace_with);
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
pub fn replace_characters_in_line_before_cursor(&mut self, replace_with: TerminalCharacter) {
|
pub fn replace_characters_in_line_before_cursor(&mut self, replace_with: TerminalCharacter) {
|
||||||
let row = self.viewport.get_mut(self.cursor.y).unwrap();
|
let row = self.viewport.get_mut(self.cursor.y).unwrap();
|
||||||
row.replace_and_pad_beginning(self.cursor.x, replace_with);
|
row.replace_and_pad_beginning(self.cursor.x, replace_with);
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
pub fn clear_all_after_cursor(&mut self, replace_with: TerminalCharacter) {
|
pub fn clear_all_after_cursor(&mut self, replace_with: TerminalCharacter) {
|
||||||
if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y) {
|
if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y) {
|
||||||
|
|
@ -737,6 +878,7 @@ impl Grid {
|
||||||
for row in self.viewport.iter_mut().skip(self.cursor.y + 1) {
|
for row in self.viewport.iter_mut().skip(self.cursor.y + 1) {
|
||||||
row.replace_columns(replace_with_columns.clone());
|
row.replace_columns(replace_with_columns.clone());
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines(); // TODO: only update the changed lines
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn clear_all_before_cursor(&mut self, replace_with: TerminalCharacter) {
|
pub fn clear_all_before_cursor(&mut self, replace_with: TerminalCharacter) {
|
||||||
|
|
@ -746,10 +888,12 @@ impl Grid {
|
||||||
for row in self.viewport.iter_mut().take(self.cursor.y) {
|
for row in self.viewport.iter_mut().take(self.cursor.y) {
|
||||||
row.replace_columns(replace_with_columns.clone());
|
row.replace_columns(replace_with_columns.clone());
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines(); // TODO: only update the changed lines
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn clear_cursor_line(&mut self) {
|
pub fn clear_cursor_line(&mut self) {
|
||||||
self.viewport.get_mut(self.cursor.y).unwrap().truncate(0);
|
self.viewport.get_mut(self.cursor.y).unwrap().truncate(0);
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
pub fn clear_all(&mut self, replace_with: TerminalCharacter) {
|
pub fn clear_all(&mut self, replace_with: TerminalCharacter) {
|
||||||
let replace_with_columns = vec![replace_with; self.width];
|
let replace_with_columns = vec![replace_with; self.width];
|
||||||
|
|
@ -757,17 +901,20 @@ impl Grid {
|
||||||
for row in self.viewport.iter_mut() {
|
for row in self.viewport.iter_mut() {
|
||||||
row.replace_columns(replace_with_columns.clone());
|
row.replace_columns(replace_with_columns.clone());
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
}
|
}
|
||||||
fn pad_current_line_until(&mut self, position: usize) {
|
fn pad_current_line_until(&mut self, position: usize) {
|
||||||
let current_row = self.viewport.get_mut(self.cursor.y).unwrap();
|
let current_row = self.viewport.get_mut(self.cursor.y).unwrap();
|
||||||
for _ in current_row.len()..position {
|
for _ in current_row.len()..position {
|
||||||
current_row.push(EMPTY_TERMINAL_CHARACTER);
|
current_row.push(EMPTY_TERMINAL_CHARACTER);
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
fn pad_lines_until(&mut self, position: usize, pad_character: TerminalCharacter) {
|
fn pad_lines_until(&mut self, position: usize, pad_character: TerminalCharacter) {
|
||||||
for _ in self.viewport.len()..=position {
|
for _ in self.viewport.len()..=position {
|
||||||
let columns = vec![pad_character; self.width];
|
let columns = vec![pad_character; self.width];
|
||||||
self.viewport.push(Row::from_columns(columns).canonical());
|
self.viewport.push(Row::from_columns(columns).canonical());
|
||||||
|
self.output_buffer.update_line(self.viewport.len() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn move_cursor_to(&mut self, x: usize, y: usize, pad_character: TerminalCharacter) {
|
pub fn move_cursor_to(&mut self, x: usize, y: usize, pad_character: TerminalCharacter) {
|
||||||
|
|
@ -816,13 +963,15 @@ impl Grid {
|
||||||
if scroll_region_bottom < self.viewport.len() {
|
if scroll_region_bottom < self.viewport.len() {
|
||||||
self.viewport.remove(scroll_region_bottom);
|
self.viewport.remove(scroll_region_bottom);
|
||||||
}
|
}
|
||||||
self.viewport.insert(current_line_index, Row::new()); // TODO: .canonical() ?
|
self.viewport
|
||||||
|
.insert(current_line_index, Row::new(self.width)); // TODO: .canonical() ?
|
||||||
} else if current_line_index > scroll_region_top
|
} else if current_line_index > scroll_region_top
|
||||||
&& current_line_index <= scroll_region_bottom
|
&& current_line_index <= scroll_region_bottom
|
||||||
{
|
{
|
||||||
self.move_cursor_up(count);
|
self.move_cursor_up(count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
}
|
}
|
||||||
pub fn move_cursor_down(&mut self, count: usize, pad_character: TerminalCharacter) {
|
pub fn move_cursor_down(&mut self, count: usize, pad_character: TerminalCharacter) {
|
||||||
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
||||||
|
|
@ -896,6 +1045,7 @@ impl Grid {
|
||||||
self.viewport.push(Row::from_columns(columns).canonical());
|
self.viewport.push(Row::from_columns(columns).canonical());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines(); // TODO: move accurately
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -920,6 +1070,7 @@ impl Grid {
|
||||||
self.viewport
|
self.viewport
|
||||||
.insert(current_line_index, Row::from_columns(columns).canonical());
|
.insert(current_line_index, Row::from_columns(columns).canonical());
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_all_lines(); // TODO: move accurately
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -941,6 +1092,7 @@ impl Grid {
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
current_row.replace_character_at(empty_character, self.cursor.x + i);
|
current_row.replace_character_at(empty_character, self.cursor.x + i);
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
pub fn erase_characters(&mut self, count: usize, empty_char_style: CharacterStyles) {
|
pub fn erase_characters(&mut self, count: usize, empty_char_style: CharacterStyles) {
|
||||||
let mut empty_character = EMPTY_TERMINAL_CHARACTER;
|
let mut empty_character = EMPTY_TERMINAL_CHARACTER;
|
||||||
|
|
@ -956,6 +1108,7 @@ impl Grid {
|
||||||
current_row.insert_character_at(empty_character, self.cursor.x);
|
current_row.insert_character_at(empty_character, self.cursor.x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
fn add_newline(&mut self) {
|
fn add_newline(&mut self) {
|
||||||
self.add_canonical_line();
|
self.add_canonical_line();
|
||||||
|
|
@ -967,7 +1120,7 @@ impl Grid {
|
||||||
fn reset_terminal_state(&mut self) {
|
fn reset_terminal_state(&mut self) {
|
||||||
self.lines_above = VecDeque::with_capacity(SCROLL_BACK);
|
self.lines_above = VecDeque::with_capacity(SCROLL_BACK);
|
||||||
self.lines_below = vec![];
|
self.lines_below = vec![];
|
||||||
self.viewport = vec![Row::new().canonical()];
|
self.viewport = vec![Row::new(self.width).canonical()];
|
||||||
self.alternative_lines_above_viewport_and_cursor = None;
|
self.alternative_lines_above_viewport_and_cursor = None;
|
||||||
self.cursor_key_mode = false;
|
self.cursor_key_mode = false;
|
||||||
self.scroll_region = None;
|
self.scroll_region = None;
|
||||||
|
|
@ -978,6 +1131,8 @@ impl Grid {
|
||||||
self.erasure_mode = false;
|
self.erasure_mode = false;
|
||||||
self.disable_linewrap = false;
|
self.disable_linewrap = false;
|
||||||
self.cursor.change_shape(CursorShape::Block);
|
self.cursor.change_shape(CursorShape::Block);
|
||||||
|
//debug_log_to_file(format!("u20"));
|
||||||
|
self.output_buffer.update_all_lines();
|
||||||
}
|
}
|
||||||
fn set_preceding_character(&mut self, terminal_character: TerminalCharacter) {
|
fn set_preceding_character(&mut self, terminal_character: TerminalCharacter) {
|
||||||
self.preceding_char = Some(terminal_character);
|
self.preceding_char = Some(terminal_character);
|
||||||
|
|
@ -987,6 +1142,8 @@ impl Grid {
|
||||||
impl Perform for Grid {
|
impl Perform for Grid {
|
||||||
fn print(&mut self, c: char) {
|
fn print(&mut self, c: char) {
|
||||||
let c = self.cursor.charsets[self.active_charset].map(c);
|
let c = self.cursor.charsets[self.active_charset].map(c);
|
||||||
|
// TODO: CONTINUE HERE - the slowness is coming from this function, do some debugging to
|
||||||
|
// see if it can be mitigated somehow?
|
||||||
// apparently, building TerminalCharacter like this without a "new" method
|
// apparently, building TerminalCharacter like this without a "new" method
|
||||||
// is a little faster
|
// is a little faster
|
||||||
let terminal_character = TerminalCharacter {
|
let terminal_character = TerminalCharacter {
|
||||||
|
|
@ -1306,8 +1463,10 @@ impl Perform for Grid {
|
||||||
&mut self.lines_above,
|
&mut self.lines_above,
|
||||||
VecDeque::with_capacity(SCROLL_BACK),
|
VecDeque::with_capacity(SCROLL_BACK),
|
||||||
);
|
);
|
||||||
let current_viewport =
|
let current_viewport = std::mem::replace(
|
||||||
std::mem::replace(&mut self.viewport, vec![Row::new().canonical()]);
|
&mut self.viewport,
|
||||||
|
vec![Row::new(self.width).canonical()],
|
||||||
|
);
|
||||||
let current_cursor = std::mem::replace(&mut self.cursor, Cursor::new(0, 0));
|
let current_cursor = std::mem::replace(&mut self.cursor, Cursor::new(0, 0));
|
||||||
self.alternative_lines_above_viewport_and_cursor =
|
self.alternative_lines_above_viewport_and_cursor =
|
||||||
Some((current_lines_above, current_viewport, current_cursor));
|
Some((current_lines_above, current_viewport, current_cursor));
|
||||||
|
|
@ -1594,28 +1753,22 @@ impl Debug for Row {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Row {
|
impl Row {
|
||||||
fn default() -> Self {
|
pub fn new(width: usize) -> Self {
|
||||||
Row {
|
Row {
|
||||||
columns: vec![],
|
columns: Vec::with_capacity(width),
|
||||||
is_canonical: false,
|
is_canonical: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Row {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
pub fn from_columns(columns: Vec<TerminalCharacter>) -> Self {
|
pub fn from_columns(columns: Vec<TerminalCharacter>) -> Self {
|
||||||
Row {
|
Row {
|
||||||
columns,
|
columns,
|
||||||
is_canonical: false,
|
is_canonical: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn from_rows(mut rows: Vec<Row>) -> Self {
|
pub fn from_rows(mut rows: Vec<Row>, width: usize) -> Self {
|
||||||
if rows.is_empty() {
|
if rows.is_empty() {
|
||||||
Row::new()
|
Row::new(width)
|
||||||
} else {
|
} else {
|
||||||
let mut first_row = rows.remove(0);
|
let mut first_row = rows.remove(0);
|
||||||
for row in rows.iter_mut() {
|
for row in rows.iter_mut() {
|
||||||
|
|
@ -1670,9 +1823,18 @@ impl Row {
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
let width_offset = self.excess_width_until(x);
|
let width_offset = self.excess_width_until(x);
|
||||||
// this is much more performant than remove/insert
|
let character_width = terminal_character.width;
|
||||||
self.columns.push(terminal_character);
|
let replaced_character = std::mem::replace(
|
||||||
self.columns.swap_remove(x.saturating_sub(width_offset));
|
&mut self.columns[x.saturating_sub(width_offset)],
|
||||||
|
terminal_character,
|
||||||
|
);
|
||||||
|
if character_width > replaced_character.width {
|
||||||
|
// this is done in a verbose manner because of performance
|
||||||
|
let width_difference = character_width - replaced_character.width;
|
||||||
|
for _ in 0..width_difference {
|
||||||
|
self.columns.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,9 @@ impl Pane for TerminalPane {
|
||||||
fn set_should_render(&mut self, should_render: bool) {
|
fn set_should_render(&mut self, should_render: bool) {
|
||||||
self.grid.should_render = should_render;
|
self.grid.should_render = should_render;
|
||||||
}
|
}
|
||||||
|
fn render_full_viewport(&mut self) {
|
||||||
|
self.grid.render_full_viewport();
|
||||||
|
}
|
||||||
fn selectable(&self) -> bool {
|
fn selectable(&self) -> bool {
|
||||||
self.selectable
|
self.selectable
|
||||||
}
|
}
|
||||||
|
|
@ -153,8 +156,6 @@ impl Pane for TerminalPane {
|
||||||
fn render(&mut self) -> Option<String> {
|
fn render(&mut self) -> Option<String> {
|
||||||
if self.should_render() {
|
if self.should_render() {
|
||||||
let mut vte_output = String::new();
|
let mut vte_output = String::new();
|
||||||
let buffer_lines = &self.read_buffer_as_lines();
|
|
||||||
let display_cols = self.get_columns();
|
|
||||||
let mut character_styles = CharacterStyles::new();
|
let mut character_styles = CharacterStyles::new();
|
||||||
if self.grid.clear_viewport_before_rendering {
|
if self.grid.clear_viewport_before_rendering {
|
||||||
for line_index in 0..self.grid.height {
|
for line_index in 0..self.grid.height {
|
||||||
|
|
@ -171,25 +172,31 @@ impl Pane for TerminalPane {
|
||||||
}
|
}
|
||||||
self.grid.clear_viewport_before_rendering = false;
|
self.grid.clear_viewport_before_rendering = false;
|
||||||
}
|
}
|
||||||
for (row, line) in buffer_lines.iter().enumerate() {
|
let max_width = self.columns();
|
||||||
let x = self.get_x();
|
for character_chunk in self.grid.read_changes() {
|
||||||
let y = self.get_y();
|
let pane_x = self.get_x();
|
||||||
vte_output.push_str(&format!("\u{1b}[{};{}H\u{1b}[m", y + row + 1, x + 1)); // goto row/col and reset styles
|
let pane_y = self.get_y();
|
||||||
for (col, t_character) in line.iter().enumerate() {
|
let chunk_absolute_x = pane_x + character_chunk.x;
|
||||||
if col < display_cols {
|
let chunk_absolute_y = pane_y + character_chunk.y;
|
||||||
// in some cases (eg. while resizing) some characters will spill over
|
let terminal_characters = character_chunk.terminal_characters;
|
||||||
// before they are corrected by the shell (for the prompt) or by reflowing
|
vte_output.push_str(&format!(
|
||||||
// lines
|
"\u{1b}[{};{}H\u{1b}[m",
|
||||||
if let Some(new_styles) =
|
chunk_absolute_y + 1,
|
||||||
character_styles.update_and_return_diff(&t_character.styles)
|
chunk_absolute_x + 1
|
||||||
{
|
)); // goto row/col and reset styles
|
||||||
// the terminal keeps the previous styles as long as we're in the same
|
|
||||||
// line, so we only want to update the new styles here (this also
|
let mut chunk_width = character_chunk.x;
|
||||||
// includes resetting previous styles as needed)
|
for t_character in terminal_characters {
|
||||||
vte_output.push_str(&new_styles.to_string());
|
chunk_width += t_character.width;
|
||||||
}
|
if chunk_width > max_width {
|
||||||
vte_output.push(t_character.character);
|
break;
|
||||||
}
|
}
|
||||||
|
if let Some(new_styles) =
|
||||||
|
character_styles.update_and_return_diff(&t_character.styles)
|
||||||
|
{
|
||||||
|
vte_output.push_str(&new_styles.to_string());
|
||||||
|
}
|
||||||
|
vte_output.push(t_character.character);
|
||||||
}
|
}
|
||||||
character_styles.clear();
|
character_styles.clear();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ source: zellij-server/src/panes/./unit/grid_tests.rs
|
||||||
expression: "format!(\"{:?}\", grid)"
|
expression: "format!(\"{:?}\", grid)"
|
||||||
|
|
||||||
---
|
---
|
||||||
00 (C): A******************************************************************************BAAAAAAAAAAAAAAAAA
|
00 (C): A******************************************************************************BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
01 (C):
|
01 (C):
|
||||||
02 (C):
|
02 (C):
|
||||||
03 (C): Test of 'Insert Mode'. The top line should be 'A*** ... ***B'. Push <RETURN>
|
03 (C): Test of 'Insert Mode'. The top line should be 'A*** ... ***B'. Push <RETURN>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ source: zellij-server/src/panes/./unit/grid_tests.rs
|
||||||
expression: "format!(\"{:?}\", grid)"
|
expression: "format!(\"{:?}\", grid)"
|
||||||
|
|
||||||
---
|
---
|
||||||
00 (C): ABAAAAAAAAAAAAAAAAA
|
00 (C): ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
01 (C):
|
01 (C):
|
||||||
02 (C):
|
02 (C):
|
||||||
03 (C): Test of 'Delete Character'. The top line should be 'AB'. Push <RETURN>
|
03 (C): Test of 'Delete Character'. The top line should be 'AB'. Push <RETURN>
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,7 @@ pub trait Pane {
|
||||||
// we should probably refactor away from this trait at some point
|
// we should probably refactor away from this trait at some point
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
fn render_full_viewport(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
|
|
@ -619,6 +620,7 @@ impl Tab {
|
||||||
for message in messages_to_pty {
|
for message in messages_to_pty {
|
||||||
self.write_to_pane_id(message, PaneId::Terminal(pid));
|
self.write_to_pane_id(message, PaneId::Terminal(pid));
|
||||||
}
|
}
|
||||||
|
// self.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn write_to_terminals_on_current_tab(&mut self, input_bytes: Vec<u8>) {
|
pub fn write_to_terminals_on_current_tab(&mut self, input_bytes: Vec<u8>) {
|
||||||
|
|
@ -707,6 +709,7 @@ impl Tab {
|
||||||
active_terminal.rows() as u16,
|
active_terminal.rows() as u16,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
self.set_force_render();
|
||||||
self.render();
|
self.render();
|
||||||
self.toggle_fullscreen_is_active();
|
self.toggle_fullscreen_is_active();
|
||||||
}
|
}
|
||||||
|
|
@ -717,6 +720,7 @@ impl Tab {
|
||||||
pub fn set_force_render(&mut self) {
|
pub fn set_force_render(&mut self) {
|
||||||
for pane in self.panes.values_mut() {
|
for pane in self.panes.values_mut() {
|
||||||
pane.set_should_render(true);
|
pane.set_should_render(true);
|
||||||
|
pane.render_full_viewport();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_sync_panes_active(&self) -> bool {
|
pub fn is_sync_panes_active(&self) -> bool {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue