ui: add window title config option

Use xterm window title escape sequences to set window title when
launched and restoring the previous one when exiting. If option is
blank, no title setting occurs.
embed
Manos Pitsidianakis 2019-10-15 22:55:53 +03:00
parent ccc58860e6
commit 205ebe2f1c
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
4 changed files with 102 additions and 89 deletions

View File

@ -19,6 +19,8 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use super::deserializers::non_empty_string;
/// Settings for terminal display
#[derive(Debug, Deserialize, Clone, Serialize)]
#[serde(default)]
@ -26,6 +28,8 @@ pub struct TerminalSettings {
/// light, dark
pub theme: String,
pub ascii_drawing: bool,
#[serde(deserialize_with = "non_empty_string")]
pub window_title: Option<String>,
}
impl Default for TerminalSettings {
@ -33,6 +37,7 @@ impl Default for TerminalSettings {
TerminalSettings {
theme: "dark".to_string(),
ascii_drawing: false,
window_title: Some("meli".to_string()),
}
}
}

View File

@ -39,7 +39,7 @@ use std::result;
use std::thread;
use termion::raw::IntoRawMode;
use termion::screen::AlternateScreen;
use termion::{clear, cursor, style};
use termion::{clear, cursor};
pub type StateStdout = termion::screen::AlternateScreen<termion::raw::RawTerminal<std::io::Stdout>>;
@ -142,17 +142,7 @@ pub struct State {
impl Drop for State {
fn drop(&mut self) {
// When done, restore the defaults to avoid messing with the terminal.
write!(
self.stdout(),
"{}{}{}{}{}",
clear::All,
style::Reset,
cursor::Goto(1, 1),
cursor::Show,
BracketModeEnd,
)
.unwrap();
self.flush();
self.switch_to_main_screen();
}
}
@ -202,15 +192,11 @@ impl State {
.collect();
accounts.sort_by(|a, b| a.name().cmp(&b.name()));
let _stdout = std::io::stdout();
_stdout.lock();
let stdout = AlternateScreen::from(_stdout.into_raw_mode().unwrap());
let mut s = State {
cols,
rows,
grid: CellBuffer::new(cols, rows, Cell::with_char(' ')),
stdout: Some(stdout),
stdout: None,
child: None,
mode: UIMode::Normal,
components: Vec::with_capacity(1),
@ -249,16 +235,7 @@ impl State {
}
}
write!(
s.stdout(),
"{}{}{}{}",
BracketModeStart,
cursor::Hide,
clear::All,
cursor::Goto(1, 1)
)
.unwrap();
s.flush();
s.switch_to_alternate_screen();
debug!("inserting mailbox hashes:");
{
/* Account::watch() needs
@ -285,7 +262,7 @@ impl State {
account.watch((work_controller, sender, replies));
}
}
s.restore_input();
s.context.restore_input();
s
}
@ -338,29 +315,41 @@ impl State {
pub fn switch_to_main_screen(&mut self) {
write!(
self.stdout(),
"{}{}",
"{}{}{}{}",
termion::screen::ToMainScreen,
cursor::Show
cursor::Show,
RestoreWindowTitleIconFromStack,
BracketModeEnd,
)
.unwrap();
self.flush();
self.stdout = None;
self.context.input.kill();
}
pub fn switch_to_alternate_screen(&mut self) {
let s = std::io::stdout();
s.lock();
self.stdout = Some(AlternateScreen::from(s.into_raw_mode().unwrap()));
let mut stdout = AlternateScreen::from(s.into_raw_mode().unwrap());
write!(
self.stdout(),
"{}{}{}{}",
&mut stdout,
"{save_title_to_stack}{}{}{}{window_title}{}{}",
termion::screen::ToAlternateScreen,
cursor::Hide,
clear::All,
cursor::Goto(1, 1)
cursor::Goto(1, 1),
BracketModeStart,
save_title_to_stack = SaveWindowTitleIconToStack,
window_title = if let Some(ref title) = self.context.settings.terminal.window_title {
format!("\x1b]2;{}\x07", title)
} else {
String::new()
},
)
.unwrap();
self.stdout = Some(stdout);
self.flush();
}

View File

@ -23,7 +23,6 @@ use self::serde::de::Visitor;
use self::serde::{de, Deserialize, Deserializer};
extern crate unicode_segmentation;
#[macro_use]
mod position;
#[macro_use]
@ -35,3 +34,76 @@ pub use self::cells::*;
pub use self::keys::*;
pub use self::position::*;
pub use self::text_editing::*;
use std::fmt;
/*
* CSI events we use
*/
// Some macros taken from termion:
/// Create a CSI-introduced sequence.
macro_rules! csi {
($( $l:expr ),*) => { concat!("\x1b[", $( $l ),*) };
}
/// Derive a CSI sequence struct.
macro_rules! derive_csi_sequence {
($(#[$outer:meta])*
($name:ident, $value:expr)) => {
$(#[$outer])*
#[derive(Copy, Clone)]
pub struct $name;
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, csi!($value))
}
}
impl AsRef<[u8]> for $name {
fn as_ref(&self) -> &'static [u8] {
csi!($value).as_bytes()
}
}
impl AsRef<str> for $name {
fn as_ref(&self) -> &'static str {
csi!($value)
}
}
};
}
/*
derive_csi_sequence!(
#[doc = ""]
(DisableMouse, "?1000l")
);
derive_csi_sequence!(
#[doc = ""]
(EnableMouse, "?1000h")
);
*/
derive_csi_sequence!(
#[doc = "`CSI Ps ; Ps ; Ps t`, where `Ps = 2 2 ; 0` -> Save xterm icon and window title on stack."]
(SaveWindowTitleIconToStack, "22;0t")
);
derive_csi_sequence!(
#[doc = "Restore window title and icon from terminal's title stack. `CSI Ps ; Ps ; Ps t`, where `Ps = 2 3 ; 0` -> Restore xterm icon and window title from stack."]
(RestoreWindowTitleIconFromStack, "23;0t")
);
derive_csi_sequence!(
#[doc = "Empty struct with a Display implementation that returns the byte sequence to start [Bracketed Paste Mode](http://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode)"]
(BracketModeStart, "?2004h")
);
derive_csi_sequence!(
#[doc = "Empty struct with a Display implementation that returns the byte sequence to end [Bracketed Paste Mode](http://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode)"]
(BracketModeEnd, "?2003l")
);
pub const BRACKET_PASTE_START: &[u8] = b"\x1B[200~";
pub const BRACKET_PASTE_END: &[u8] = b"\x1B[201~";

