summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--default-plugins/status-bar/src/first_line.rs2
-rw-r--r--zellij-server/src/lib.rs4
-rw-r--r--zellij-server/src/route.rs22
-rw-r--r--zellij-server/src/screen.rs18
-rw-r--r--zellij-server/src/ui/mod.rs1
-rw-r--r--zellij-server/src/ui/overlay/mod.rs104
-rw-r--r--zellij-server/src/ui/overlay/prompt.rs55
-rw-r--r--zellij-tile/src/data.rs4
-rw-r--r--zellij-utils/src/errors.rs4
-rw-r--r--zellij-utils/src/input/actions.rs6
-rw-r--r--zellij-utils/src/input/mod.rs2
11 files changed, 217 insertions, 5 deletions
diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs
index 5dc4f08ae..b3dea2783 100644
--- a/default-plugins/status-bar/src/first_line.rs
+++ b/default-plugins/status-bar/src/first_line.rs
@@ -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),
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
index 036d41f0a..4d94607f4 100644
--- a/zellij-server/src/lib.rs
+++ b/zellij-server/src/lib.rs
@@ -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>,
diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs
index 457354916..ba70a3c81 100644
--- a/zellij-server/src/route.rs
+++ b/zellij-server/src/route.rs
@@ -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
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index c2c9d4237..a6b871f9c 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -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(_) => {}
}
}
}
diff --git a/zellij-server/src/ui/mod.rs b/zellij-server/src/ui/mod.rs
index 6ebc46831..08c381836 100644
--- a/zellij-server/src/ui/mod.rs
+++ b/zellij-server/src/ui/mod.rs
@@ -1,3 +1,4 @@
pub mod boundaries;
+pub mod overlay;
pub mod pane_boundaries_frame;
pub mod pane_resizer;
diff --git a/zellij-server/src/ui/overlay/mod.rs b/zellij-server/src/ui/overlay/mod.rs
new file mode 100644
index 000000000..3bac1afb5
--- /dev/null
+++ b/zellij-server/src/ui/overlay/mod.rs
@@ -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(' ');
+ }
+ }
+ }
+}
diff --git a/zellij-server/src/ui/overlay/prompt.rs b/zellij-server/src/ui/overlay/prompt.rs
new file mode 100644
index 000000000..f28e3c405
--- /dev/null
+++ b/zellij-server/src/ui/overlay/prompt.rs
@@ -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),
+ }
+}
diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs
index 872a78f9f..11b94beff 100644
--- a/zellij-tile/src/data.rs
+++ b/zellij-tile/src/data.rs
@@ -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()),
}
}
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index 7088c30ce..aeeed2030 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -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.
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index bd43206cf..c84ad285e 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -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 {
diff --git a/zellij-utils/src/input/mod.rs b/zellij-utils/src/input/mod.rs
index ca26f78f6..a10ffa2cd 100644
--- a/zellij-utils/src/input/mod.rs
+++ b/zellij-utils/src/input/mod.rs
@@ -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()),