summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src/term/mod.rs
diff options
context:
space:
mode:
authorii41 <23465321+ii41@users.noreply.github.com>2020-09-27 15:36:08 -0700
committerGitHub <noreply@github.com>2020-09-27 22:36:08 +0000
commita754d06b44139b85e8b34a71ece4477cb1caa85e (patch)
tree1733f8d17101947b6df5e1ad15b3fd64cf1db9a0 /alacritty_terminal/src/term/mod.rs
parente9c0034f4d3ee003149fe5454942bea7ff3d98be (diff)
downloadalacritty-a754d06b44139b85e8b34a71ece4477cb1caa85e.tar.gz
alacritty-a754d06b44139b85e8b34a71ece4477cb1caa85e.zip
Add support for single line terminals
This changes the minimum terminal dimensions from 2 lines and 2 columns, to 1 line and 2 columns. This also reworks the `SizeInfo` to store the number of columns and lines and consistently has only the terminal lines/columns stored, instead of including the message bar and search in some places of the Alacritty renderer/input. These new changes also make it easy to properly start the selection scrolling as soon as the mouse is over the message bar, instead of waiting until it is beyond it. Fixes #4207. Co-authored-by: Christian Duerr <contact@christianduerr.com>
Diffstat (limited to 'alacritty_terminal/src/term/mod.rs')
-rw-r--r--alacritty_terminal/src/term/mod.rs279
1 files changed, 135 insertions, 144 deletions
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs
index 00726dad..a33ac919 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -40,8 +40,13 @@ const MAX_SEARCH_LINES: usize = 100;
/// Default tab interval, corresponding to terminfo `it` value.
const INITIAL_TABSTOPS: usize = 8;
-/// Minimum number of columns and lines.
-const MIN_SIZE: usize = 2;
+/// Minimum number of columns.
+///
+/// A minimum of 2 is necessary to hold fullwidth unicode characters.
+pub const MIN_COLS: usize = 2;
+
+/// Minimum number of visible lines.
+pub const MIN_SCREEN_LINES: usize = 1;
/// Cursor storing all information relevant for rendering.
#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize)]
@@ -612,69 +617,139 @@ impl From<&BellConfig> for VisualBell {
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
pub struct SizeInfo {
/// Terminal window width.
- pub width: f32,
+ width: f32,
/// Terminal window height.
- pub height: f32,
+ height: f32,
/// Width of individual cell.
- pub cell_width: f32,
+ cell_width: f32,
/// Height of individual cell.
- pub cell_height: f32,
+ cell_height: f32,
/// Horizontal window padding.
- pub padding_x: f32,
+ padding_x: f32,
/// Horizontal window padding.
- pub padding_y: f32,
+ padding_y: f32,
- /// DPR of the current window.
- #[serde(default)]
- pub dpr: f64,
+ /// Number of lines in the viewport.
+ screen_lines: Line,
+
+ /// Number of columns in the viewport.
+ cols: Column,
}
impl SizeInfo {
- #[inline]
- pub fn lines(&self) -> Line {
- Line(((self.height - 2. * self.padding_y) / self.cell_height) as usize)
- }
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ width: f32,
+ height: f32,
+ cell_width: f32,
+ cell_height: f32,
+ mut padding_x: f32,
+ mut padding_y: f32,
+ dynamic_padding: bool,
+ ) -> SizeInfo {
+ if dynamic_padding {
+ padding_x = Self::dynamic_padding(padding_x.floor(), width, cell_width);
+ padding_y = Self::dynamic_padding(padding_y.floor(), height, cell_height);
+ }
- #[inline]
- pub fn cols(&self) -> Column {
- Column(((self.width - 2. * self.padding_x) / self.cell_width) as usize)
- }
+ let lines = (height - 2. * padding_y) / cell_height;
+ let screen_lines = Line(max(lines as usize, MIN_SCREEN_LINES));
- #[inline]
- pub fn padding_right(&self) -> usize {
- (self.padding_x + (self.width - 2. * self.padding_x) % self.cell_width) as usize
+ let cols = (width - 2. * padding_x) / cell_width;
+ let cols = Column(max(cols as usize, MIN_COLS));
+
+ SizeInfo {
+ width,
+ height,
+ cell_width,
+ cell_height,
+ padding_x: padding_x.floor(),
+ padding_y: padding_y.floor(),
+ screen_lines,
+ cols,
+ }
}
#[inline]
- pub fn padding_bottom(&self) -> usize {
- (self.padding_y + (self.height - 2. * self.padding_y) % self.cell_height) as usize
+ pub fn reserve_lines(&mut self, count: usize) {
+ self.screen_lines = Line(max(self.screen_lines.saturating_sub(count), MIN_SCREEN_LINES));
}
/// Check if coordinates are inside the terminal grid.
///
- /// The padding is not counted as part of the grid.
+ /// The padding, message bar or search are not counted as part of the grid.
#[inline]
pub fn contains_point(&self, x: usize, y: usize) -> bool {
- x < (self.width as usize - self.padding_right())
- && x >= self.padding_x as usize
- && y < (self.height as usize - self.padding_bottom())
- && y >= self.padding_y as usize
+ x <= (self.padding_x + self.cols.0 as f32 * self.cell_width) as usize
+ && x > self.padding_x as usize
+ && y <= (self.padding_y + self.screen_lines.0 as f32 * self.cell_height) as usize
+ && y > self.padding_y as usize
}
+ /// Convert window space pixels to terminal grid coordinates.
+ ///
+ /// If the coordinates are outside of the terminal grid, like positions inside the padding, the
+ /// coordinates will be clamped to the closest grid coordinates.
pub fn pixels_to_coords(&self, x: usize, y: usize) -> Point {
let col = Column(x.saturating_sub(self.padding_x as usize) / (self.cell_width as usize));
let line = Line(y.saturating_sub(self.padding_y as usize) / (self.cell_height as usize));
Point {
- line: min(line, Line(self.lines().saturating_sub(1))),
- col: min(col, Column(self.cols().saturating_sub(1))),
+ line: min(line, Line(self.screen_lines.saturating_sub(1))),
+ col: min(col, Column(self.cols.saturating_sub(1))),
}
}
+
+ #[inline]
+ pub fn width(&self) -> f32 {
+ self.width
+ }
+
+ #[inline]
+ pub fn height(&self) -> f32 {
+ self.height
+ }
+
+ #[inline]
+ pub fn cell_width(&self) -> f32 {
+ self.cell_width
+ }
+
+ #[inline]
+ pub fn cell_height(&self) -> f32 {
+ self.cell_height
+ }
+
+ #[inline]
+ pub fn padding_x(&self) -> f32 {
+ self.padding_x
+ }
+
+ #[inline]
+ pub fn padding_y(&self) -> f32 {
+ self.padding_y
+ }
+
+ #[inline]
+ pub fn screen_lines(&self) -> Line {
+ self.screen_lines
+ }
+
+ #[inline]
+ pub fn cols(&self) -> Column {
+ self.cols
+ }
+
+ /// Calculate padding to spread it evenly around the terminal content.
+ #[inline]
+ fn dynamic_padding(padding: f32, dimension: f32, cell_dimension: f32) -> f32 {
+ padding + ((dimension - 2. * padding) % cell_dimension) / 2.
+ }
}
pub struct Term<T> {
@@ -751,8 +826,9 @@ pub struct Term<T> {
/// Current forward and backward buffer search regexes.
regex_search: Option<RegexSearch>,
- /// Information about window dimensions.
- size: SizeInfo,
+ /// Information about cell dimensions.
+ cell_width: usize,
+ cell_height: usize,
}
impl<T> Term<T> {
@@ -767,8 +843,8 @@ impl<T> Term<T> {
}
pub fn new<C>(config: &Config<C>, size: SizeInfo, event_proxy: T) -> Term<T> {
- let num_cols = size.cols();
- let num_lines = size.lines();
+ let num_cols = size.cols;
+ let num_lines = size.screen_lines;
let history_size = config.scrolling.history() as usize;
let grid = Grid::new(num_lines, num_cols, history_size, Cell::default());
@@ -803,7 +879,8 @@ impl<T> Term<T> {
title_stack: Vec::new(),
selection: None,
regex_search: None,
- size,
+ cell_width: size.cell_width as usize,
+ cell_height: size.cell_height as usize,
}
}
@@ -974,12 +1051,14 @@ impl<T> Term<T> {
/// Resize terminal to new dimensions.
pub fn resize(&mut self, size: SizeInfo) {
- self.size = size;
+ self.cell_width = size.cell_width as usize;
+ self.cell_height = size.cell_height as usize;
let old_cols = self.cols();
let old_lines = self.screen_lines();
- let num_cols = max(size.cols(), Column(MIN_SIZE));
- let num_lines = max(size.lines(), Line(MIN_SIZE));
+
+ let num_cols = size.cols;
+ let num_lines = size.screen_lines;
if old_cols == num_cols && old_lines == num_lines {
debug!("Term::resize dimensions unchanged");
@@ -2233,8 +2312,8 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn text_area_size_pixels<W: io::Write>(&mut self, writer: &mut W) {
- let width = self.size.cell_width as usize * self.cols().0;
- let height = self.size.cell_height as usize * self.screen_lines().0;
+ let width = self.cell_width * self.cols().0;
+ let height = self.cell_height * self.screen_lines().0;
let _ = write!(writer, "\x1b[4;{};{}t", height, width);
}
@@ -2356,15 +2435,7 @@ pub mod test {
.unwrap_or(0);
// Create terminal with the appropriate dimensions.
- let size = SizeInfo {
- width: num_cols as f32,
- height: lines.len() as f32,
- cell_width: 1.,
- cell_height: 1.,
- padding_x: 0.,
- padding_y: 0.,
- dpr: 1.,
- };
+ let size = SizeInfo::new(num_cols as f32, lines.len() as f32, 1., 1., 0., 0., false);
let mut term = Term::new(&Config::<()>::default(), size, ());
// Fill terminal with content.
@@ -2413,15 +2484,7 @@ mod tests {
#[test]
fn semantic_selection_works() {
- let size = SizeInfo {
- width: 21.0,
- height: 51.0,
- cell_width: 3.0,
- cell_height: 3.0,
- padding_x: 0.0,
- padding_y: 0.0,
- dpr: 1.0,
- };
+ let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, Mock);
let mut grid: Grid<Cell> = Grid::new(Line(3), Column(5), 0, Cell::default());
for i in 0..5 {
@@ -2469,15 +2532,7 @@ mod tests {
#[test]
fn line_selection_works() {
- let size = SizeInfo {
- width: 21.0,
- height: 51.0,
- cell_width: 3.0,
- cell_height: 3.0,
- padding_x: 0.0,
- padding_y: 0.0,
- dpr: 1.0,
- };
+ let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, Mock);
let mut grid: Grid<Cell> = Grid::new(Line(1), Column(5), 0, Cell::default());
for i in 0..5 {
@@ -2498,15 +2553,7 @@ mod tests {
#[test]
fn selecting_empty_line() {
- let size = SizeInfo {
- width: 21.0,
- height: 51.0,
- cell_width: 3.0,
- cell_height: 3.0,
- padding_x: 0.0,
- padding_y: 0.0,
- dpr: 1.0,
- };
+ let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, Mock);
let mut grid: Grid<Cell> = Grid::new(Line(3), Column(3), 0, Cell::default());
for l in 0..3 {
@@ -2543,15 +2590,7 @@ mod tests {
#[test]
fn input_line_drawing_character() {
- let size = SizeInfo {
- width: 21.0,
- height: 51.0,
- cell_width: 3.0,
- cell_height: 3.0,
- padding_x: 0.0,
- padding_y: 0.0,
- dpr: 1.0,
- };
+ let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, Mock);
let cursor = Point::new(Line(0), Column(0));
term.configure_charset(CharsetIndex::G0, StandardCharset::SpecialCharacterAndLineDrawing);
@@ -2562,15 +2601,7 @@ mod tests {
#[test]
fn clear_saved_lines() {
- let size = SizeInfo {
- width: 21.0,
- height: 51.0,
- cell_width: 3.0,
- cell_height: 3.0,
- padding_x: 0.0,
- padding_y: 0.0,
- dpr: 1.0,
- };
+ let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, Mock);
// Add one line of scrollback.
@@ -2592,15 +2623,7 @@ mod tests {
#[test]
fn grow_lines_updates_active_cursor_pos() {
- let mut size = SizeInfo {
- width: 100.0,
- height: 10.0,
- cell_width: 1.0,
- cell_height: 1.0,
- padding_x: 0.0,
- padding_y: 0.0,
- dpr: 1.0,
- };
+ let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, Mock);
// Create 10 lines of scrollback.
@@ -2611,7 +2634,7 @@ mod tests {
assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
// Increase visible lines.
- size.height = 30.;
+ size.screen_lines.0 = 30;
term.resize(size);
assert_eq!(term.history_size(), 0);
@@ -2620,15 +2643,7 @@ mod tests {
#[test]
fn grow_lines_updates_inactive_cursor_pos() {
- let mut size = SizeInfo {
- width: 100.0,
- height: 10.0,
- cell_width: 1.0,
- cell_height: 1.0,
- padding_x: 0.0,
- padding_y: 0.0,
- dpr: 1.0,
- };
+ let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, Mock);
// Create 10 lines of scrollback.
@@ -2642,7 +2657,7 @@ mod tests {
term.set_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
// Increase visible lines.
- size.height = 30.;
+ size.screen_lines.0 = 30;
term.resize(size);
// Leave alt screen.
@@ -2654,15 +2669,7 @@ mod tests {
#[test]
fn shrink_lines_updates_active_cursor_pos() {
- let mut size = SizeInfo {
- width: 100.0,
- height: 10.0,
- cell_width: 1.0,
- cell_height: 1.0,
- padding_x: 0.0,
- padding_y: 0.0,
- dpr: 1.0,
- };
+ let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, Mock);
// Create 10 lines of scrollback.
@@ -2673,7 +2680,7 @@ mod tests {
assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
// Increase visible lines.
- size.height = 5.;
+ size.screen_lines.0 = 5;
term.resize(size);
assert_eq!(term.history_size(), 15);
@@ -2682,15 +2689,7 @@ mod tests {
#[test]
fn shrink_lines_updates_inactive_cursor_pos() {
- let mut size = SizeInfo {
- width: 100.0,
- height: 10.0,
- cell_width: 1.0,
- cell_height: 1.0,
- padding_x: 0.0,
- padding_y: 0.0,
- dpr: 1.0,
- };
+ let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, Mock);
// Create 10 lines of scrollback.
@@ -2704,7 +2703,7 @@ mod tests {
term.set_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
// Increase visible lines.
- size.height = 5.;
+ size.screen_lines.0 = 5;
term.resize(size);
// Leave alt screen.
@@ -2716,15 +2715,7 @@ mod tests {
#[test]
fn window_title() {
- let size = SizeInfo {
- width: 21.0,
- height: 51.0,
- cell_width: 3.0,
- cell_height: 3.0,
- padding_x: 0.0,
- padding_y: 0.0,
- dpr: 1.0,
- };
+ let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, Mock);
// Title None by default.