Implement user actions for font resize (#625)

Adds support for font resizing at run-time. Three new actions are
introduced:

* IncreaseFontSize - Increases current font size by 1.0
* DecreaseFontSize - Decreases current font size by 1.0
* ResetFontSize - Resets font size to that specified in the
  configuration.

The stock config files have example configuration for each which should
match gnome-terminal. For convenience, the config entries are:

    - { key: Key0,     mods: Control, action: ResetFontSize    }
    - { key: Equals,   mods: Control, action: IncreaseFontSize }
    - { key: Subtract, mods: Control, action: DecreaseFontSize }
This commit is contained in:
Dan Aloni 2017-10-14 20:35:56 +03:00 committed by Joe Wilm
parent fd410f9ec8
commit 8a0b1d9c3f
9 changed files with 149 additions and 49 deletions

View File

@ -215,6 +215,9 @@ key_bindings:
- { key: Q, mods: Command, action: Quit }
- { key: W, mods: Command, action: Quit }
- { key: Insert, mods: Shift, action: PasteSelection }
- { key: Key0, mods: Control, action: ResetFontSize }
- { key: Equals, mods: Control, action: IncreaseFontSize }
- { key: Subtract, mods: Control, action: DecreaseFontSize }
- { key: Home, chars: "\x1bOH", mode: AppCursor }
- { key: Home, chars: "\x1b[H", mode: ~AppCursor }
- { key: End, chars: "\x1bOF", mode: AppCursor }

View File

@ -196,6 +196,9 @@ key_bindings:
- { key: Home, chars: "\x1b[H", mode: ~AppCursor }
- { key: End, chars: "\x1bOF", mode: AppCursor }
- { key: End, chars: "\x1b[F", mode: ~AppCursor }
- { key: Key0, mods: Control, action: ResetFontSize }
- { key: Equals, mods: Control, action: IncreaseFontSize }
- { key: Subtract, mods: Control, action: DecreaseFontSize }
- { key: PageUp, mods: Shift, chars: "\x1b[5;2~" }
- { key: PageUp, mods: Control, chars: "\x1b[5;5~" }
- { key: PageUp, chars: "\x1b[5~" }

View File

@ -409,6 +409,9 @@ impl<'a> de::Deserialize<'a> for ActionWrapper {
"Paste" => Action::Paste,
"Copy" => Action::Copy,
"PasteSelection" => Action::PasteSelection,
"IncreaseFontSize" => Action::IncreaseFontSize,
"DecreaseFontSize" => Action::DecreaseFontSize,
"ResetFontSize" => Action::ResetFontSize,
"Quit" => Action::Quit,
_ => return Err(E::invalid_value(Unexpected::Str(value), &self)),
}))
@ -1298,7 +1301,7 @@ impl DeserializeFromF32 for Size {
/// field in this struct. It might be nice in the future to have defaults for
/// each value independently. Alternatively, maybe erroring when the user
/// doesn't provide complete config is Ok.
#[derive(Debug, Deserialize)]
#[derive(Debug, Deserialize, Clone)]
pub struct Font {
/// Font family
pub normal: FontDescription,
@ -1311,7 +1314,7 @@ pub struct Font {
// Font size in points
#[serde(deserialize_with="DeserializeFromF32::deserialize_from_f32")]
size: Size,
pub size: Size,
/// Extra spacing per character
offset: Delta,
@ -1333,7 +1336,7 @@ fn default_italic_desc() -> FontDescription {
}
/// Description of a single font
#[derive(Debug, Deserialize)]
#[derive(Debug, Deserialize, Clone)]
pub struct FontDescription {
pub family: String,
pub style: Option<String>,
@ -1366,6 +1369,18 @@ impl Font {
pub fn glyph_offset(&self) -> &Delta {
&self.glyph_offset
}
/// Get a font clone with a size modification
pub fn with_size_delta(self, delta: f32) -> Font {
let mut new_size = self.size.as_f32_pts() + delta;
if new_size < 1.0 {
new_size = 1.0;
}
Font {
size : Size::new(new_size),
.. self
}
}
}
#[cfg(target_os = "macos")]

View File

@ -96,6 +96,7 @@ pub struct Display {
rx: mpsc::Receiver<(u32, u32)>,
tx: mpsc::Sender<(u32, u32)>,
meter: Meter,
font_size_modifier: i8,
size_info: SizeInfo,
last_background_color: Rgb,
}
@ -133,7 +134,6 @@ impl Display {
options: &cli::Options,
) -> Result<Display, Error> {
// Extract some properties from config
let font = config.font();
let render_timer = config.render_timer();
// Create the window where Alacritty will be displayed
@ -146,39 +146,17 @@ impl Display {
info!("device_pixel_ratio: {}", dpr);
let rasterizer = font::Rasterizer::new(dpr, config.use_thin_strokes())?;
// Create renderer
let mut renderer = QuadRenderer::new(&config, size)?;
// Initialize glyph cache
let glyph_cache = {
info!("Initializing glyph cache");
let init_start = ::std::time::Instant::now();
let cache = renderer.with_loader(|mut api| {
GlyphCache::new(rasterizer, config, &mut api)
})?;
let stop = init_start.elapsed();
let stop_f = stop.as_secs() as f64 + stop.subsec_nanos() as f64 / 1_000_000_000f64;
info!("Finished initializing glyph cache in {}", stop_f);
cache
};
// Need font metrics to resize the window properly. This suggests to me the
// font metrics should be computed before creating the window in the first
// place so that a resize is not needed.
let metrics = glyph_cache.font_metrics();
let cell_width = (metrics.average_advance + font.offset().x as f64) as u32;
let cell_height = (metrics.line_height + font.offset().y as f64) as u32;
let (glyph_cache, cell_width, cell_height) =
Self::new_glyph_cache(&window, &mut renderer, config, 0)?;
// Resize window to specified dimensions
let dimensions = options.dimensions()
.unwrap_or_else(|| config.dimensions());
let width = cell_width * dimensions.columns_u32();
let height = cell_height * dimensions.lines_u32();
let width = cell_width as u32 * dimensions.columns_u32();
let height = cell_height as u32 * dimensions.lines_u32();
let size = Size { width: Pixels(width), height: Pixels(height) };
info!("set_inner_size: {}", size);
@ -222,11 +200,56 @@ impl Display {
tx: tx,
rx: rx,
meter: Meter::new(),
font_size_modifier: 0,
size_info: size_info,
last_background_color: background_color,
})
}
fn new_glyph_cache(window : &Window, renderer : &mut QuadRenderer,
config: &Config, font_size_delta: i8)
-> Result<(GlyphCache, f32, f32), Error>
{
let font = config.font().clone().with_size_delta(font_size_delta as f32);
let dpr = window.hidpi_factor();
let rasterizer = font::Rasterizer::new(dpr, config.use_thin_strokes())?;
// Initialize glyph cache
let glyph_cache = {
info!("Initializing glyph cache");
let init_start = ::std::time::Instant::now();
let cache = renderer.with_loader(|mut api| {
GlyphCache::new(rasterizer, &font, &mut api)
})?;
let stop = init_start.elapsed();
let stop_f = stop.as_secs() as f64 + stop.subsec_nanos() as f64 / 1_000_000_000f64;
info!("Finished initializing glyph cache in {}", stop_f);
cache
};
// Need font metrics to resize the window properly. This suggests to me the
// font metrics should be computed before creating the window in the first
// place so that a resize is not needed.
let metrics = glyph_cache.font_metrics();
let cell_width = (metrics.average_advance + font.offset().x as f64) as u32;
let cell_height = (metrics.line_height + font.offset().y as f64) as u32;
return Ok((glyph_cache, cell_width as f32, cell_height as f32));
}
pub fn update_glyph_cache(&mut self, config: &Config, font_size_delta: i8)
{
let (glyph_cache, cell_width, cell_height) =
Self::new_glyph_cache(&self.window,
&mut self.renderer, config, font_size_delta).unwrap();
self.glyph_cache = glyph_cache;
self.size_info.cell_width = cell_width;
self.size_info.cell_height = cell_height;
}
#[inline]
pub fn resize_channel(&self) -> mpsc::Sender<(u32, u32)> {
self.tx.clone()
@ -240,6 +263,7 @@ impl Display {
pub fn handle_resize(
&mut self,
terminal: &mut MutexGuard<Term>,
config: &Config,
items: &mut [&mut OnResize]
) {
// Resize events new_size and are handled outside the poll_events
@ -252,11 +276,27 @@ impl Display {
new_size = Some(sz);
}
if terminal.font_size_modifier != self.font_size_modifier {
// Font size modification detected
self.font_size_modifier = terminal.font_size_modifier;
self.update_glyph_cache(config, terminal.font_size_modifier);
if new_size == None {
// Force a resize to refresh things
new_size = Some((self.size_info.width as u32,
self.size_info.height as u32));
}
}
// Receive any resize events; only call gl::Viewport on last
// available
if let Some((w, h)) = new_size.take() {
terminal.resize(w as f32, h as f32);
let size = terminal.size_info();
self.size_info.width = w as f32;
self.size_info.height = h as f32;
let size = &self.size_info;
terminal.resize(size);
for item in items {
item.on_resize(size)

View File

@ -107,6 +107,14 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> {
self.terminal.pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize)
}
fn change_font_size(&mut self, delta: i8) {
self.terminal.change_font_size(delta);
}
fn reset_font_size(&mut self) {
self.terminal.reset_font_size();
}
#[inline]
fn mouse_mut(&mut self) -> &mut Mouse {
self.mouse

View File

@ -62,6 +62,8 @@ pub trait ActionContext {
fn received_count(&mut self) -> &mut usize;
fn suppress_chars(&mut self) -> &mut bool;
fn last_modifiers(&mut self) -> &mut ModifiersState;
fn change_font_size(&mut self, delta: i8);
fn reset_font_size(&mut self);
}
/// Describes a state and action to take in that state
@ -154,6 +156,15 @@ pub enum Action {
/// Paste contents of selection buffer
PasteSelection,
/// Increase font size
IncreaseFontSize,
/// Decrease font size
DecreaseFontSize,
/// Reset font size to the config value
ResetFontSize,
/// Run given command
Command(String, Vec<String>),
@ -202,6 +213,15 @@ impl Action {
// FIXME should do a more graceful shutdown
::std::process::exit(0);
},
Action::IncreaseFontSize => {
ctx.change_font_size(1);
},
Action::DecreaseFontSize => {
ctx.change_font_size(-1);
}
Action::ResetFontSize => {
ctx.reset_font_size();
}
}
}
@ -593,6 +613,10 @@ mod tests {
fn last_modifiers(&mut self) -> &mut ModifiersState {
&mut self.last_modifiers
}
fn change_font_size(&mut self, _delta: i8) {
}
fn reset_font_size(&mut self) {
}
}
macro_rules! test_clickstate {

View File

@ -188,7 +188,7 @@ fn run(mut config: Config, options: cli::Options) -> Result<(), Box<Error>> {
//
// The second argument is a list of types that want to be notified
// of display size changes.
display.handle_resize(&mut terminal, &mut [&mut pty, &mut processor]);
display.handle_resize(&mut terminal, &config, &mut [&mut pty, &mut processor]);
// Draw the current state of the terminal
display.draw(terminal, &config, processor.selection.as_ref());

View File

@ -29,7 +29,7 @@ use gl;
use index::{Line, Column, RangeInclusive};
use notify::{Watcher, watcher, RecursiveMode, DebouncedEvent};
use config::{self, Config, Delta};
use config::{self, Config, Font, Delta};
use term::{self, cell, RenderableCell};
use window::{Size, Pixels};
@ -170,12 +170,11 @@ pub struct GlyphCache {
impl GlyphCache {
pub fn new<L>(
mut rasterizer: Rasterizer,
config: &Config,
font: &Font,
loader: &mut L
) -> Result<GlyphCache, font::Error>
where L: LoadGlyph
{
let font = config.font();
let size = font.size();
let glyph_offset = *font.glyph_offset();

View File

@ -655,6 +655,9 @@ pub struct Term {
/// Scroll region
scroll_region: Range<Line>,
/// Font size modifier
pub font_size_modifier: i8,
/// Size
size_info: SizeInfo,
@ -767,6 +770,7 @@ impl Term {
grid: grid,
alt_grid: alt,
alt: false,
font_size_modifier: 0,
active_charset: Default::default(),
cursor: Default::default(),
cursor_save: Default::default(),
@ -783,6 +787,18 @@ impl Term {
}
}
pub fn change_font_size(&mut self, delta: i8) {
if let Some(sum) = self.font_size_modifier.checked_add(delta) {
self.font_size_modifier = sum;
self.dirty = true;
}
}
pub fn reset_font_size(&mut self) {
self.font_size_modifier = 0;
self.dirty = true;
}
pub fn update_config(&mut self, config: &Config) {
self.semantic_escape_chars = config.selection().semantic_escape_chars.clone();
self.original_colors.fill_named(config.colors());
@ -965,30 +981,22 @@ impl Term {
}
/// Resize terminal to new dimensions
pub fn resize(&mut self, width: f32, height: f32) {
pub fn resize(&mut self, size : &SizeInfo) {
debug!("Term::resize");
// Bounds check; lots of math assumes width and height are > 0
if width as usize <= 2 * self.size_info.padding_x as usize ||
height as usize <= 2 * self.size_info.padding_y as usize
if size.width as usize <= 2 * self.size_info.padding_x as usize ||
size.height as usize <= 2 * self.size_info.padding_y as usize
{
return;
}
let size = SizeInfo {
width: width,
height: height,
cell_width: self.size_info.cell_width,
cell_height: self.size_info.cell_height,
padding_x: self.size_info.padding_x,
padding_y: self.size_info.padding_y,
};
let old_cols = self.grid.num_cols();
let old_lines = self.grid.num_lines();
let mut num_cols = size.cols();
let mut num_lines = size.lines();
self.size_info = size;
self.size_info = *size;
if old_cols == num_cols && old_lines == num_lines {
debug!("Term::resize dimensions unchanged");