1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-23 23:21:08 +03:00

wezterm: refactor close confirmations

Make pane, tab, window close confirmations use the same core function.

Make that function accept mouse input so that closing the window with
a mouse click doesn't require switching to the keyboard to confirm
the close.

refs: #280
This commit is contained in:
Wez Furlong 2020-12-23 11:37:23 -08:00
parent 5787bdff53
commit 3d81740a04

View File

@ -3,29 +3,83 @@ use mux::tab::TabId;
use mux::termwiztermtab::TermWizTerminal;
use mux::window::WindowId;
use mux::Mux;
use termwiz::cell::AttributeChange;
use termwiz::color::ColorAttribute;
use termwiz::input::{InputEvent, KeyCode, KeyEvent};
use termwiz::surface::{Change, Position};
use termwiz::input::{InputEvent, KeyCode, KeyEvent, MouseButtons, MouseEvent};
use termwiz::surface::{Change, CursorVisibility,Position};
use termwiz::terminal::Terminal;
pub fn confirm_close_pane(
pane_id: PaneId,
mut term: TermWizTerminal,
mux_window_id: WindowId,
) -> anyhow::Result<()> {
fn run_confirmation_app(message: &str, term: &mut TermWizTerminal) -> anyhow::Result<bool> {
term.set_raw_mode()?;
let changes = vec![
Change::ClearScreen(ColorAttribute::Default),
Change::CursorPosition {
x: Position::Absolute(0),
y: Position::Absolute(0),
},
Change::Text("Really kill this pane? [y/n]\r\n".to_string()),
];
let size = term.get_screen_size()?;
term.render(&changes)?;
term.flush()?;
// Render 80% wide, centered
let text_width = size.cols * 80 / 100;
let x_pos = size.cols * 10 / 100;
// Fit text to the width
let wrapped = textwrap::fill(message, text_width);
let message_rows = wrapped.split("\n").count();
// Now we want to vertically center the prompt in the view.
// After the prompt there will be a blank line and then the "buttons",
// so we add two to the number of rows.
let top_row = (size.rows - (message_rows + 2)) / 2;
let button_row = top_row + message_rows + 1;
let mut active = ActiveButton::None;
#[derive(Copy, Clone, PartialEq, Eq)]
enum ActiveButton {
None,
Yes,
No,
}
let render = |term: &mut TermWizTerminal, active: ActiveButton| -> anyhow::Result<()> {
let mut changes = vec![
Change::ClearScreen(ColorAttribute::Default),
Change::CursorVisibility(CursorVisibility::Hidden),
];
for (y, row) in wrapped.split("\n").enumerate() {
let row = row.trim_end();
changes.push(Change::CursorPosition {
x: Position::Absolute(x_pos),
y: Position::Absolute(top_row + y),
});
changes.push(Change::Text(row.to_string()));
}
changes.push(Change::CursorPosition {
x: Position::Absolute(x_pos),
y: Position::Absolute(button_row),
});
if active == ActiveButton::Yes {
changes.push(AttributeChange::Reverse(true).into());
}
changes.push(" Yes ".into());
if active == ActiveButton::Yes {
changes.push(AttributeChange::Reverse(false).into());
}
changes.push(" ".into());
if active == ActiveButton::No {
changes.push(AttributeChange::Reverse(true).into());
}
changes.push(" No ".into());
if active == ActiveButton::No {
changes.push(AttributeChange::Reverse(false).into());
}
term.render(&changes)?;
term.flush()
};
render(term, active)?;
while let Ok(Some(event)) = term.poll_input(None) {
match event {
@ -33,16 +87,7 @@ pub fn confirm_close_pane(
key: KeyCode::Char('y'),
..
}) => {
promise::spawn::spawn_into_main_thread(async move {
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(mux_window_id) {
Some(tab) => tab,
None => return,
};
tab.kill_pane(pane_id);
})
.detach();
break;
return Ok(true);
}
InputEvent::Key(KeyEvent {
key: KeyCode::Char('n'),
@ -52,10 +97,59 @@ pub fn confirm_close_pane(
key: KeyCode::Escape,
..
}) => {
break;
return Ok(false);
}
InputEvent::Mouse(MouseEvent {
x,
y,
mouse_buttons,
..
}) => {
let x = x as usize;
let y = y as usize;
if y == button_row && x >= x_pos && x <= x_pos + 5 {
active = ActiveButton::Yes;
if mouse_buttons == MouseButtons::LEFT {
return Ok(true);
}
} else if y == button_row && x >= x_pos + 14 && x <= x_pos + 20 {
active = ActiveButton::No;
if mouse_buttons == MouseButtons::LEFT {
return Ok(false);
}
} else {
active = ActiveButton::None;
}
if mouse_buttons != MouseButtons::NONE {
// Treat any other mouse button as cancel
return Ok(false);
}
}
_ => {}
}
render(term, active)?;
}
Ok(false)
}
pub fn confirm_close_pane(
pane_id: PaneId,
mut term: TermWizTerminal,
mux_window_id: WindowId,
) -> anyhow::Result<()> {
if run_confirmation_app("🛑 Really kill this pane?", &mut term)? {
promise::spawn::spawn_into_main_thread(async move {
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(mux_window_id) {
Some(tab) => tab,
None => return,
};
tab.kill_pane(pane_id);
})
.detach();
}
Ok(())
@ -66,45 +160,15 @@ pub fn confirm_close_tab(
mut term: TermWizTerminal,
_mux_window_id: WindowId,
) -> anyhow::Result<()> {
term.set_raw_mode()?;
let changes = vec![
Change::ClearScreen(ColorAttribute::Default),
Change::CursorPosition {
x: Position::Absolute(0),
y: Position::Absolute(0),
},
Change::Text("Really kill this tab and all contained panes? [y/n]\r\n".to_string()),
];
term.render(&changes)?;
term.flush()?;
while let Ok(Some(event)) = term.poll_input(None) {
match event {
InputEvent::Key(KeyEvent {
key: KeyCode::Char('y'),
..
}) => {
promise::spawn::spawn_into_main_thread(async move {
let mux = Mux::get().unwrap();
mux.remove_tab(tab_id);
})
.detach();
break;
}
InputEvent::Key(KeyEvent {
key: KeyCode::Char('n'),
..
})
| InputEvent::Key(KeyEvent {
key: KeyCode::Escape,
..
}) => {
break;
}
_ => {}
}
if run_confirmation_app(
"🛑 Really kill this tab and all contained panes?",
&mut term,
)? {
promise::spawn::spawn_into_main_thread(async move {
let mux = Mux::get().unwrap();
mux.remove_tab(tab_id);
})
.detach();
}
Ok(())
@ -114,47 +178,15 @@ pub fn confirm_close_window(
mut term: TermWizTerminal,
mux_window_id: WindowId,
) -> anyhow::Result<()> {
term.set_raw_mode()?;
let changes = vec![
Change::ClearScreen(ColorAttribute::Default),
Change::CursorPosition {
x: Position::Absolute(0),
y: Position::Absolute(0),
},
Change::Text(
"Really kill this window and all contained tabs and panes? [y/n]\r\n".to_string(),
),
];
term.render(&changes)?;
term.flush()?;
while let Ok(Some(event)) = term.poll_input(None) {
match event {
InputEvent::Key(KeyEvent {
key: KeyCode::Char('y'),
..
}) => {
promise::spawn::spawn_into_main_thread(async move {
let mux = Mux::get().unwrap();
mux.kill_window(mux_window_id);
})
.detach();
break;
}
InputEvent::Key(KeyEvent {
key: KeyCode::Char('n'),
..
})
| InputEvent::Key(KeyEvent {
key: KeyCode::Escape,
..
}) => {
break;
}
_ => {}
}
if run_confirmation_app(
"🛑 Really kill this window and all contained tabs and panes?",
&mut term,
)? {
promise::spawn::spawn_into_main_thread(async move {
let mux = Mux::get().unwrap();
mux.kill_window(mux_window_id);
})
.detach();
}
Ok(())