diff options
Diffstat (limited to 'alacritty/src/display.rs')
-rw-r--r-- | alacritty/src/display.rs | 173 |
1 files changed, 149 insertions, 24 deletions
diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs index d61a5bbd..53e6fc58 100644 --- a/alacritty/src/display.rs +++ b/alacritty/src/display.rs @@ -1,6 +1,7 @@ //! The display subsystem including window management, font rasterization, and //! GPU drawing. +use std::cmp::min; use std::f64; use std::fmt::{self, Formatter}; #[cfg(not(any(target_os = "macos", windows)))] @@ -15,6 +16,7 @@ use glutin::platform::unix::EventLoopWindowTargetExtUnix; use glutin::window::CursorIcon; use log::{debug, info}; use parking_lot::MutexGuard; +use unicode_width::UnicodeWidthChar; #[cfg(not(any(target_os = "macos", windows)))] use wayland_client::{Display as WaylandDisplay, EventQueue}; @@ -23,21 +25,23 @@ use font::set_font_smoothing; use font::{self, Rasterize}; use alacritty_terminal::config::{Font, StartupMode}; -use alacritty_terminal::event::OnResize; -use alacritty_terminal::index::Line; +use alacritty_terminal::event::{EventListener, OnResize}; +use alacritty_terminal::grid::Dimensions; +use alacritty_terminal::index::{Column, Line, Point}; use alacritty_terminal::message_bar::MessageBuffer; use alacritty_terminal::meter::Meter; use alacritty_terminal::selection::Selection; -use alacritty_terminal::term::color::Rgb; use alacritty_terminal::term::{RenderableCell, SizeInfo, Term, TermMode}; use crate::config::Config; -use crate::event::{DisplayUpdate, Mouse}; +use crate::event::Mouse; use crate::renderer::rects::{RenderLines, RenderRect}; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::url::{Url, Urls}; use crate::window::{self, Window}; +const SEARCH_LABEL: &str = "Search: "; + #[derive(Debug)] pub enum Error { /// Error with window management. @@ -99,6 +103,44 @@ impl From<glutin::ContextError> for Error { } } +#[derive(Default, Clone, Debug, PartialEq)] +pub struct DisplayUpdate { + pub dirty: bool, + + dimensions: Option<PhysicalSize<u32>>, + font: Option<Font>, + cursor_dirty: bool, +} + +impl DisplayUpdate { + pub fn dimensions(&self) -> Option<PhysicalSize<u32>> { + self.dimensions + } + + pub fn font(&self) -> Option<&Font> { + self.font.as_ref() + } + + pub fn cursor_dirty(&self) -> bool { + self.cursor_dirty + } + + pub fn set_dimensions(&mut self, dimensions: PhysicalSize<u32>) { + self.dimensions = Some(dimensions); + self.dirty = true; + } + + pub fn set_font(&mut self, font: Font) { + self.font = Some(font); + self.dirty = true; + } + + pub fn set_cursor_dirty(&mut self) { + self.cursor_dirty = true; + self.dirty = true; + } +} + /// The display wraps a window, font rasterizer, and GPU renderer. pub struct Display { pub size_info: SizeInfo, @@ -300,7 +342,7 @@ impl Display { } /// Update font size and cell dimensions. - fn update_glyph_cache(&mut self, config: &Config, font: Font) { + fn update_glyph_cache(&mut self, config: &Config, font: &Font) { let size_info = &mut self.size_info; let cache = &mut self.glyph_cache; @@ -328,13 +370,16 @@ impl Display { terminal: &mut Term<T>, pty_resize_handle: &mut dyn OnResize, message_buffer: &MessageBuffer, + search_active: bool, config: &Config, update_pending: DisplayUpdate, - ) { + ) where + T: EventListener, + { // Update font size and cell dimensions. - if let Some(font) = update_pending.font { + if let Some(font) = update_pending.font() { self.update_glyph_cache(config, font); - } else if update_pending.cursor { + } else if update_pending.cursor_dirty() { self.clear_glyph_cache(); } @@ -346,7 +391,7 @@ impl Display { let mut padding_y = f32::from(config.window.padding.y) * self.size_info.dpr as f32; // Update the window dimensions. - if let Some(size) = update_pending.dimensions { + if let Some(size) = update_pending.dimensions() { // Ensure we have at least one column and row. self.size_info.width = (size.width as f32).max(cell_width + 2. * padding_x); self.size_info.height = (size.height as f32).max(cell_height + 2. * padding_y); @@ -369,6 +414,11 @@ impl Display { pty_size.height -= pty_size.cell_height * lines as f32; } + // Add an extra line for the current search regex. + if search_active { + pty_size.height -= pty_size.cell_height; + } + // Resize PTY. pty_resize_handle.on_resize(&pty_size); @@ -393,8 +443,10 @@ impl Display { config: &Config, mouse: &Mouse, mods: ModifiersState, + search_regex: Option<&String>, ) { let grid_cells: Vec<RenderableCell> = terminal.renderable_cells(config).collect(); + let search_regex = search_regex.map(|regex| Self::format_search(®ex)); let visual_bell_intensity = terminal.visual_bell.intensity(); let background_color = terminal.background_color(); let metrics = self.glyph_cache.font_metrics(); @@ -413,7 +465,17 @@ impl Display { // Update IME position. #[cfg(not(windows))] - self.window.update_ime_position(&terminal, &self.size_info); + { + let point = match &search_regex { + Some(regex) => { + let column = min(regex.len() + SEARCH_LABEL.len() - 1, terminal.cols().0 - 1); + Point::new(terminal.screen_lines() - 1, Column(column)) + }, + None => terminal.grid().cursor.point, + }; + + self.window.update_ime_position(point, &self.size_info); + } // Drop terminal as early as possible to free lock. drop(terminal); @@ -484,11 +546,13 @@ impl Display { rects.push(visual_bell_rect); } + let mut message_bar_lines = 0; if let Some(message) = message_buffer.message() { let text = message.text(&size_info); + message_bar_lines = text.len(); // Create a new rectangle for the background. - let start_line = size_info.lines().0 - text.len(); + let start_line = size_info.lines().0 - message_bar_lines; let y = size_info.cell_height.mul_add(start_line as f32, size_info.padding_y); let message_bar_rect = RenderRect::new(0., y, size_info.width, size_info.height - y, message.color(), 1.); @@ -500,31 +564,25 @@ impl Display { self.renderer.draw_rects(&size_info, rects); // Relay messages to the user. - let mut offset = 1; - for message_text in text.iter().rev() { + let fg = config.colors.primary.background; + for (i, message_text) in text.iter().rev().enumerate() { self.renderer.with_api(&config, &size_info, |mut api| { api.render_string( - &message_text, - Line(size_info.lines().saturating_sub(offset)), glyph_cache, + Line(size_info.lines().saturating_sub(i + 1)), + &message_text, + fg, None, ); }); - offset += 1; } } else { // Draw rectangles. self.renderer.draw_rects(&size_info, rects); } - // Draw render timer. - if config.render_timer() { - let timing = format!("{:.3} usec", self.meter.average()); - let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 }; - self.renderer.with_api(&config, &size_info, |mut api| { - api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, Some(color)); - }); - } + self.draw_search(config, &size_info, message_bar_lines, search_regex); + self.draw_render_timer(config, &size_info); // Frame event should be requested before swaping buffers, since it requires surface // `commit`, which is done by swap buffers under the hood. @@ -546,6 +604,73 @@ impl Display { } } + /// Format search regex to account for the cursor and fullwidth characters. + fn format_search(search_regex: &str) -> String { + // Add spacers for wide chars. + let mut text = String::with_capacity(search_regex.len()); + for c in search_regex.chars() { + text.push(c); + if c.width() == Some(2) { + text.push(' '); + } + } + + // Add cursor to show whitespace. + text.push('_'); + + text + } + + /// Draw current search regex. + fn draw_search( + &mut self, + config: &Config, + size_info: &SizeInfo, + message_bar_lines: usize, + search_regex: Option<String>, + ) { + let search_regex = match search_regex { + Some(search_regex) => search_regex, + None => return, + }; + let glyph_cache = &mut self.glyph_cache; + + let label_len = SEARCH_LABEL.len(); + let num_cols = size_info.cols().0; + + // Truncate beginning of text when it exceeds viewport width. + let text_len = search_regex.len(); + let truncate_len = min((text_len + label_len).saturating_sub(num_cols), text_len); + let text = &search_regex[truncate_len..]; + + // Assure text length is at least num_cols. + let padding_len = num_cols.saturating_sub(label_len); + let text = format!("{}{:<2$}", SEARCH_LABEL, text, padding_len); + + let fg = config.colors.search_bar_foreground(); + let bg = config.colors.search_bar_background(); + let line = size_info.lines() - message_bar_lines - 1; + self.renderer.with_api(&config, &size_info, |mut api| { + api.render_string(glyph_cache, line, &text, fg, Some(bg)); + }); + } + + /// Draw render timer. + fn draw_render_timer(&mut self, config: &Config, size_info: &SizeInfo) { + if !config.render_timer() { + return; + } + let glyph_cache = &mut self.glyph_cache; + + let timing = format!("{:.3} usec", self.meter.average()); + let fg = config.colors.normal().black; + let bg = config.colors.normal().red; + + self.renderer.with_api(&config, &size_info, |mut api| { + api.render_string(glyph_cache, size_info.lines() - 2, &timing[..], fg, Some(bg)); + }); + } + /// Requst a new frame for a window on Wayland. #[inline] #[cfg(not(any(target_os = "macos", windows)))] |