zellij/default-plugins/tab-bar/src/tab.rs
Mark Grey 26b99eac63
feat(config): new theme definition spec (#3242)
* Implement initial structs from spec

* kdl configuration unmarshalling

* typo text styling

* remove is_selected toggle

* incorporate new status bar ui into theming

* improve test coverage of config behavior

* tab bar correction

* correct also compact bar

* remove spacing between table columns

* refactor table styling

* use text_unselected.emphasis_1 for keygroup sep

* fix tab bar more text

* repair field flattening for theme

* remove extra styling KDL node

* update tests

* updated selected text conversion

* padding for header bar

* minor corrections for existing themes

* background handling

* compact bar corrections

* properly handle opaque method to activate background

* update newer plugins to use styling struct

* correct omission of selected state

* fix: bold typeface for text elements

* fix: fg -> white for list_unselected conversion

* fix: emphasis and opacity handling for nested_list

* correct stylings in the session-manager

* fix emphases translation for table component

* correct emphasis for run instructions

* correct frame_highlight translation for old themes

* provide missing implementation of frame_highlight

* fencepost emphasis color names

* Set a pseudo-None for frame_unselected in old theme conversion

* correct alternating bg for simplified-ui

* update snapshots

* fix inner text padding and errorneous snapshots

* suppress warning about deprecated usage of palette

* remove unused import

* feat(plugins): API to change floating pane coordinates (#3958)

* basic functionality through the cli

* added to plugin api

* add display area and viewport size to TabInfo

* fix tests and add new one

* some cleanups

* refactor: extract pane_id parsing logic

* style(fmt): rustfmt

* docs(changelog): floating pane coordinate chagne API

* fix(tiled-panes): opening panes from the cli (#3963)

* feat(plugins): add `PastedText` Event (#3962)

* working with text paste

* handle utf8 conversion error

* feat(plugins): add PastedText Event

* docs(changelog): plugins pasted text event

* black for table opaque background

* properly apply opacity to table

* correct padding for explicit width ribbons

* feat(plugins): Allow opening panes near plugin (#3966)

* added command + terminal variants

* added editor variant

* style(fmt): rustfmt

* docs(changelog): plugin apis to open panes near plugin

* feat(plugins): send info about $EDITOR and $SHELL (#3971)

* feat(plugins): send info about $EDITOR and $SHELL

* fix(e2e): snapshot update

* docs(changelog): plugin editor and shell info

* fix(floating-panes): when changing coordinates, if a pane is not floating - make it floating (#3972)

* fix(panes): when changing floating pane coordinates, if the pane is not floating, float it

* style(fmt): rustfmt

* docs(changelog): floating pane coordinate fix

* fix(break-pane): strip logical position when inserting pane to new tab (#3973)

* docs(changelog): logical position fix

* Optional frame_unselected theme

* fixture with correct width to account for arrow padding

* update snapshot and rustfmt

---------

Co-authored-by: Aram Drevekenin <aram@poor.dev>
2025-02-07 11:59:54 +01:00

140 lines
4.4 KiB
Rust

use crate::{line::tab_separator, LinePart};
use ansi_term::{ANSIString, ANSIStrings};
use unicode_width::UnicodeWidthStr;
use zellij_tile::prelude::*;
use zellij_tile_utils::style;
fn cursors(
focused_clients: &[ClientId],
multiplayer_colors: MultiplayerColors,
) -> (Vec<ANSIString>, usize) {
// cursor section, text length
let mut len = 0;
let mut cursors = vec![];
for client_id in focused_clients.iter() {
if let Some(color) = client_id_to_colors(*client_id, multiplayer_colors) {
cursors.push(style!(color.1, color.0).paint(" "));
len += 1;
}
}
(cursors, len)
}
pub fn render_tab(
text: String,
tab: &TabInfo,
is_alternate_tab: bool,
palette: Styling,
separator: &str,
) -> LinePart {
let focused_clients = tab.other_focused_clients.as_slice();
let separator_width = separator.width();
let alternate_tab_color = if is_alternate_tab {
palette.ribbon_unselected.emphasis_1
} else {
palette.ribbon_unselected.background
};
let background_color = if tab.active {
palette.ribbon_selected.background
} else if is_alternate_tab {
alternate_tab_color
} else {
palette.ribbon_unselected.background
};
let foreground_color = if tab.active {
palette.ribbon_selected.base
} else {
palette.ribbon_unselected.base
};
let separator_fill_color = palette.text_unselected.background;
let left_separator = style!(separator_fill_color, background_color).paint(separator);
let mut tab_text_len = text.width() + (separator_width * 2) + 2; // +2 for padding
let tab_styled_text = style!(foreground_color, background_color)
.bold()
.paint(format!(" {} ", text));
let right_separator = style!(background_color, separator_fill_color).paint(separator);
let tab_styled_text = if !focused_clients.is_empty() {
let (cursor_section, extra_length) =
cursors(focused_clients, palette.multiplayer_user_colors);
tab_text_len += extra_length + 2; // 2 for cursor_beginning and cursor_end
let mut s = String::new();
let cursor_beginning = style!(foreground_color, background_color)
.bold()
.paint("[")
.to_string();
let cursor_section = ANSIStrings(&cursor_section).to_string();
let cursor_end = style!(foreground_color, background_color)
.bold()
.paint("]")
.to_string();
s.push_str(&left_separator.to_string());
s.push_str(&tab_styled_text.to_string());
s.push_str(&cursor_beginning);
s.push_str(&cursor_section);
s.push_str(&cursor_end);
s.push_str(&right_separator.to_string());
s
} else {
ANSIStrings(&[left_separator, tab_styled_text, right_separator]).to_string()
};
LinePart {
part: tab_styled_text,
len: tab_text_len,
tab_index: Some(tab.position),
}
}
pub fn tab_style(
mut tabname: String,
tab: &TabInfo,
mut is_alternate_tab: bool,
palette: Styling,
capabilities: PluginCapabilities,
) -> LinePart {
let separator = tab_separator(capabilities);
if tab.is_fullscreen_active {
tabname.push_str(" (FULLSCREEN)");
} else if tab.is_sync_panes_active {
tabname.push_str(" (SYNC)");
}
// we only color alternate tabs differently if we can't use the arrow fonts to separate them
if !capabilities.arrow_fonts {
is_alternate_tab = false;
}
render_tab(tabname, tab, is_alternate_tab, palette, separator)
}
pub(crate) fn get_tab_to_focus(
tab_line: &[LinePart],
active_tab_idx: usize,
mouse_click_col: usize,
) -> Option<usize> {
let clicked_line_part = get_clicked_line_part(tab_line, mouse_click_col)?;
let clicked_tab_idx = clicked_line_part.tab_index?;
// tabs are indexed starting from 1 so we need to add 1
let clicked_tab_idx = clicked_tab_idx + 1;
if clicked_tab_idx != active_tab_idx {
return Some(clicked_tab_idx);
}
None
}
pub(crate) fn get_clicked_line_part(
tab_line: &[LinePart],
mouse_click_col: usize,
) -> Option<&LinePart> {
let mut len = 0;
for tab_line_part in tab_line {
if mouse_click_col >= len && mouse_click_col < len + tab_line_part.len {
return Some(tab_line_part);
}
len += tab_line_part.len;
}
None
}