Fix live config reload for window title

This enables live config reload for the window title. This includes
updating the title after it has been pushed and popped from the title
stack.

The dynamic title option also isn't disabled automatically anymore when
the title is set in the config. If the title is set from CLI, the
behavior is unchanged and dynamic title changes are still disabled.

If the dynamic title is disabled in the config, the title is still
updated when the config title is changed. Dynamic title now only
prevents changes to the UI's title.
This commit is contained in:
Christian Duerr 2020-03-14 15:09:10 +00:00 committed by GitHub
parent d20051b5e5
commit ead8d68c69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 84 deletions

View File

@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Capitalized the default Alacritty.desktop file
- Capitalized the Alacritty.desktop file
### Added
- Live config reload for `window.title`
### Changed
- Pressing additional modifiers for mouse bindings will no longer trigger them
@ -22,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The default value for `draw_bold_text_with_bright_colors` is now `false`
- Mirror OSC query terminators instead of always using BEL
- Increased Beam, Underline, and Hollow Block cursors' line widths
- Dynamic title is not disabled anymore when `window.title` is set in config
### Fixed

View File

@ -272,6 +272,7 @@ impl Options {
config.hold = self.hold;
config.set_dynamic_title(config.dynamic_title() && self.title.is_none());
config.window.dimensions = self.dimensions.unwrap_or(config.window.dimensions);
config.window.title = self.title.unwrap_or(config.window.title);
config.window.position = self.position.or(config.window.position);
@ -285,8 +286,6 @@ impl Options {
}
}
config.set_dynamic_title(config.dynamic_title() && config.window.title == DEFAULT_NAME);
config.debug.print_events = self.print_events || config.debug.print_events;
config.debug.log_level = max(config.debug.log_level, self.log_level);
config.debug.ref_test = self.ref_test || config.debug.ref_test;
@ -328,12 +327,12 @@ mod tests {
}
#[test]
fn dynamic_title_overridden_by_config() {
fn dynamic_title_not_overridden_by_config() {
let mut config = Config::default();
config.window.title = "foo".to_owned();
let config = Options::default().into_config(config);
assert!(!config.dynamic_title());
assert!(config.dynamic_title());
}
}

View File

