From 35efb4619c4b7a77b3c30de763856bc4441e236e Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Thu, 7 Feb 2019 22:36:45 +0000 Subject: Dynamically resize terminal for errors/warnings The warning and error messages now don't overwrite other terminal content anymore but instead resize the terminal to make sure that text can always be read. Instead of just showing that there is a new error and pointing to the log, errors will now be displayed fully in multiple lines of text, assuming that there is enough space left in the terminal. Explicit mouse click handling has also been added to the message bar, which made it possible to add a simple `close` button in the form of `[X]`. Alacritty's log file location is now stored in the `$ALACRITTY_LOG` environment variable which the shell inherits automatically. Previously there were some issues with the log file only being deleted when certain methods for closing Alacritty were used (like typing `exit`). This has been reworked and now Ctrl+D, exit and signals should all work properly. Before the config is reloaded, all current messages are now dropped. This should help with multiple terminals all getting clogged up at the same time when the config is broken. When one message is removed, all other duplicate messages are automatically removed too. --- src/renderer/lines.rs | 151 -------------------------------------------- src/renderer/mod.rs | 29 +++------ src/renderer/rects.rs | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 172 deletions(-) delete mode 100644 src/renderer/lines.rs create mode 100644 src/renderer/rects.rs (limited to 'src/renderer') diff --git a/src/renderer/lines.rs b/src/renderer/lines.rs deleted file mode 100644 index 3c250caf..00000000 --- a/src/renderer/lines.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2016 Joe Wilm, The Alacritty Project Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -use std::collections::HashMap; - -use crate::renderer::Rect; -use crate::term::cell::Flags; -use crate::term::{RenderableCell, SizeInfo}; -use crate::Rgb; -use font::Metrics; - -/// Lines for underline and strikeout. -pub struct Lines<'a> { - inner: Vec<(Rect, Rgb)>, - last_starts: HashMap>, - last_cell: Option, - metrics: &'a Metrics, - size: &'a SizeInfo, -} - -impl<'a> Lines<'a> { - pub fn new(metrics: &'a Metrics, size: &'a SizeInfo) -> Self { - let mut last_starts = HashMap::new(); - last_starts.insert(Flags::UNDERLINE, None); - last_starts.insert(Flags::STRIKEOUT, None); - - Self { - inner: Vec::new(), - last_cell: None, - last_starts, - metrics, - size, - } - } - - /// Convert the stored lines to rectangles for the renderer. - pub fn rects(mut self) -> Vec<(Rect, Rgb)> { - // If there's still a line pending, draw it until the last cell - for (flag, start_cell) in self.last_starts.iter_mut() { - if let Some(start) = start_cell { - self.inner.push( - create_rect( - &start, - &self.last_cell.unwrap(), - *flag, - &self.metrics, - &self.size, - ) - ); - } - } - - self.inner - } - - /// Update the stored lines with the next cell info. - pub fn update_lines(&mut self, cell: &RenderableCell) { - for (flag, start_cell) in self.last_starts.iter_mut() { - let flag = *flag; - *start_cell = match *start_cell { - // Check for end if line is present - Some(ref mut start) => { - let last_cell = self.last_cell.unwrap(); - - // No change in line - if cell.line == start.line - && cell.flags.contains(flag) - && cell.fg == start.fg - && cell.column == last_cell.column + 1 - { - continue; - } - - self.inner.push(create_rect( - &start, - &last_cell, - flag, - &self.metrics, - &self.size, - )); - - // Start a new line if the flag is present - if cell.flags.contains(flag) { - Some(*cell) - } else { - None - } - } - // Check for new start of line - None => if cell.flags.contains(flag) { - Some(*cell) - } else { - None - }, - }; - } - - self.last_cell = Some(*cell); - } -} - -/// Create a rectangle that starts on the left of `start` and ends on the right -/// of `end`, based on the given flag and size metrics. -fn create_rect( - start: &RenderableCell, - end: &RenderableCell, - flag: Flags, - metrics: &Metrics, - size: &SizeInfo, -) -> (Rect, Rgb) { - let start_x = start.column.0 as f32 * size.cell_width; - let end_x = (end.column.0 + 1) as f32 * size.cell_width; - let width = end_x - start_x; - - let (position, mut height) = match flag { - Flags::UNDERLINE => (metrics.underline_position, metrics.underline_thickness), - Flags::STRIKEOUT => (metrics.strikeout_position, metrics.strikeout_thickness), - _ => unimplemented!("Invalid flag for cell line drawing specified"), - }; - - // Make sure lines are always visible - height = height.max(1.); - - let cell_bottom = (start.line.0 as f32 + 1.) * size.cell_height; - let baseline = cell_bottom + metrics.descent; - - let mut y = baseline - position - height / 2.; - let max_y = cell_bottom - height; - if y > max_y { - y = max_y; - } - - let rect = Rect::new( - start_x + size.padding_x, - y.round() + size.padding_y, - width, - height.round(), - ); - - (rect, start.fg) -} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index e96d35a5..9a33410f 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -29,12 +29,12 @@ use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use crate::gl::types::*; use crate::gl; use crate::index::{Column, Line, RangeInclusive}; -use crate::Rgb; +use crate::term::color::Rgb; use crate::config::{self, Config, Delta}; use crate::term::{self, cell, RenderableCell}; -use crate::renderer::lines::Lines; +use crate::renderer::rects::{Rect, Rects}; -pub mod lines; +pub mod rects; // Shader paths for live reload static TEXT_SHADER_F_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl"); @@ -668,7 +668,7 @@ impl QuadRenderer { config: &Config, props: &term::SizeInfo, visual_bell_intensity: f64, - cell_line_rects: Lines, + cell_line_rects: Rects, ) { // Swap to rectangle rendering program unsafe { @@ -866,20 +866,6 @@ impl QuadRenderer { } } -#[derive(Debug, Copy, Clone)] -pub struct Rect { - x: T, - y: T, - width: T, - height: T, -} - -impl Rect { - pub fn new(x: T, y: T, width: T, height: T) -> Self { - Rect { x, y, width, height } - } -} - impl<'a> RenderApi<'a> { pub fn clear(&self, color: Rgb) { let alpha = self.config.background_opacity().get(); @@ -941,8 +927,9 @@ impl<'a> RenderApi<'a> { string: &str, line: Line, glyph_cache: &mut GlyphCache, - color: Rgb + color: Option ) { + let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0); let col = Column(0); let cells = string @@ -956,10 +943,10 @@ impl<'a> RenderApi<'a> { chars[0] = c; chars }, - bg: color, + bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0}), fg: Rgb { r: 0, g: 0, b: 0 }, flags: cell::Flags::empty(), - bg_alpha: 1.0, + bg_alpha, }) .collect::>(); diff --git a/src/renderer/rects.rs b/src/renderer/rects.rs new file mode 100644 index 00000000..e066c365 --- /dev/null +++ b/src/renderer/rects.rs @@ -0,0 +1,169 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use std::collections::HashMap; + +use crate::term::cell::Flags; +use crate::term::{RenderableCell, SizeInfo}; +use crate::term::color::Rgb; +use font::Metrics; + +#[derive(Debug, Copy, Clone)] +pub struct Rect { + pub x: T, + pub y: T, + pub width: T, + pub height: T, +} + +impl Rect { + pub fn new(x: T, y: T, width: T, height: T) -> Self { + Rect { x, y, width, height } + } +} + +/// Rects for underline, strikeout and more. +pub struct Rects<'a> { + inner: Vec<(Rect, Rgb)>, + last_starts: HashMap>, + last_cell: Option, + metrics: &'a Metrics, + size: &'a SizeInfo, +} + +impl<'a> Rects<'a> { + pub fn new(metrics: &'a Metrics, size: &'a SizeInfo) -> Self { + let mut last_starts = HashMap::new(); + last_starts.insert(Flags::UNDERLINE, None); + last_starts.insert(Flags::STRIKEOUT, None); + + Self { + inner: Vec::new(), + last_cell: None, + last_starts, + metrics, + size, + } + } + + /// Convert the stored rects to rectangles for the renderer. + pub fn rects(mut self) -> Vec<(Rect, Rgb)> { + // If there's still a line pending, draw it until the last cell + for (flag, start_cell) in self.last_starts.iter_mut() { + if let Some(start) = start_cell { + self.inner.push( + create_rect( + &start, + &self.last_cell.unwrap(), + *flag, + &self.metrics, + &self.size, + ) + ); + } + } + + self.inner + } + + /// Update the stored lines with the next cell info. + pub fn update_lines(&mut self, cell: &RenderableCell) { + for (flag, start_cell) in self.last_starts.iter_mut() { + let flag = *flag; + *start_cell = match *start_cell { + // Check for end if line is present + Some(ref mut start) => { + let last_cell = self.last_cell.unwrap(); + + // No change in line + if cell.line == start.line + && cell.flags.contains(flag) + && cell.fg == start.fg + && cell.column == last_cell.column + 1 + { + continue; + } + + self.inner.push(create_rect( + &start, + &last_cell, + flag, + &self.metrics, + &self.size, + )); + + // Start a new line if the flag is present + if cell.flags.contains(flag) { + Some(*cell) + } else { + None + } + } + // Check for new start of line + None => if cell.flags.contains(flag) { + Some(*cell) + } else { + None + }, + }; + } + + self.last_cell = Some(*cell); + } + + // Add a rectangle + pub fn push(&mut self, rect: Rect, color: Rgb) { + self.inner.push((rect, color)); + } +} + +/// Create a rectangle that starts on the left of `start` and ends on the right +/// of `end`, based on the given flag and size metrics. +fn create_rect( + start: &RenderableCell, + end: &RenderableCell, + flag: Flags, + metrics: &Metrics, + size: &SizeInfo, +) -> (Rect, Rgb) { + let start_x = start.column.0 as f32 * size.cell_width; + let end_x = (end.column.0 + 1) as f32 * size.cell_width; + let width = end_x - start_x; + + let (position, mut height) = match flag { + Flags::UNDERLINE => (metrics.underline_position, metrics.underline_thickness), + Flags::STRIKEOUT => (metrics.strikeout_position, metrics.strikeout_thickness), + _ => unimplemented!("Invalid flag for cell line drawing specified"), + }; + + // Make sure lines are always visible + height = height.max(1.); + + let cell_bottom = (start.line.0 as f32 + 1.) * size.cell_height; + let baseline = cell_bottom + metrics.descent; + + let mut y = baseline - position - height / 2.; + let max_y = cell_bottom - height; + if y > max_y { + y = max_y; + } + + let rect = Rect::new( + start_x + size.padding_x, + y.round() + size.padding_y, + width, + height.round(), + ); + + (rect, start.fg) +} -- cgit