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:
parent
5787bdff53
commit
3d81740a04
@ -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(())
|
||||
|
Loading…
Reference in New Issue
Block a user