@ -154,7 +154,7 @@ pub trait TermInfo {
/// writing specific handler impls for tests far easier.
pub trait Handler {
/// OSC to set window title
fn set_title(&mut self, _: &str) {}
fn set_title(&mut self, _: Option<String>) {}
/// Set the cursor style
fn set_cursor_style(&mut self, _: Option<CursorStyle>) {}
@ -771,8 +771,10 @@ where
.iter()
.flat_map(|x| str::from_utf8(x))
.collect::<Vec<&str>>()
.join(";");
self.handler.set_title(&title);
.join(";")
.trim()
.to_owned();
self.handler.set_title(Some(title));
return;
}
unhandled(params);

View File

@ -26,7 +26,7 @@ use crate::ansi::{
self, Attr, CharsetIndex, Color, CursorStyle, Handler, NamedColor, StandardCharset, TermInfo,
};
use crate::clipboard::{Clipboard, ClipboardType};
use crate::config::{Config, VisualBellAnimation, DEFAULT_NAME};
use crate::config::{Config, VisualBellAnimation};
use crate::event::{Event, EventListener};
use crate::grid::{
BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll,
@ -35,8 +35,6 @@ use crate::index::{self, Column, IndexRange, Line, Point};
use crate::selection::{Selection, SelectionRange};
use crate::term::cell::{Cell, Flags, LineLength};
use crate::term::color::Rgb;
#[cfg(windows)]
use crate::tty;
pub mod cell;
pub mod color;
@ -797,21 +795,24 @@ pub struct Term<T> {
/// Default style for resetting the cursor
default_cursor_style: CursorStyle,
/// Whether to permit updating the terminal title
dynamic_title: bool,
/// Clipboard access coupled to the active window
clipboard: Clipboard,
/// Proxy for sending events to the event loop
event_proxy: T,
/// Current title of the window
title: String,
/// Current title of the window.
title: Option<String>,
/// Default title for resetting it.
default_title: String,
/// Whether to permit updating the terminal title.
dynamic_title: bool,
/// Stack of saved window titles. When a title is popped from this stack, the `title` for the
/// term is set, and the Glutin window's title attribute is changed through the event listener.
title_stack: Vec<String>,
title_stack: Vec<Option<String>>,
}
/// Terminal size info
@ -934,12 +935,16 @@ impl<T> Term<T> {
clipboard,
event_proxy,
is_focused: true,
title: config.window.title.clone(),
title: None,
default_title: config.window.title.clone(),
title_stack: Vec::new(),
}
}
pub fn update_config<C>(&mut self, config: &Config<C>) {
pub fn update_config<C>(&mut self, config: &Config<C>)
where
T: EventListener,
{
self.semantic_escape_chars = config.selection.semantic_escape_chars().to_owned();
self.original_colors.fill_named(&config.colors);
self.original_colors.fill_cube(&config.colors);
@ -954,8 +959,16 @@ impl<T> Term<T> {
self.mode.remove(TermMode::ALTERNATE_SCROLL);
}
self.default_cursor_style = config.cursor.style;
self.default_title = config.window.title.clone();
self.dynamic_title = config.dynamic_title();
if self.dynamic_title {
self.set_title(self.title.clone());
} else {
self.event_proxy.send_event(Event::Title(self.default_title.clone()));
}
if self.alt {
self.alt_grid.update_history(config.scrolling.history() as usize);
} else {
@ -1299,40 +1312,6 @@ impl<T> TermInfo for Term<T> {
}
impl<T: EventListener> Handler for Term<T> {
#[inline]
#[cfg(not(windows))]
fn set_title(&mut self, title: &str) {
if self.dynamic_title {
trace!("Setting window title to '{}'", title);
self.title = title.into();
self.event_proxy.send_event(Event::Title(title.to_owned()));
}
}
#[inline]
#[cfg(windows)]
fn set_title(&mut self, title: &str) {
if self.dynamic_title {
// cmd.exe in winpty: winpty incorrectly sets the title to ' ' instead of
// 'Alacritty' - thus we have to substitute this back to get equivalent
// behaviour as conpty.
//
// The starts_with check is necessary because other shells e.g. bash set a
// different title and don't need Alacritty prepended.
trace!("Setting window title to '{}'", title);
let title = if !tty::is_conpty() && title.starts_with(' ') {
format!("Alacritty {}", title.trim())
} else {
title.to_owned()
};
self.title = title.clone();
self.event_proxy.send_event(Event::Title(title));
}
}
/// A character to be displayed
#[inline]
fn input(&mut self, c: char) {
@ -1916,8 +1895,8 @@ impl<T: EventListener> Handler for Term<T> {
self.grid.reset(&Cell::default());
self.alt_grid.reset(&Cell::default());
self.scroll_region = Line(0)..self.grid.num_lines();
self.title = DEFAULT_NAME.to_string();
self.title_stack.clear();
self.title_stack = Vec::new();
self.title = None;
}
#[inline]
@ -2109,14 +2088,26 @@ impl<T: EventListener> Handler for Term<T> {
self.cursor_style = style;
}
#[inline]
fn set_title(&mut self, title: Option<String>) {
trace!("Setting title to '{:?}'", title);
self.title = title.clone();
if self.dynamic_title {
let title = title.unwrap_or_else(|| self.default_title.clone());
self.event_proxy.send_event(Event::Title(title));
}
}
#[inline]
fn push_title(&mut self) {
trace!("Pushing '{}' onto title stack", self.title);
trace!("Pushing '{:?}' onto title stack", self.title);
if self.title_stack.len() >= TITLE_STACK_MAX_DEPTH {
let removed = self.title_stack.remove(0);
trace!(
"Removing '{}' from bottom of title stack that exceeds its maximum depth",
"Removing '{:?}' from bottom of title stack that exceeds its maximum depth",
removed
);
}
@ -2129,8 +2120,8 @@ impl<T: EventListener> Handler for Term<T> {
trace!("Attempting to pop title from stack...");
if let Some(popped) = self.title_stack.pop() {
trace!("Title '{}' popped from stack", popped);
self.set_title(&popped);
trace!("Title '{:?}' popped from stack", popped);
self.set_title(popped);
}
}
}
@ -2377,42 +2368,47 @@ mod tests {
};
let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
// Title None by default
assert_eq!(term.title, None);
// Title can be set
{
term.title = "Test".to_string();
assert_eq!(term.title, "Test");
}
term.set_title(Some("Test".into()));
assert_eq!(term.title, Some("Test".into()));
// Title can be pushed onto stack
{
term.push_title();
term.title = "Next".to_string();
assert_eq!(term.title, "Next");
assert_eq!(term.title_stack.get(0).unwrap(), "Test");
}
term.push_title();
term.set_title(Some("Next".into()));
assert_eq!(term.title, Some("Next".into()));
assert_eq!(term.title_stack.get(0).unwrap(), &Some("Test".into()));
// Title can be popped from stack and set as the window title
{
term.pop_title();
assert_eq!(term.title, "Test");
assert!(term.title_stack.is_empty());
}
term.pop_title();
assert_eq!(term.title, Some("Test".into()));
assert!(term.title_stack.is_empty());
// Title stack doesn't grow infinitely
{
for _ in 0..4097 {
term.push_title();
}
assert_eq!(term.title_stack.len(), 4096);
for _ in 0..4097 {
term.push_title();
}
assert_eq!(term.title_stack.len(), 4096);
// Title and title stack reset when terminal state is reset
{
term.push_title();
term.reset_state();
assert_eq!(term.title, "Alacritty");
assert!(term.title_stack.is_empty());
}
term.push_title();
term.reset_state();
assert_eq!(term.title, None);
assert!(term.title_stack.is_empty());
// Title stack pops back to default
term.title = None;
term.push_title();
term.set_title(Some("Test".into()));
term.pop_title();
assert_eq!(term.title, None);
// Title can be reset to default
term.title = Some("Test".into());
term.set_title(None);
assert_eq!(term.title, None);
}
}