View File

@ -22,7 +22,6 @@
use super::*;
use crossbeam::{channel::Receiver, select};
use serde::{Serialize, Serializer};
use std::fmt;
use std::io;
use termion::event::Event as TermionEvent;
use termion::event::Key as TermionKey;
@ -191,58 +190,6 @@ pub fn get_events(
}
}
}
/*
* CSI events we use
*/
// Some macros taken from termion:
/// Create a CSI-introduced sequence.
macro_rules! csi {
($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) };
}
/// Derive a CSI sequence struct.
macro_rules! derive_csi_sequence {
($(#[$outer:meta])*
($name:ident, $value:expr)) => {
$(#[$outer])*
#[derive(Copy, Clone)]
pub struct $name;
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, csi!($value))
}
}
impl AsRef<[u8]> for $name {
fn as_ref(&self) -> &'static [u8] {
csi!($value).as_bytes()
}
}
impl AsRef<str> for $name {
fn as_ref(&self) -> &'static str {
csi!($value)
}
}
};
}
derive_csi_sequence!(
#[doc = "Empty struct with a Display implementation that returns the byte sequence to start [Bracketed Paste Mode](http://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode)"]
(BracketModeStart, "?2004h")
);
derive_csi_sequence!(
#[doc = "Empty struct with a Display implementation that returns the byte sequence to end [Bracketed Paste Mode](http://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode)"]
(BracketModeEnd, "?2003l")
);
pub const BRACKET_PASTE_START: &[u8] = b"\x1B[200~";
pub const BRACKET_PASTE_END: &[u8] = b"\x1B[201~";
const FIELDS: &[&str] = &[];
impl<'de> Deserialize<'de> for Key {