Skip to content

Commit

Permalink
This adds a UI for multiple users in panes (behind a feature flag) (#897
Browse files Browse the repository at this point in the history
)

* feat(ui): multiple users in panes

* style(fmt): make rustfmt happy

* style(fmt): make clippy happy
  • Loading branch information
imsnif authored Nov 25, 2021
1 parent 9fb2c7c commit 6c6a439
Show file tree
Hide file tree
Showing 18 changed files with 1,005 additions and 281 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 29 additions & 35 deletions default-plugins/status-bar/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,61 +76,55 @@ pub struct ColoredElements {
// that can be defined in the config perhaps
fn color_elements(palette: Palette) -> ColoredElements {
match palette.source {
// "cyan" here is used as a background as a dirty hack
// this is because the Palette struct doesn't have a "gray" section
// and we can't use its "bg" because that is now dynamically taken from the terminal
// and might often not actually fit the rest of the colorscheme
//
// to fix this, we need to restructure the Palette struct
PaletteSource::Default => ColoredElements {
selected_prefix_separator: style!(palette.cyan, palette.green),
selected_prefix_separator: style!(palette.gray, palette.green),
selected_char_left_separator: style!(palette.black, palette.green).bold(),
selected_char_shortcut: style!(palette.red, palette.green).bold(),
selected_char_right_separator: style!(palette.black, palette.green).bold(),
selected_styled_text: style!(palette.black, palette.green).bold(),
selected_suffix_separator: style!(palette.green, palette.cyan).bold(),
unselected_prefix_separator: style!(palette.cyan, palette.fg),
selected_suffix_separator: style!(palette.green, palette.gray).bold(),
unselected_prefix_separator: style!(palette.gray, palette.fg),
unselected_char_left_separator: style!(palette.black, palette.fg).bold(),
unselected_char_shortcut: style!(palette.red, palette.fg).bold(),
unselected_char_right_separator: style!(palette.black, palette.fg).bold(),
unselected_styled_text: style!(palette.black, palette.fg).bold(),
unselected_suffix_separator: style!(palette.fg, palette.cyan),
disabled_prefix_separator: style!(palette.cyan, palette.fg),
disabled_styled_text: style!(palette.cyan, palette.fg).dimmed(),
disabled_suffix_separator: style!(palette.fg, palette.cyan),
selected_single_letter_prefix_separator: style!(palette.cyan, palette.green),
unselected_suffix_separator: style!(palette.fg, palette.gray),
disabled_prefix_separator: style!(palette.gray, palette.fg),
disabled_styled_text: style!(palette.gray, palette.fg).dimmed(),
disabled_suffix_separator: style!(palette.fg, palette.gray),
selected_single_letter_prefix_separator: style!(palette.gray, palette.green),
selected_single_letter_char_shortcut: style!(palette.red, palette.green).bold(),
selected_single_letter_suffix_separator: style!(palette.green, palette.cyan),
unselected_single_letter_prefix_separator: style!(palette.cyan, palette.fg),
selected_single_letter_suffix_separator: style!(palette.green, palette.gray),
unselected_single_letter_prefix_separator: style!(palette.gray, palette.fg),
unselected_single_letter_char_shortcut: style!(palette.red, palette.fg).bold(),
unselected_single_letter_suffix_separator: style!(palette.fg, palette.cyan),
superkey_prefix: style!(palette.white, palette.cyan).bold(),
superkey_suffix_separator: style!(palette.cyan, palette.cyan),
unselected_single_letter_suffix_separator: style!(palette.fg, palette.gray),
superkey_prefix: style!(palette.white, palette.gray).bold(),
superkey_suffix_separator: style!(palette.gray, palette.gray),
},
PaletteSource::Xresources => ColoredElements {
selected_prefix_separator: style!(palette.cyan, palette.green),
selected_prefix_separator: style!(palette.gray, palette.green),
selected_char_left_separator: style!(palette.fg, palette.green).bold(),
selected_char_shortcut: style!(palette.red, palette.green).bold(),
selected_char_right_separator: style!(palette.fg, palette.green).bold(),
selected_styled_text: style!(palette.cyan, palette.green).bold(),
selected_suffix_separator: style!(palette.green, palette.cyan).bold(),
unselected_prefix_separator: style!(palette.cyan, palette.fg),
unselected_char_left_separator: style!(palette.cyan, palette.fg).bold(),
selected_styled_text: style!(palette.gray, palette.green).bold(),
selected_suffix_separator: style!(palette.green, palette.gray).bold(),
unselected_prefix_separator: style!(palette.gray, palette.fg),
unselected_char_left_separator: style!(palette.gray, palette.fg).bold(),
unselected_char_shortcut: style!(palette.red, palette.fg).bold(),
unselected_char_right_separator: style!(palette.cyan, palette.fg).bold(),
unselected_styled_text: style!(palette.cyan, palette.fg).bold(),
unselected_suffix_separator: style!(palette.fg, palette.cyan),
disabled_prefix_separator: style!(palette.cyan, palette.fg),
disabled_styled_text: style!(palette.cyan, palette.fg).dimmed(),
disabled_suffix_separator: style!(palette.fg, palette.cyan),
unselected_char_right_separator: style!(palette.gray, palette.fg).bold(),
unselected_styled_text: style!(palette.gray, palette.fg).bold(),
unselected_suffix_separator: style!(palette.fg, palette.gray),
disabled_prefix_separator: style!(palette.gray, palette.fg),
disabled_styled_text: style!(palette.gray, palette.fg).dimmed(),
disabled_suffix_separator: style!(palette.fg, palette.gray),
selected_single_letter_prefix_separator: style!(palette.fg, palette.green),
selected_single_letter_char_shortcut: style!(palette.red, palette.green).bold(),
selected_single_letter_suffix_separator: style!(palette.green, palette.fg),
unselected_single_letter_prefix_separator: style!(palette.fg, palette.cyan),
unselected_single_letter_prefix_separator: style!(palette.fg, palette.gray),
unselected_single_letter_char_shortcut: style!(palette.red, palette.fg).bold(),
unselected_single_letter_suffix_separator: style!(palette.fg, palette.cyan),
superkey_prefix: style!(palette.cyan, palette.fg).bold(),
superkey_suffix_separator: style!(palette.fg, palette.cyan),
unselected_single_letter_suffix_separator: style!(palette.fg, palette.gray),
superkey_prefix: style!(palette.gray, palette.fg).bold(),
superkey_suffix_separator: style!(palette.fg, palette.gray),
},
}
}
Expand Down Expand Up @@ -231,7 +225,7 @@ impl ZellijPlugin for State {

// [48;5;238m is gray background, [0K is so that it fills the rest of the line
// [m is background reset, [0K is so that it clears the rest of the line
match self.mode_info.palette.cyan {
match self.mode_info.palette.gray {
PaletteColor::Rgb((r, g, b)) => {
println!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", first_line, r, g, b);
}
Expand Down
12 changes: 6 additions & 6 deletions default-plugins/tab-bar/src/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ fn left_more_message(tab_count_to_the_left: usize, palette: Palette, separator:
// 238
// chars length plus separator length on both sides
let more_text_len = more_text.width() + 2 * separator.width();
let left_separator = style!(palette.cyan, palette.orange).paint(separator);
let left_separator = style!(palette.gray, palette.orange).paint(separator);
let more_styled_text = style!(palette.black, palette.orange)
.bold()
.paint(more_text);
let right_separator = style!(palette.orange, palette.cyan).paint(separator);
let right_separator = style!(palette.orange, palette.gray).paint(separator);
let more_styled_text = format!(
"{}",
ANSIStrings(&[left_separator, more_styled_text, right_separator,])
Expand All @@ -132,11 +132,11 @@ fn right_more_message(
};
// chars length plus separator length on both sides
let more_text_len = more_text.width() + 2 * separator.width();
let left_separator = style!(palette.cyan, palette.orange).paint(separator);
let left_separator = style!(palette.gray, palette.orange).paint(separator);
let more_styled_text = style!(palette.black, palette.orange)
.bold()
.paint(more_text);
let right_separator = style!(palette.orange, palette.cyan).paint(separator);
let right_separator = style!(palette.orange, palette.gray).paint(separator);
let more_styled_text = format!(
"{}",
ANSIStrings(&[left_separator, more_styled_text, right_separator,])
Expand All @@ -151,7 +151,7 @@ fn tab_line_prefix(session_name: Option<&str>, palette: Palette, cols: usize) ->
let prefix_text = " Zellij ".to_string();

let prefix_text_len = prefix_text.chars().count();
let prefix_styled_text = style!(palette.white, palette.cyan)
let prefix_styled_text = style!(palette.white, palette.gray)
.bold()
.paint(prefix_text);
let mut parts = vec![LinePart {
Expand All @@ -161,7 +161,7 @@ fn tab_line_prefix(session_name: Option<&str>, palette: Palette, cols: usize) ->
if let Some(name) = session_name {
let name_part = format!("({}) ", name);
let name_part_len = name_part.width();
let name_part_styled_text = style!(palette.white, palette.cyan).bold().paint(name_part);
let name_part_styled_text = style!(palette.white, palette.gray).bold().paint(name_part);
if cols.saturating_sub(prefix_text_len) >= name_part_len {
parts.push(LinePart {
part: format!("{}", name_part_styled_text),
Expand Down
2 changes: 1 addition & 1 deletion default-plugins/tab-bar/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl ZellijPlugin for State {
}
len_cnt += bar_part.len;
}
match self.mode_info.palette.cyan {
match self.mode_info.palette.gray {
PaletteColor::Rgb((r, g, b)) => {
println!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b);
}
Expand Down
8 changes: 4 additions & 4 deletions default-plugins/tab-bar/src/tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ use zellij_tile::prelude::*;
use zellij_tile_utils::style;

pub fn active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
let left_separator = style!(palette.cyan, palette.green).paint(separator);
let left_separator = style!(palette.gray, palette.green).paint(separator);
let tab_text_len = text.width() + 2 + separator.width() * 2; // 2 for left and right separators, 2 for the text padding
let tab_styled_text = style!(palette.black, palette.green)
.bold()
.paint(format!(" {} ", text));
let right_separator = style!(palette.green, palette.cyan).paint(separator);
let right_separator = style!(palette.green, palette.gray).paint(separator);
let tab_styled_text = format!(
"{}",
ANSIStrings(&[left_separator, tab_styled_text, right_separator,])
Expand All @@ -22,12 +22,12 @@ pub fn active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
}

pub fn non_active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
let left_separator = style!(palette.cyan, palette.fg).paint(separator);
let left_separator = style!(palette.gray, palette.fg).paint(separator);
let tab_text_len = text.width() + 2 + separator.width() * 2; // 2 for left and right separators, 2 for the text padding
let tab_styled_text = style!(palette.black, palette.fg)
.bold()
.paint(format!(" {} ", text));
let right_separator = style!(palette.fg, palette.cyan).paint(separator);
let right_separator = style!(palette.fg, palette.gray).paint(separator);
let tab_styled_text = format!(
"{}",
ANSIStrings(&[left_separator, tab_styled_text, right_separator,])
Expand Down
1 change: 1 addition & 0 deletions zellij-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ wasmer = "1.0.0"
wasmer-wasi = "1.0.0"
cassowary = "0.3.0"
zellij-utils = { path = "../zellij-utils/", version = "0.21.0" }
zellij-tile = { path = "../zellij-tile/", version = "0.21.0" }
log = "0.4.14"
typetag = "0.1.7"
chrono = "0.4.19"
Expand Down
14 changes: 10 additions & 4 deletions zellij-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod ui;
mod wasm_vm;

use log::info;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::{
path::PathBuf,
sync::{Arc, Mutex, RwLock},
Expand Down Expand Up @@ -134,9 +134,15 @@ impl SessionState {
}
}
pub fn new_client(&mut self) -> ClientId {
let mut clients: Vec<ClientId> = self.clients.keys().copied().collect();
clients.sort_unstable();
let next_client_id = clients.last().unwrap_or(&0) + 1;
let clients: HashSet<ClientId> = self.clients.keys().copied().collect();
let mut next_client_id = 1;
loop {
if clients.contains(&next_client_id) {
next_client_id += 1;
} else {
break;
}
}
self.clients.insert(next_client_id, None);
next_client_id
}
Expand Down
13 changes: 13 additions & 0 deletions zellij-server/src/panes/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,9 @@ impl Grid {
pub fn render_full_viewport(&mut self) {
self.output_buffer.update_all_lines();
}
pub fn update_line_for_rendering(&mut self, line_index: usize) {
self.output_buffer.update_line(line_index);
}
pub fn advance_to_next_tabstop(&mut self, styles: CharacterStyles) {
let mut next_tabstop = None;
for tabstop in self.horizontal_tabstops.iter() {
Expand Down Expand Up @@ -1057,6 +1060,16 @@ impl Grid {
self.add_character_at_cursor_position(terminal_character);
self.move_cursor_forward_until_edge(character_width);
}
pub fn get_character_under_cursor(&self) -> Option<TerminalCharacter> {
let absolute_x_in_line = self.get_absolute_character_index(self.cursor.x, self.cursor.y);
self.viewport
.get(self.cursor.y)
.and_then(|current_line| current_line.columns.get(absolute_x_in_line))
.copied()
}
pub fn get_absolute_character_index(&self, x: usize, y: usize) -> usize {
self.viewport.get(y).unwrap().absolute_character_index(x)
}
pub fn move_cursor_forward_until_edge(&mut self, count: usize) {
let count_to_move = std::cmp::min(count, self.width - (self.cursor.x));
self.cursor.x += count_to_move;
Expand Down
42 changes: 24 additions & 18 deletions zellij-server/src/panes/plugin_pane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use std::unimplemented;
use crate::panes::PaneId;
use crate::pty::VteBytes;
use crate::tab::Pane;
use crate::ui::pane_boundaries_frame::PaneFrame;
use crate::ui::pane_boundaries_frame::{FrameParams, PaneFrame};
use crate::wasm_vm::PluginInstruction;
use crate::ClientId;
use zellij_utils::pane_size::Offset;
use zellij_utils::position::Position;
use zellij_utils::shared::ansi_len;
Expand All @@ -27,7 +28,6 @@ pub(crate) struct PluginPane {
pub active_at: Instant,
pub pane_title: String,
frame: bool,
frame_color: Option<PaletteColor>,
borderless: bool,
}

Expand All @@ -47,7 +47,6 @@ impl PluginPane {
send_plugin_instructions,
active_at: Instant::now(),
frame: false,
frame_color: None,
content_offset: Offset::default(),
pane_title: title,
borderless: false,
Expand Down Expand Up @@ -152,17 +151,6 @@ impl Pane for PluginPane {

self.should_render = false;
let contents = buf_rx.recv().unwrap();
// FIXME: This is a hack that assumes all fixed-size panes are borderless. This
// will eventually need fixing!
if self.frame && !(self.geom.rows.is_fixed() || self.geom.cols.is_fixed()) {
let frame = PaneFrame {
geom: self.current_geom().into(),
title: self.pane_title.clone(),
color: self.frame_color,
..Default::default()
};
vte_output.push_str(&frame.render());
}
for (index, line) in contents.lines().enumerate() {
let actual_len = ansi_len(line);
let line_to_print = if actual_len > self.get_content_columns() {
Expand Down Expand Up @@ -212,6 +200,28 @@ impl Pane for PluginPane {
None
}
}
fn render_frame(&mut self, _client_id: ClientId, frame_params: FrameParams) -> Option<String> {
// FIXME: This is a hack that assumes all fixed-size panes are borderless. This
// will eventually need fixing!
if self.frame && !(self.geom.rows.is_fixed() || self.geom.cols.is_fixed()) {
let frame = PaneFrame::new(
self.current_geom().into(),
(0, 0), // scroll position
self.pane_title.clone(),
frame_params,
);
Some(frame.render())
} else {
None
}
}
fn render_fake_cursor(
&mut self,
_cursor_color: PaletteColor,
_text_color: PaletteColor,
) -> Option<String> {
None
}
fn pid(&self) -> PaneId {
PaneId::Plugin(self.pid)
}
Expand Down Expand Up @@ -317,10 +327,6 @@ impl Pane for PluginPane {
fn set_content_offset(&mut self, offset: Offset) {
self.content_offset = offset;
}
fn set_boundary_color(&mut self, color: Option<PaletteColor>) {
self.frame_color = color;
self.set_should_render(true);
}
fn set_borderless(&mut self, borderless: bool) {
self.borderless = borderless;
}
Expand Down
11 changes: 11 additions & 0 deletions zellij-server/src/panes/terminal_character.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::convert::From;
use std::fmt::{self, Debug, Display, Formatter};
use std::ops::{Index, IndexMut};

use zellij_utils::vte::ParamsIter;

use crate::panes::alacritty_functions::parse_sgr_color;
use zellij_tile::data::PaletteColor;

pub const EMPTY_TERMINAL_CHARACTER: TerminalCharacter = TerminalCharacter {
character: ' ',
Expand Down Expand Up @@ -35,6 +37,15 @@ pub enum AnsiCode {
ColorIndex(u8),
}

impl From<PaletteColor> for AnsiCode {
fn from(palette_color: PaletteColor) -> Self {
match palette_color {
PaletteColor::Rgb((r, g, b)) => AnsiCode::RgbCode((r, g, b)),
PaletteColor::EightBit(index) => AnsiCode::ColorIndex(index),
}
}
}

#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum NamedColor {
Black,
Expand Down
Loading

0 comments on commit 6c6a439

Please sign in to comment.