First attempt to provide an overlay prompt (#871)
This commit is contained in:
parent
55aef0aeec
commit
b861baa6a1
11 changed files with 217 additions and 5 deletions
|
|
@ -339,7 +339,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
|
||||||
colored_elements,
|
colored_elements,
|
||||||
separator,
|
separator,
|
||||||
),
|
),
|
||||||
InputMode::Normal => key_indicators(
|
InputMode::Normal | InputMode::Prompt => key_indicators(
|
||||||
max_len,
|
max_len,
|
||||||
&[
|
&[
|
||||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock),
|
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock),
|
||||||
|
|
|
||||||
|
|
@ -49,11 +49,11 @@ use zellij_utils::{
|
||||||
setup::get_default_data_dir,
|
setup::get_default_data_dir,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) type ClientId = u16;
|
pub type ClientId = u16;
|
||||||
|
|
||||||
/// Instructions related to server-side application
|
/// Instructions related to server-side application
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) enum ServerInstruction {
|
pub enum ServerInstruction {
|
||||||
NewClient(
|
NewClient(
|
||||||
ClientAttributes,
|
ClientAttributes,
|
||||||
Box<CliArgs>,
|
Box<CliArgs>,
|
||||||
|
|
|
||||||
|
|
@ -335,6 +335,28 @@ fn route_action(
|
||||||
.send_to_screen(ScreenInstruction::Copy(client_id))
|
.send_to_screen(ScreenInstruction::Copy(client_id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
Action::Confirm => {
|
||||||
|
session
|
||||||
|
.senders
|
||||||
|
.send_to_screen(ScreenInstruction::ConfirmPrompt(client_id))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Action::Deny => {
|
||||||
|
session
|
||||||
|
.senders
|
||||||
|
.send_to_screen(ScreenInstruction::DenyPrompt(client_id))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
#[allow(clippy::single_match)]
|
||||||
|
Action::SkipConfirm(action) => match *action {
|
||||||
|
Action::Quit => {
|
||||||
|
to_server
|
||||||
|
.send(ServerInstruction::ClientExit(client_id))
|
||||||
|
.unwrap();
|
||||||
|
should_break = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
Action::NoOp => {}
|
Action::NoOp => {}
|
||||||
}
|
}
|
||||||
should_break
|
should_break
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ use crate::{
|
||||||
pty::{ClientOrTabIndex, PtyInstruction, VteBytes},
|
pty::{ClientOrTabIndex, PtyInstruction, VteBytes},
|
||||||
tab::{Output, Tab},
|
tab::{Output, Tab},
|
||||||
thread_bus::Bus,
|
thread_bus::Bus,
|
||||||
|
ui::overlay::{Overlay, OverlayWindow},
|
||||||
wasm_vm::PluginInstruction,
|
wasm_vm::PluginInstruction,
|
||||||
ClientId, ServerInstruction,
|
ClientId, ServerInstruction,
|
||||||
};
|
};
|
||||||
|
|
@ -24,7 +25,7 @@ use zellij_utils::{
|
||||||
|
|
||||||
/// Instructions that can be sent to the [`Screen`].
|
/// Instructions that can be sent to the [`Screen`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) enum ScreenInstruction {
|
pub enum ScreenInstruction {
|
||||||
PtyBytes(RawFd, VteBytes),
|
PtyBytes(RawFd, VteBytes),
|
||||||
Render,
|
Render,
|
||||||
NewPane(PaneId, ClientOrTabIndex),
|
NewPane(PaneId, ClientOrTabIndex),
|
||||||
|
|
@ -84,6 +85,10 @@ pub(crate) enum ScreenInstruction {
|
||||||
Copy(ClientId),
|
Copy(ClientId),
|
||||||
AddClient(ClientId),
|
AddClient(ClientId),
|
||||||
RemoveClient(ClientId),
|
RemoveClient(ClientId),
|
||||||
|
AddOverlay(Overlay, ClientId),
|
||||||
|
RemoveOverlay(ClientId),
|
||||||
|
ConfirmPrompt(ClientId),
|
||||||
|
DenyPrompt(ClientId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&ScreenInstruction> for ScreenContext {
|
impl From<&ScreenInstruction> for ScreenContext {
|
||||||
|
|
@ -154,6 +159,10 @@ impl From<&ScreenInstruction> for ScreenContext {
|
||||||
ScreenInstruction::ToggleTab(..) => ScreenContext::ToggleTab,
|
ScreenInstruction::ToggleTab(..) => ScreenContext::ToggleTab,
|
||||||
ScreenInstruction::AddClient(..) => ScreenContext::AddClient,
|
ScreenInstruction::AddClient(..) => ScreenContext::AddClient,
|
||||||
ScreenInstruction::RemoveClient(..) => ScreenContext::RemoveClient,
|
ScreenInstruction::RemoveClient(..) => ScreenContext::RemoveClient,
|
||||||
|
ScreenInstruction::AddOverlay(..) => ScreenContext::AddOverlay,
|
||||||
|
ScreenInstruction::RemoveOverlay(..) => ScreenContext::RemoveOverlay,
|
||||||
|
ScreenInstruction::ConfirmPrompt(..) => ScreenContext::ConfirmPrompt,
|
||||||
|
ScreenInstruction::DenyPrompt(..) => ScreenContext::DenyPrompt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -169,6 +178,8 @@ pub(crate) struct Screen {
|
||||||
tabs: BTreeMap<usize, Tab>,
|
tabs: BTreeMap<usize, Tab>,
|
||||||
/// The full size of this [`Screen`].
|
/// The full size of this [`Screen`].
|
||||||
size: Size,
|
size: Size,
|
||||||
|
/// The overlay that is drawn on top of [`Pane`]'s', [`Tab`]'s and the [`Screen`]
|
||||||
|
_overlay: OverlayWindow,
|
||||||
/// The indices of this [`Screen`]'s active [`Tab`]s.
|
/// The indices of this [`Screen`]'s active [`Tab`]s.
|
||||||
active_tab_indices: BTreeMap<ClientId, usize>,
|
active_tab_indices: BTreeMap<ClientId, usize>,
|
||||||
tab_history: BTreeMap<ClientId, Vec<usize>>,
|
tab_history: BTreeMap<ClientId, Vec<usize>>,
|
||||||
|
|
@ -193,6 +204,7 @@ impl Screen {
|
||||||
colors: client_attributes.palette,
|
colors: client_attributes.palette,
|
||||||
active_tab_indices: BTreeMap::new(),
|
active_tab_indices: BTreeMap::new(),
|
||||||
tabs: BTreeMap::new(),
|
tabs: BTreeMap::new(),
|
||||||
|
_overlay: OverlayWindow::default(),
|
||||||
tab_history: BTreeMap::new(),
|
tab_history: BTreeMap::new(),
|
||||||
mode_info,
|
mode_info,
|
||||||
draw_pane_frames,
|
draw_pane_frames,
|
||||||
|
|
@ -1088,6 +1100,10 @@ pub(crate) fn screen_thread_main(
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
ScreenInstruction::AddOverlay(_, _) => {}
|
||||||
|
ScreenInstruction::RemoveOverlay(_) => {}
|
||||||
|
ScreenInstruction::ConfirmPrompt(_) => {}
|
||||||
|
ScreenInstruction::DenyPrompt(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod boundaries;
|
pub mod boundaries;
|
||||||
|
pub mod overlay;
|
||||||
pub mod pane_boundaries_frame;
|
pub mod pane_boundaries_frame;
|
||||||
pub mod pane_resizer;
|
pub mod pane_resizer;
|
||||||
|
|
|
||||||
104
zellij-server/src/ui/overlay/mod.rs
Normal file
104
zellij-server/src/ui/overlay/mod.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
//! This module handles the overlay's over the [`Screen`]
|
||||||
|
//!
|
||||||
|
//! They consist of:
|
||||||
|
//!
|
||||||
|
//! prompt's:
|
||||||
|
//!
|
||||||
|
//! notification's:
|
||||||
|
|
||||||
|
pub mod prompt;
|
||||||
|
|
||||||
|
use crate::ServerInstruction;
|
||||||
|
use zellij_utils::pane_size::Size;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Overlay {
|
||||||
|
pub overlay_type: OverlayType,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Overlayable {
|
||||||
|
/// Generates vte_output that can be passed into
|
||||||
|
/// the `render()` function
|
||||||
|
fn generate_overlay(&self, size: Size) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Padding {
|
||||||
|
rows: usize,
|
||||||
|
cols: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum OverlayType {
|
||||||
|
Prompt(prompt::Prompt),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Overlayable for OverlayType {
|
||||||
|
fn generate_overlay(&self, size: Size) -> String {
|
||||||
|
match &self {
|
||||||
|
OverlayType::Prompt(prompt) => prompt.generate_overlay(size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Entrypoint from [`Screen`], which holds the context in which
|
||||||
|
/// the overlays are being rendered.
|
||||||
|
/// The most recent overlays draw over the previous overlays.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct OverlayWindow {
|
||||||
|
pub overlay_stack: Vec<Overlay>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OverlayWindow {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
overlay_stack: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Overlayable for OverlayWindow {
|
||||||
|
fn generate_overlay(&self, size: Size) -> String {
|
||||||
|
let mut output = String::new();
|
||||||
|
//let clear_display = "\u{1b}[2J";
|
||||||
|
//output.push_str(&clear_display);
|
||||||
|
for overlay in &self.overlay_stack {
|
||||||
|
let vte_output = overlay.generate_overlay(size);
|
||||||
|
output.push_str(&vte_output);
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Overlay {
|
||||||
|
pub fn prompt_confirm(self) -> Option<Box<ServerInstruction>> {
|
||||||
|
match self.overlay_type {
|
||||||
|
OverlayType::Prompt(p) => p.confirm(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn prompt_deny(self) -> Option<Box<ServerInstruction>> {
|
||||||
|
match self.overlay_type {
|
||||||
|
OverlayType::Prompt(p) => p.deny(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Overlayable for Overlay {
|
||||||
|
fn generate_overlay(&self, size: Size) -> String {
|
||||||
|
self.overlay_type.generate_overlay(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Overlay {
|
||||||
|
pub fn new(overlay_type: OverlayType) -> Self {
|
||||||
|
Self { overlay_type }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_cols(output: &mut String, cols: usize) {
|
||||||
|
if let Some(padding) = cols.checked_sub(output.len()) {
|
||||||
|
for _ in 0..padding {
|
||||||
|
output.push(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
zellij-server/src/ui/overlay/prompt.rs
Normal file
55
zellij-server/src/ui/overlay/prompt.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
use zellij_utils::pane_size::Size;
|
||||||
|
|
||||||
|
use super::{Overlay, OverlayType, Overlayable};
|
||||||
|
use crate::{ClientId, ServerInstruction};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Prompt {
|
||||||
|
pub message: String,
|
||||||
|
on_confirm: Option<Box<ServerInstruction>>,
|
||||||
|
on_deny: Option<Box<ServerInstruction>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Prompt {
|
||||||
|
pub fn new(
|
||||||
|
message: String,
|
||||||
|
on_confirm: Option<Box<ServerInstruction>>,
|
||||||
|
on_deny: Option<Box<ServerInstruction>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
message,
|
||||||
|
on_confirm,
|
||||||
|
on_deny,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn confirm(self) -> Option<Box<ServerInstruction>> {
|
||||||
|
self.on_confirm
|
||||||
|
}
|
||||||
|
pub fn deny(self) -> Option<Box<ServerInstruction>> {
|
||||||
|
self.on_deny
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Overlayable for Prompt {
|
||||||
|
fn generate_overlay(&self, size: Size) -> String {
|
||||||
|
let mut output = String::new();
|
||||||
|
let rows = size.rows;
|
||||||
|
let mut vte_output = self.message.clone();
|
||||||
|
Overlay::pad_cols(&mut vte_output, size.cols);
|
||||||
|
for (x, h) in vte_output.chars().enumerate() {
|
||||||
|
output.push_str(&format!("\u{1b}[{};{}H\u{1b}[48;5;238m{}", rows, x + 1, h,));
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _generate_quit_prompt(client_id: ClientId) -> Overlay {
|
||||||
|
let prompt = Prompt::new(
|
||||||
|
(" Do you want to quit zellij? [Y]es / [N]o").to_string(),
|
||||||
|
Some(Box::new(ServerInstruction::ClientExit(client_id))),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
Overlay {
|
||||||
|
overlay_type: OverlayType::Prompt(prompt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -85,6 +85,9 @@ pub enum InputMode {
|
||||||
/// `Move` mode allows moving the different existing panes within a tab
|
/// `Move` mode allows moving the different existing panes within a tab
|
||||||
#[serde(alias = "move")]
|
#[serde(alias = "move")]
|
||||||
Move,
|
Move,
|
||||||
|
/// `Prompt` mode allows interacting with active prompts.
|
||||||
|
#[serde(alias = "prompt")]
|
||||||
|
Prompt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for InputMode {
|
impl Default for InputMode {
|
||||||
|
|
@ -129,6 +132,7 @@ impl FromStr for InputMode {
|
||||||
"renametab" => Ok(InputMode::RenameTab),
|
"renametab" => Ok(InputMode::RenameTab),
|
||||||
"session" => Ok(InputMode::Session),
|
"session" => Ok(InputMode::Session),
|
||||||
"move" => Ok(InputMode::Move),
|
"move" => Ok(InputMode::Move),
|
||||||
|
"prompt" => Ok(InputMode::Prompt),
|
||||||
e => Err(e.to_string().into()),
|
e => Err(e.to_string().into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -267,6 +267,10 @@ pub enum ScreenContext {
|
||||||
ToggleTab,
|
ToggleTab,
|
||||||
AddClient,
|
AddClient,
|
||||||
RemoveClient,
|
RemoveClient,
|
||||||
|
AddOverlay,
|
||||||
|
RemoveOverlay,
|
||||||
|
ConfirmPrompt,
|
||||||
|
DenyPrompt,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
|
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,12 @@ pub enum Action {
|
||||||
MouseRelease(Position),
|
MouseRelease(Position),
|
||||||
MouseHold(Position),
|
MouseHold(Position),
|
||||||
Copy,
|
Copy,
|
||||||
|
/// Confirm a prompt
|
||||||
|
Confirm,
|
||||||
|
/// Deny a prompt
|
||||||
|
Deny,
|
||||||
|
/// Confirm an action that invokes a prompt automatically
|
||||||
|
SkipConfirm(Box<Action>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OnForceClose> for Action {
|
impl From<OnForceClose> for Action {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ pub fn get_mode_info(
|
||||||
capabilities: PluginCapabilities,
|
capabilities: PluginCapabilities,
|
||||||
) -> ModeInfo {
|
) -> ModeInfo {
|
||||||
let keybinds = match mode {
|
let keybinds = match mode {
|
||||||
InputMode::Normal | InputMode::Locked => Vec::new(),
|
InputMode::Normal | InputMode::Locked | InputMode::Prompt => Vec::new(),
|
||||||
InputMode::Resize => vec![
|
InputMode::Resize => vec![
|
||||||
("←↓↑→".to_string(), "Resize".to_string()),
|
("←↓↑→".to_string(), "Resize".to_string()),
|
||||||
("+-".to_string(), "Increase/Decrease size".to_string()),
|
("+-".to_string(), "Increase/Decrease size".to_string()),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue