yoda: attenuation des panneaux non focalises, a la Ghostty
Avec Claude Code et Modjo cote a cote, rien ne distinguait le panneau ou
l'on tape, hors le curseur creux. L'amont assombrissait le fond de 8 %
(aussi quand la fenetre perdait le focus) et l'a retire en juillet
(a43b5ae).
Reprise de unfocused-split-opacity de Ghostty : un voile de la couleur du
fond, texte compris, sur chaque panneau non focalise, seulement quand il y
en a plusieurs ; la perte de focus de la fenetre ne change rien.
- Config unfocused_pane_dim (pourcentage, defaut 30 = opacite 0,7 de
Ghostty, 0 desactive, plafond 85 = opacite minimale 0,15 de Ghostty).
- Reglages > Panneaux : curseur « Attenuation des panneaux inactifs ».
- terminal_box : voile trace dans un calque a part en fin de rendu (dans
le meme calque, la primitive de texte passerait par-dessus), aux coins
du fond du panneau.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
52b37cc2f8
commit
db9ee02d8d
5 changed files with 99 additions and 0 deletions
|
|
@ -59,6 +59,7 @@ use-bright-bold = Make bold text brighter
|
||||||
splits = Splits
|
splits = Splits
|
||||||
focus-follow-mouse = Typing focus follows mouse
|
focus-follow-mouse = Typing focus follows mouse
|
||||||
show-pane-borders = Show pane borders
|
show-pane-borders = Show pane borders
|
||||||
|
unfocused-pane-dim = Dim unfocused panes
|
||||||
|
|
||||||
### Advanced
|
### Advanced
|
||||||
advanced = Advanced
|
advanced = Advanced
|
||||||
|
|
|
||||||
|
|
@ -172,3 +172,4 @@ keywords = Commande;Shell;Terminal;CLI;
|
||||||
tab-new-inherit-working-directory = Les nouveaux onglets et fenêtres utilisent le répertoire actuel
|
tab-new-inherit-working-directory = Les nouveaux onglets et fenêtres utilisent le répertoire actuel
|
||||||
tab-new-inherit-working-directory-description = Ouvrir les nouveaux onglets et fenêtres dans le répertoire de travail de l'onglet actif
|
tab-new-inherit-working-directory-description = Ouvrir les nouveaux onglets et fenêtres dans le répertoire de travail de l'onglet actif
|
||||||
show-pane-borders = Afficher les bordures des panneaux
|
show-pane-borders = Afficher les bordures des panneaux
|
||||||
|
unfocused-pane-dim = Atténuation des panneaux inactifs
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,10 @@ pub struct Config {
|
||||||
pub profiles: BTreeMap<ProfileId, Profile>,
|
pub profiles: BTreeMap<ProfileId, Profile>,
|
||||||
pub show_headerbar: bool,
|
pub show_headerbar: bool,
|
||||||
pub show_pane_borders: bool,
|
pub show_pane_borders: bool,
|
||||||
|
/// [yoda] Voile posé sur les panneaux non focalisés, en pourcentage
|
||||||
|
/// (`unfocused-split-opacity` de Ghostty : 0,7 d'opacité = 30 % de voile).
|
||||||
|
#[serde(default = "default_unfocused_pane_dim")]
|
||||||
|
pub unfocused_pane_dim: u8,
|
||||||
pub use_bright_bold: bool,
|
pub use_bright_bold: bool,
|
||||||
pub syntax_theme_dark: String,
|
pub syntax_theme_dark: String,
|
||||||
pub syntax_theme_light: String,
|
pub syntax_theme_light: String,
|
||||||
|
|
@ -243,6 +247,13 @@ pub struct Config {
|
||||||
pub shortcuts_custom: Shortcuts,
|
pub shortcuts_custom: Shortcuts,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Voile maximal des panneaux non focalisés : Ghostty borne leur opacité à 0,15.
|
||||||
|
pub const UNFOCUSED_PANE_DIM_MAX: u8 = 85;
|
||||||
|
|
||||||
|
fn default_unfocused_pane_dim() -> u8 {
|
||||||
|
30
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -262,6 +273,7 @@ impl Default for Config {
|
||||||
profiles: BTreeMap::new(),
|
profiles: BTreeMap::new(),
|
||||||
show_headerbar: true,
|
show_headerbar: true,
|
||||||
show_pane_borders: false,
|
show_pane_borders: false,
|
||||||
|
unfocused_pane_dim: default_unfocused_pane_dim(),
|
||||||
syntax_theme_dark: COSMIC_THEME_DARK.to_string(),
|
syntax_theme_dark: COSMIC_THEME_DARK.to_string(),
|
||||||
syntax_theme_light: COSMIC_THEME_LIGHT.to_string(),
|
syntax_theme_light: COSMIC_THEME_LIGHT.to_string(),
|
||||||
use_bright_bold: false,
|
use_bright_bold: false,
|
||||||
|
|
@ -343,6 +355,12 @@ impl Config {
|
||||||
Metrics::new(font_size, line_height)
|
Metrics::new(font_size, line_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opacité du voile des panneaux non focalisés ; `None` si désactivé.
|
||||||
|
pub fn unfocused_pane_dim_ratio(&self) -> Option<f32> {
|
||||||
|
let dim = self.unfocused_pane_dim.min(UNFOCUSED_PANE_DIM_MAX);
|
||||||
|
(dim > 0).then(|| f32::from(dim) / 100.0)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn opacity_ratio(&self) -> f32 {
|
pub fn opacity_ratio(&self) -> f32 {
|
||||||
f32::from(self.opacity) / 100.0
|
f32::from(self.opacity) / 100.0
|
||||||
}
|
}
|
||||||
|
|
@ -404,3 +422,22 @@ impl Config {
|
||||||
})[&self.font_stretch]
|
})[&self.font_stretch]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{Config, UNFOCUSED_PANE_DIM_MAX};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unfocused_pane_dim_ratio() {
|
||||||
|
let mut config = Config::default();
|
||||||
|
// Défaut de Ghostty : opacité 0,7, soit 30 % de voile
|
||||||
|
assert_eq!(config.unfocused_pane_dim_ratio(), Some(0.3));
|
||||||
|
config.unfocused_pane_dim = 0;
|
||||||
|
assert_eq!(config.unfocused_pane_dim_ratio(), None);
|
||||||
|
config.unfocused_pane_dim = 100;
|
||||||
|
assert_eq!(
|
||||||
|
config.unfocused_pane_dim_ratio(),
|
||||||
|
Some(f32::from(UNFOCUSED_PANE_DIM_MAX) / 100.0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
25
src/main.rs
25
src/main.rs
|
|
@ -591,6 +591,7 @@ pub enum Message {
|
||||||
ShowHeaderBar(bool),
|
ShowHeaderBar(bool),
|
||||||
SshConnect(usize),
|
SshConnect(usize),
|
||||||
ShowPaneBorders(bool),
|
ShowPaneBorders(bool),
|
||||||
|
UnfocusedPaneDim(u8),
|
||||||
|
|
||||||
SyntaxTheme(ColorSchemeKind, usize),
|
SyntaxTheme(ColorSchemeKind, usize),
|
||||||
SystemThemeChange,
|
SystemThemeChange,
|
||||||
|
|
@ -1729,6 +1730,15 @@ impl App {
|
||||||
.add(
|
.add(
|
||||||
widget::settings::item::builder(fl!("show-pane-borders"))
|
widget::settings::item::builder(fl!("show-pane-borders"))
|
||||||
.toggler(self.config.show_pane_borders, Message::ShowPaneBorders),
|
.toggler(self.config.show_pane_borders, Message::ShowPaneBorders),
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
widget::settings::item::builder(fl!("unfocused-pane-dim"))
|
||||||
|
.description(format!("{}%", self.config.unfocused_pane_dim))
|
||||||
|
.control(widget::slider(
|
||||||
|
0..=config::UNFOCUSED_PANE_DIM_MAX,
|
||||||
|
self.config.unfocused_pane_dim,
|
||||||
|
Message::UnfocusedPaneDim,
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let advanced_section = widget::settings::section()
|
let advanced_section = widget::settings::section()
|
||||||
|
|
@ -3371,6 +3381,12 @@ impl Application for App {
|
||||||
config_set!(show_pane_borders, show_pane_borders);
|
config_set!(show_pane_borders, show_pane_borders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Message::UnfocusedPaneDim(dim) => {
|
||||||
|
config_set!(
|
||||||
|
unfocused_pane_dim,
|
||||||
|
cmp::min(config::UNFOCUSED_PANE_DIM_MAX, dim)
|
||||||
|
);
|
||||||
|
}
|
||||||
Message::UseBrightBold(use_bright_bold) => {
|
Message::UseBrightBold(use_bright_bold) => {
|
||||||
if use_bright_bold != self.config.use_bright_bold {
|
if use_bright_bold != self.config.use_bright_bold {
|
||||||
config_set!(use_bright_bold, use_bright_bold);
|
config_set!(use_bright_bold, use_bright_bold);
|
||||||
|
|
@ -4247,6 +4263,15 @@ impl Application for App {
|
||||||
terminal_box = terminal_box.on_mouse_enter(move || Message::MouseEnter(pane));
|
terminal_box = terminal_box.on_mouse_enter(move || Message::MouseEnter(pane));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [yoda] Voile sur les panneaux non focalisés (à la Ghostty), seulement
|
||||||
|
// quand il y en a plusieurs : on voit d'un coup d'œil où l'on tape.
|
||||||
|
if let Some(dim) = self.config.unfocused_pane_dim_ratio()
|
||||||
|
&& self.pane_model.panes.panes.len() > 1
|
||||||
|
&& pane != self.pane_model.focused()
|
||||||
|
{
|
||||||
|
terminal_box = terminal_box.dim(dim);
|
||||||
|
}
|
||||||
|
|
||||||
// The terminal records what was under the right press; the widget opens the
|
// The terminal records what was under the right press; the widget opens the
|
||||||
// menu on release, so build it from that state
|
// menu on release, so build it from that state
|
||||||
let context_link = terminal
|
let context_link = terminal
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,7 @@ pub struct TerminalBox<'a, Message> {
|
||||||
on_context_menu: Option<Box<dyn Fn(Option<MenuState>) -> Message + 'a>>,
|
on_context_menu: Option<Box<dyn Fn(Option<MenuState>) -> Message + 'a>>,
|
||||||
on_mouse_enter: Option<Box<dyn Fn() -> Message + 'a>>,
|
on_mouse_enter: Option<Box<dyn Fn() -> Message + 'a>>,
|
||||||
opacity: Option<f32>,
|
opacity: Option<f32>,
|
||||||
|
dim: Option<f32>,
|
||||||
mouse_inside_boundary: Option<bool>,
|
mouse_inside_boundary: Option<bool>,
|
||||||
on_middle_click: Option<Box<dyn Fn() -> Message + 'a>>,
|
on_middle_click: Option<Box<dyn Fn() -> Message + 'a>>,
|
||||||
on_open_hyperlink: Option<Box<dyn Fn(String) -> Message + 'a>>,
|
on_open_hyperlink: Option<Box<dyn Fn(String) -> Message + 'a>>,
|
||||||
|
|
@ -147,6 +148,7 @@ where
|
||||||
on_context_menu: None,
|
on_context_menu: None,
|
||||||
on_mouse_enter: None,
|
on_mouse_enter: None,
|
||||||
opacity: None,
|
opacity: None,
|
||||||
|
dim: None,
|
||||||
mouse_inside_boundary: None,
|
mouse_inside_boundary: None,
|
||||||
on_middle_click: None,
|
on_middle_click: None,
|
||||||
key_binds,
|
key_binds,
|
||||||
|
|
@ -216,6 +218,13 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [yoda] Voile de la couleur du fond posé sur tout le panneau (texte
|
||||||
|
/// compris), d'opacité `amount` : panneau non focalisé.
|
||||||
|
pub fn dim(mut self, amount: f32) -> Self {
|
||||||
|
self.dim = Some(amount);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sharp_corners(mut self, sharp_corners: bool) -> Self {
|
pub fn sharp_corners(mut self, sharp_corners: bool) -> Self {
|
||||||
self.sharp_corners = sharp_corners;
|
self.sharp_corners = sharp_corners;
|
||||||
self
|
self
|
||||||
|
|
@ -915,6 +924,32 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [yoda] Panneau non focalisé : voile de la couleur du fond, comme
|
||||||
|
// `unfocused-split-opacity` de Ghostty. Calque à part, sinon le texte
|
||||||
|
// (primitive du même calque) serait tracé par-dessus.
|
||||||
|
if let Some(dim) = self.dim {
|
||||||
|
let bg = terminal.metadata_set[terminal.default_attrs().metadata].bg;
|
||||||
|
renderer.with_layer(layout.bounds(), |renderer| {
|
||||||
|
renderer.fill_quad(
|
||||||
|
Quad {
|
||||||
|
bounds: layout.bounds(),
|
||||||
|
border: Border {
|
||||||
|
radius: border_radius,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
snap: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Color::from_rgba(
|
||||||
|
f32::from(bg.r()) / 255.0,
|
||||||
|
f32::from(bg.g()) / 255.0,
|
||||||
|
f32::from(bg.b()) / 255.0,
|
||||||
|
dim,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let duration = instant.elapsed();
|
let duration = instant.elapsed();
|
||||||
log::trace!("redraw {}, {}: {:?}", view_w, view_h, duration);
|
log::trace!("redraw {}, {}: {:?}", view_w, view_h, duration);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue