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,
|
||||
separator,
|
||||
),
|
||||
InputMode::Normal => key_indicators(
|
||||
InputMode::Normal | InputMode::Prompt => key_indicators(
|
||||
max_len,
|
||||
&[
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock),
|
||||
|
|
|
|||
|
|
@ -49,11 +49,11 @@ use zellij_utils::{
|
|||
setup::get_default_data_dir,
|
||||
};
|
||||
|
||||
pub(crate) type ClientId = u16;
|
||||
pub type ClientId = u16;
|
||||
|
||||
/// Instructions related to server-side application
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum ServerInstruction {
|
||||
pub enum ServerInstruction {
|
||||
NewClient(
|
||||
ClientAttributes,
|
||||
Box<CliArgs>,
|
||||
|
|
|
|||
|
|
@ -335,6 +335,28 @@ fn route_action(
|
|||
.send_to_screen(ScreenInstruction::Copy(client_id))
|
||||
.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 => {}
|
||||
}
|
||||
should_break
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use crate::{
|
|||
pty::{ClientOrTabIndex, PtyInstruction, VteBytes},
|
||||
tab::{Output, Tab},
|
||||
thread_bus::Bus,
|
||||
ui::overlay::{Overlay, OverlayWindow},
|
||||
wasm_vm::PluginInstruction,
|
||||
ClientId, ServerInstruction,
|
||||
};
|
||||
|
|
@ -24,7 +25,7 @@ use zellij_utils::{
|
|||
|
||||
/// Instructions that can be sent to the [`Screen`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum ScreenInstruction {
|
||||
pub enum ScreenInstruction {
|
||||
PtyBytes(RawFd, VteBytes),
|
||||
Render,
|
||||
NewPane(PaneId, ClientOrTabIndex),
|
||||
|
|
@ -84,6 +85,10 @@ pub(crate) enum ScreenInstruction {
|
|||
Copy(ClientId),
|
||||
AddClient(ClientId),
|
||||
RemoveClient(ClientId),
|
||||
AddOverlay(Overlay, ClientId),
|
||||
RemoveOverlay(ClientId),
|
||||
ConfirmPrompt(ClientId),
|
||||
DenyPrompt(ClientId),
|
||||
}
|
||||
|
||||
impl From<&ScreenInstruction> for ScreenContext {
|
||||
|
|
@ -154,6 +159,10 @@ impl From<&ScreenInstruction> for ScreenContext {
|
|||
ScreenInstruction::ToggleTab(..) => ScreenContext::ToggleTab,
|
||||
ScreenInstruction::AddClient(..) => ScreenContext::AddClient,
|
||||
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>,
|
||||
/// The full size of this [`Screen`].
|
||||
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.
|
||||
active_tab_indices: BTreeMap<ClientId, usize>,
|
||||
tab_history: BTreeMap<ClientId, Vec<usize>>,
|
||||
|
|
@ -193,6 +204,7 @@ impl Screen {
|
|||
colors: client_attributes.palette,
|
||||
active_tab_indices: BTreeMap::new(),
|
||||
tabs: BTreeMap::new(),
|
||||
_overlay: OverlayWindow::default(),
|
||||
tab_history: BTreeMap::new(),
|
||||
mode_info,
|
||||
draw_pane_frames,
|
||||
|
|
@ -1088,6 +1100,10 @@ pub(crate) fn screen_thread_main(
|
|||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::AddOverlay(_, _) => {}
|
||||
ScreenInstruction::RemoveOverlay(_) => {}
|
||||
ScreenInstruction::ConfirmPrompt(_) => {}
|
||||
ScreenInstruction::DenyPrompt(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod boundaries;
|
||||
pub mod overlay;
|
||||
pub mod pane_boundaries_frame;
|
||||
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
|
||||
#[serde(alias = "move")]
|
||||
Move,
|
||||
/// `Prompt` mode allows interacting with active prompts.
|
||||
#[serde(alias = "prompt")]
|
||||
Prompt,
|
||||
}
|
||||
|
||||
impl Default for InputMode {
|
||||
|
|
@ -129,6 +132,7 @@ impl FromStr for InputMode {
|
|||
"renametab" => Ok(InputMode::RenameTab),
|
||||
"session" => Ok(InputMode::Session),
|
||||
"move" => Ok(InputMode::Move),
|
||||
"prompt" => Ok(InputMode::Prompt),
|
||||
e => Err(e.to_string().into()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,6 +267,10 @@ pub enum ScreenContext {
|
|||
ToggleTab,
|
||||
AddClient,
|
||||
RemoveClient,
|
||||
AddOverlay,
|
||||
RemoveOverlay,
|
||||
ConfirmPrompt,
|
||||
DenyPrompt,
|
||||
}
|
||||
|
||||
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
|
||||
|
|
|
|||
|
|
@ -105,6 +105,12 @@ pub enum Action {
|
|||
MouseRelease(Position),
|
||||
MouseHold(Position),
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub fn get_mode_info(
|
|||
capabilities: PluginCapabilities,
|
||||
) -> ModeInfo {
|
||||
let keybinds = match mode {
|
||||
InputMode::Normal | InputMode::Locked => Vec::new(),
|
||||
InputMode::Normal | InputMode::Locked | InputMode::Prompt => Vec::new(),
|
||||
InputMode::Resize => vec![
|
||||
("←↓↑→".to_string(), "Resize".to_string()),
|
||||
("+-".to_string(), "Increase/Decrease size".to_string()),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue