Skip to content

Commit

Permalink
First attempt to provide an overlay prompt (#871)
Browse files Browse the repository at this point in the history
  • Loading branch information
a-kenji committed Nov 15, 2021
1 parent 55aef0a commit b861baa
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 5 deletions.
2 changes: 1 addition & 1 deletion default-plugins/status-bar/src/first_line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions zellij-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
22 changes: 22 additions & 0 deletions zellij-server/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 17 additions & 1 deletion zellij-server/src/screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -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),
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
}
}
}
Expand All @@ -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>>,
Expand All @@ -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,
Expand Down Expand Up @@ -1088,6 +1100,10 @@ pub(crate) fn screen_thread_main(

screen.render();
}
ScreenInstruction::AddOverlay(_, _) => {}
ScreenInstruction::RemoveOverlay(_) => {}
ScreenInstruction::ConfirmPrompt(_) => {}
ScreenInstruction::DenyPrompt(_) => {}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions zellij-server/src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod boundaries;
pub mod overlay;
pub mod pane_boundaries_frame;
pub mod pane_resizer;
104 changes: 104 additions & 0 deletions zellij-server/src/ui/overlay/mod.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 55 additions & 0 deletions zellij-server/src/ui/overlay/prompt.rs
Original file line number Diff line number Diff line change
@@ -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),
}
}
4 changes: 4 additions & 0 deletions zellij-tile/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()),
}
}
Expand Down
4 changes: 4 additions & 0 deletions zellij-utils/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions zellij-utils/src/input/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion zellij-utils/src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
Expand Down

0 comments on commit b861baa

Please sign in to comment.