From 4fe604129ddbc13acad4895053ae26344f738f75 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Tue, 20 Aug 2024 16:10:27 +0200 Subject: [PATCH] feat(ux): change themes at runtime (#3559) --- zellij-client/src/lib.rs | 4 +- zellij-server/src/lib.rs | 4 ++ zellij-server/src/panes/floating_panes/mod.rs | 8 +++- zellij-server/src/panes/grid.rs | 3 ++ zellij-server/src/panes/plugin_pane.rs | 6 +++ zellij-server/src/panes/terminal_pane.rs | 8 ++++ zellij-server/src/panes/tiled_panes/mod.rs | 8 +++- zellij-server/src/route.rs | 17 --------- zellij-server/src/screen.rs | 38 +++++++++++++++---- zellij-server/src/tab/mod.rs | 9 +++++ zellij-utils/src/data.rs | 3 ++ zellij-utils/src/input/config.rs | 4 +- 12 files changed, 81 insertions(+), 31 deletions(-) diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index 8805416f5f..a7cb6a27f8 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -216,7 +216,7 @@ pub fn start_client( config.env.set_vars(); let palette = config - .theme_config(&config_options) + .theme_config(config_options.theme.as_ref()) .unwrap_or_else(|| os_input.load_palette()); let full_screen_ws = os_input.get_terminal_size_using_fd(0); @@ -624,7 +624,7 @@ pub fn start_server_detached( config.env.set_vars(); let palette = config - .theme_config(&config_options) + .theme_config(config_options.theme.as_ref()) .unwrap_or_else(|| os_input.load_palette()); let client_attributes = ClientAttributes { diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index fe1b3ada41..537ba059f7 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -55,6 +55,7 @@ use zellij_utils::{ plugins::PluginAliases, }, ipc::{ClientAttributes, ExitReason, ServerToClientMsg}, + shared::default_palette, }; pub type ClientId = u16; @@ -267,6 +268,9 @@ impl SessionMetaData { client_id, keybinds: Some(new_config.keybinds.clone()), default_mode: new_config.options.default_mode, + theme: new_config + .theme_config(new_config.options.theme.as_ref()) + .or_else(|| Some(default_palette())), }) .unwrap(); self.senders diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs index 2aa74d9ced..9bb438cba9 100644 --- a/zellij-server/src/panes/floating_panes/mod.rs +++ b/zellij-server/src/panes/floating_panes/mod.rs @@ -22,7 +22,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::rc::Rc; use std::time::Instant; use zellij_utils::{ - data::{ModeInfo, Style}, + data::{ModeInfo, Palette, Style}, errors::prelude::*, input::command::RunCommand, input::layout::{FloatingPaneLayout, Run, RunPluginOrAlias}, @@ -913,4 +913,10 @@ impl FloatingPanes { }, } } + pub fn update_pane_themes(&mut self, theme: Palette) { + self.style.colors = theme; + for pane in self.panes.values_mut() { + pane.update_theme(theme); + } + } } diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs index 781030b175..55eee8187b 100644 --- a/zellij-server/src/panes/grid.rs +++ b/zellij-server/src/panes/grid.rs @@ -2155,6 +2155,9 @@ impl Grid { pub fn unlock_renders(&mut self) { self.lock_renders = false; } + pub fn update_theme(&mut self, theme: Palette) { + self.style.colors = theme.clone(); + } } impl Perform for Grid { diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs index f7ad387114..50c3236ca1 100644 --- a/zellij-server/src/panes/plugin_pane.rs +++ b/zellij-server/src/panes/plugin_pane.rs @@ -675,6 +675,12 @@ impl Pane for PluginPane { self.pane_name = String::from_utf8_lossy(&buf).to_string(); self.set_should_render(true); } + fn update_theme(&mut self, theme: Palette) { + self.style.colors = theme.clone(); + for grid in self.grids.values_mut() { + grid.update_theme(theme.clone()); + } + } } impl PluginPane { diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index f2f89c59a2..1c689dfe61 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -790,6 +790,14 @@ impl Pane for TerminalPane { run_command.clone() }) } + fn update_theme(&mut self, theme: Palette) { + self.style.colors = theme.clone(); + self.grid.update_theme(theme); + if self.banner.is_some() { + // we do this so that the banner will be updated with the new theme colors + self.render_first_run_banner(); + } + } } impl TerminalPane { diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index 9c2458c2c8..5baad9b954 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -18,7 +18,7 @@ use crate::{ }; use stacked_panes::StackedPanes; use zellij_utils::{ - data::{Direction, ModeInfo, PaneInfo, ResizeStrategy, Style}, + data::{Direction, ModeInfo, Palette, PaneInfo, ResizeStrategy, Style}, errors::prelude::*, input::{ command::RunCommand, @@ -1766,6 +1766,12 @@ impl TiledPanes { } pane_infos } + pub fn update_pane_themes(&mut self, theme: Palette) { + self.style.colors = theme; + for pane in self.panes.values_mut() { + pane.update_theme(theme); + } + } } #[allow(clippy::borrowed_box)] diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index f23f3176da..f27266e0e2 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -93,23 +93,6 @@ pub(crate) fn route_action( }, Action::SwitchToMode(mode) => { let attrs = &client_attributes; - // TODO: use the palette from the client and remove it from the server os api - // this is left here as a stop gap measure until we shift some code around - // to allow for this - // TODO: Need access to `ClientAttributes` here - senders - .send_to_plugin(PluginInstruction::Update(vec![( - None, - Some(client_id), - Event::ModeUpdate(get_mode_info( - mode, - attrs, - capabilities, - &client_keybinds, - Some(default_mode), - )), - )])) - .with_context(err_context)?; senders .send_to_server(ServerInstruction::ChangeMode(client_id, mode)) .with_context(err_context)?; diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index ea1814bc70..8dd01ac249 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -368,6 +368,7 @@ pub enum ScreenInstruction { client_id: ClientId, keybinds: Option, default_mode: Option, + theme: Option, }, RerunCommandPane(u32), // u32 - terminal pane id } @@ -1735,11 +1736,13 @@ impl Screen { if mode_info.session_name.as_ref() != Some(&self.session_name) { mode_info.session_name = Some(self.session_name.clone()); } - let previous_mode = self + + let previous_mode_info = self .mode_info .get(&client_id) - .unwrap_or(&self.default_mode_info) - .mode; + .unwrap_or(&self.default_mode_info); + let previous_mode = previous_mode_info.mode; + mode_info.style = previous_mode_info.style; let err_context = || { format!( @@ -2183,10 +2186,22 @@ impl Screen { &mut self, new_keybinds: Option, new_default_mode: Option, + theme: Option, client_id: ClientId, ) -> Result<()> { + let should_update_mode_info = + new_keybinds.is_some() || new_default_mode.is_some() || theme.is_some(); + + // themes are currently global and not per-client + if let Some(theme) = theme { + self.default_mode_info.update_theme(theme); + for tab in self.tabs.values_mut() { + tab.update_theme(theme); + } + } + + // client specific configuration if self.connected_clients_contains(&client_id) { - let should_update_mode_info = new_keybinds.is_some() || new_default_mode.is_some(); let mode_info = self .mode_info .entry(client_id) @@ -2197,15 +2212,21 @@ impl Screen { if let Some(new_default_mode) = new_default_mode { mode_info.update_default_mode(new_default_mode); } + if let Some(theme) = theme { + mode_info.update_theme(theme); + } if should_update_mode_info { for tab in self.tabs.values_mut() { tab.change_mode_info(mode_info.clone(), client_id); tab.mark_active_pane_for_rerender(client_id); - tab.update_input_modes()?; } } - } else { - log::error!("Could not find client_id {client_id} to rebind keys"); + } + + if should_update_mode_info { + for tab in self.tabs.values_mut() { + tab.update_input_modes()?; + } } Ok(()) } @@ -4082,9 +4103,10 @@ pub(crate) fn screen_thread_main( client_id, keybinds, default_mode, + theme, } => { screen - .reconfigure_mode_info(keybinds, default_mode, client_id) + .reconfigure_mode_info(keybinds, default_mode, theme, client_id) .non_fatal(); }, ScreenInstruction::RerunCommandPane(terminal_pane_id) => { diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index e08110b0d6..44673dad44 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -492,6 +492,7 @@ pub trait Pane { fn rerun(&mut self) -> Option { None } // only relevant to terminal panes + fn update_theme(&mut self, _theme: Palette) {} } #[derive(Clone, Debug)] @@ -3951,6 +3952,14 @@ impl Tab { }, } } + pub fn update_theme(&mut self, theme: Palette) { + self.style.colors = theme; + self.floating_panes.update_pane_themes(theme); + self.tiled_panes.update_pane_themes(theme); + for (_, pane) in self.suppressed_panes.values_mut() { + pane.update_theme(theme); + } + } } pub fn pane_info_for_pane(pane_id: &PaneId, pane: &Box) -> PaneInfo { diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 59653e7825..f1e95df208 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -1175,6 +1175,9 @@ impl ModeInfo { pub fn update_default_mode(&mut self, new_default_mode: InputMode) { self.base_mode = Some(new_default_mode); } + pub fn update_theme(&mut self, theme: Palette) { + self.style.colors = theme; + } } #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index cf3ba2cc21..49ff5c2e52 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -163,8 +163,8 @@ impl TryFrom<&CliArgs> for Config { } impl Config { - pub fn theme_config(&self, opts: &Options) -> Option { - match &opts.theme { + pub fn theme_config(&self, theme_name: Option<&String>) -> Option { + match &theme_name { Some(theme_name) => self.themes.get_theme(theme_name).map(|theme| theme.palette), None => self.themes.get_theme("default").map(|theme| theme.palette), }