Support opacity

This commit is contained in:
Jeremy Soller 2024-02-12 12:36:30 -07:00
parent c93032ab9d
commit 72c97fd5fc
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
4 changed files with 86 additions and 24 deletions

View file

@ -22,6 +22,7 @@ light = Light
syntax-dark = Color scheme dark syntax-dark = Color scheme dark
syntax-light = Color scheme light syntax-light = Color scheme light
default-zoom-step = Zoom steps default-zoom-step = Zoom steps
opacity = Background opacity
### Font ### Font
font = Font font = Font

View file

@ -70,6 +70,7 @@ pub struct Config {
pub bold_font_weight: u16, pub bold_font_weight: u16,
pub font_stretch: u16, pub font_stretch: u16,
pub font_size_zoom_step_mul_100: u16, pub font_size_zoom_step_mul_100: u16,
pub opacity: u8,
pub profiles: BTreeMap<ProfileId, Profile>, pub profiles: BTreeMap<ProfileId, Profile>,
pub show_headerbar: bool, pub show_headerbar: bool,
pub use_bright_bold: bool, pub use_bright_bold: bool,
@ -90,6 +91,7 @@ impl Default for Config {
font_size_zoom_step_mul_100: 100, font_size_zoom_step_mul_100: 100,
font_stretch: Stretch::Normal.to_number(), font_stretch: Stretch::Normal.to_number(),
font_weight: Weight::NORMAL.0, font_weight: Weight::NORMAL.0,
opacity: 100,
profiles: BTreeMap::new(), profiles: BTreeMap::new(),
show_headerbar: true, show_headerbar: true,
syntax_theme_dark: "COSMIC Dark".to_string(), syntax_theme_dark: "COSMIC Dark".to_string(),
@ -114,6 +116,10 @@ impl Config {
Metrics::new(font_size, line_height) Metrics::new(font_size, line_height)
} }
pub fn opacity_ratio(&self) -> f32 {
(self.opacity as f32) / 100.0
}
// Get a sorted and adjusted for duplicates list of profiles names and ids // Get a sorted and adjusted for duplicates list of profiles names and ids
pub fn profile_names(&self) -> Vec<(String, ProfileId)> { pub fn profile_names(&self) -> Vec<(String, ProfileId)> {
let mut profile_names = Vec::<(String, ProfileId)>::with_capacity(self.profiles.len()); let mut profile_names = Vec::<(String, ProfileId)>::with_capacity(self.profiles.len());

View file

@ -23,6 +23,7 @@ use cosmic::{
use cosmic_text::{fontdb::FaceInfo, Family, Stretch, Weight}; use cosmic_text::{fontdb::FaceInfo, Family, Stretch, Weight};
use std::{ use std::{
any::TypeId, any::TypeId,
cmp,
collections::{BTreeMap, BTreeSet, HashMap}, collections::{BTreeMap, BTreeSet, HashMap},
env, process, env, process,
sync::{atomic::Ordering, Mutex}, sync::{atomic::Ordering, Mutex},
@ -266,6 +267,7 @@ pub enum Message {
PaneResized(pane_grid::ResizeEvent), PaneResized(pane_grid::ResizeEvent),
Modifiers(Modifiers), Modifiers(Modifiers),
MouseEnter(pane_grid::Pane), MouseEnter(pane_grid::Pane),
Opacity(u8),
Paste(Option<segmented_button::Entity>), Paste(Option<segmented_button::Entity>),
PasteValue(Option<segmented_button::Entity>, String), PasteValue(Option<segmented_button::Entity>, String),
ProfileCollapse(ProfileId), ProfileCollapse(ProfileId),
@ -728,8 +730,14 @@ impl App {
Message::DefaultZoomStep(index) Message::DefaultZoomStep(index)
}), }),
), ),
)
.add(
widget::settings::item::builder(fl!("opacity"))
.description(format!("{}%", self.config.opacity))
.control(widget::slider(0..=100, self.config.opacity, |opacity| {
Message::Opacity(opacity)
})),
); );
//TODO: background opacity
let mut font_section = widget::settings::view_section(fl!("font")) let mut font_section = widget::settings::view_section(fl!("font"))
.add( .add(
@ -934,6 +942,7 @@ impl Application for App {
/// Creates the application, and optionally emits command on initialize. /// Creates the application, and optionally emits command on initialize.
fn init(mut core: Core, flags: Self::Flags) -> (Self, Command<Self::Message>) { fn init(mut core: Core, flags: Self::Flags) -> (Self, Command<Self::Message>) {
core.window.content_container = false;
core.window.show_headerbar = flags.config.show_headerbar; core.window.show_headerbar = flags.config.show_headerbar;
// Update font name from config // Update font name from config
@ -1106,6 +1115,33 @@ impl Application for App {
/// Handle application events here. /// Handle application events here.
fn update(&mut self, message: Self::Message) -> Command<Self::Message> { fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
// Helper for updating config values efficiently
macro_rules! config_set {
($name: ident, $value: expr) => {
match &self.config_handler {
Some(config_handler) => {
match paste::paste! { self.config.[<set_ $name>](config_handler, $value) } {
Ok(_) => {}
Err(err) => {
log::warn!(
"failed to save config {:?}: {}",
stringify!($name),
err
);
}
}
}
None => {
self.config.$name = $value;
log::warn!(
"failed to save config {:?}: no config handler",
stringify!($name)
);
}
}
};
}
match message { match message {
Message::AppTheme(app_theme) => { Message::AppTheme(app_theme) => {
self.config.app_theme = app_theme; self.config.app_theme = app_theme;
@ -1291,6 +1327,9 @@ impl Application for App {
self.pane_model.focus = pane; self.pane_model.focus = pane;
return self.update_focus(); return self.update_focus();
} }
Message::Opacity(opacity) => {
config_set!(opacity, cmp::min(100, opacity));
}
Message::PaneClicked(pane) => { Message::PaneClicked(pane) => {
self.pane_model.focus = pane; self.pane_model.focus = pane;
return self.update_title(Some(pane)); return self.update_title(Some(pane));
@ -1443,10 +1482,7 @@ impl Application for App {
} }
} }
Message::FocusFollowMouse(focus_follow_mouse) => { Message::FocusFollowMouse(focus_follow_mouse) => {
if focus_follow_mouse != self.config.focus_follow_mouse { config_set!(focus_follow_mouse, focus_follow_mouse);
self.config.focus_follow_mouse = focus_follow_mouse;
return self.save_config();
}
} }
Message::SystemThemeChange => { Message::SystemThemeChange => {
return self.update_config(); return self.update_config();
@ -1751,10 +1787,11 @@ impl Application for App {
fn view(&self) -> Element<Self::Message> { fn view(&self) -> Element<Self::Message> {
let cosmic_theme = self.core().system_theme().cosmic(); let cosmic_theme = self.core().system_theme().cosmic();
let cosmic_theme::Spacing { space_xxs, .. } = cosmic_theme.spacing; let cosmic_theme::Spacing { space_xxs, .. } = cosmic_theme.spacing;
{ {
// Update terminal window color // Update terminal window color
//TODO: do this only when theme changes? //TODO: do this only when theme changes?
let color = Color::from(cosmic_theme.bg_color()); let color = Color::from(cosmic_theme.background.base);
let bytes = color.into_rgba8(); let bytes = color.into_rgba8();
let data = (bytes[2] as u32) let data = (bytes[2] as u32)
| ((bytes[1] as u32) << 8) | ((bytes[1] as u32) << 8)
@ -1773,6 +1810,7 @@ impl Application for App {
} }
} }
} }
let pane_grid = PaneGrid::new(&self.pane_model.panes, |pane, tab_model, _is_maximized| { let pane_grid = PaneGrid::new(&self.pane_model.panes, |pane, tab_model, _is_maximized| {
let mut tab_column = widget::column::with_capacity(1); let mut tab_column = widget::column::with_capacity(1);
@ -1798,9 +1836,13 @@ impl Application for App {
.unwrap_or_else(widget::Id::unique); .unwrap_or_else(widget::Id::unique);
match tab_model.data::<Mutex<Terminal>>(entity) { match tab_model.data::<Mutex<Terminal>>(entity) {
Some(terminal) => { Some(terminal) => {
let mut terminal_box = terminal_box(terminal).id(terminal_id).on_context_menu( let mut terminal_box = terminal_box(terminal)
move |position_opt| Message::TabContextMenu(pane, position_opt), .id(terminal_id)
); .on_context_menu(move |position_opt| {
Message::TabContextMenu(pane, position_opt)
})
.opacity(self.config.opacity_ratio())
.padding(space_xxs);
if self.config.focus_follow_mouse { if self.config.focus_follow_mouse {
terminal_box = terminal_box =
@ -1891,11 +1933,11 @@ impl Application for App {
}) })
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill)
.spacing(space_xxs)
.on_click(Message::PaneClicked) .on_click(Message::PaneClicked)
.on_resize(space_xxs, Message::PaneResized) .on_resize(space_xxs, Message::PaneResized)
.on_drag(Message::PaneDragged); .on_drag(Message::PaneDragged);
//TODO: apply window border radius xs at bottom of window
pane_grid.into() pane_grid.into()
} }

View file

@ -44,11 +44,13 @@ use crate::{terminal::Metadata, Terminal, TerminalScroll};
pub struct TerminalBox<'a, Message> { pub struct TerminalBox<'a, Message> {
terminal: &'a Mutex<Terminal>, terminal: &'a Mutex<Terminal>,
id: Option<Id>, id: Option<Id>,
border: Border,
padding: Padding, padding: Padding,
click_timing: Duration, click_timing: Duration,
context_menu: Option<Point>, context_menu: Option<Point>,
on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>, on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
on_mouse_enter: Option<Box<dyn Fn() -> Message + 'a>>, on_mouse_enter: Option<Box<dyn Fn() -> Message + 'a>>,
opacity: Option<f32>,
mouse_inside_boundary: Option<bool>, mouse_inside_boundary: Option<bool>,
} }
@ -60,11 +62,13 @@ where
Self { Self {
terminal, terminal,
id: None, id: None,
border: Border::default(),
padding: Padding::new(0.0), padding: Padding::new(0.0),
click_timing: Duration::from_millis(500), click_timing: Duration::from_millis(500),
context_menu: None, context_menu: None,
on_context_menu: None, on_context_menu: None,
on_mouse_enter: None, on_mouse_enter: None,
opacity: None,
mouse_inside_boundary: None, mouse_inside_boundary: None,
} }
} }
@ -74,6 +78,11 @@ where
self self
} }
pub fn border<B: Into<Border>>(mut self, border: B) -> Self {
self.border = border.into();
self
}
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
self.padding = padding.into(); self.padding = padding.into();
self self
@ -101,6 +110,11 @@ where
self.on_mouse_enter = Some(Box::new(on_mouse_enter)); self.on_mouse_enter = Some(Box::new(on_mouse_enter));
self self
} }
pub fn opacity(mut self, opacity: f32) -> Self {
self.opacity = Some(opacity);
self
}
} }
pub fn terminal_box<Message>(terminal: &Mutex<Terminal>) -> TerminalBox<'_, Message> pub fn terminal_box<Message>(terminal: &Mutex<Terminal>) -> TerminalBox<'_, Message>
@ -248,17 +262,18 @@ where
renderer.fill_quad( renderer.fill_quad(
Quad { Quad {
bounds: Rectangle::new( bounds: layout.bounds(),
view_position, border: self.border,
Size::new(view_w as f32 + scrollbar_w, view_h as f32),
),
..Default::default() ..Default::default()
}, },
Color::new( Color::new(
background_color.r() as f32 / 255.0, background_color.r() as f32 / 255.0,
background_color.g() as f32 / 255.0, background_color.g() as f32 / 255.0,
background_color.b() as f32 / 255.0, background_color.b() as f32 / 255.0,
background_color.a() as f32 / 255.0, match self.opacity {
Some(opacity) => opacity,
None => background_color.a() as f32 / 255.0,
},
), ),
); );
} }
@ -301,10 +316,6 @@ where
renderer: &mut Renderer, renderer: &mut Renderer,
is_focused: bool, is_focused: bool,
) { ) {
if self.metadata == self.default_metadata {
return;
}
let cosmic_text_to_iced_color = |color: cosmic_text::Color| { let cosmic_text_to_iced_color = |color: cosmic_text::Color| {
Color::new( Color::new(
color.r() as f32 / 255.0, color.r() as f32 / 255.0,
@ -339,11 +350,13 @@ where
} }
let metadata = &self.metadata_set[self.metadata]; let metadata = &self.metadata_set[self.metadata];
let color = shade(metadata.bg, is_focused); if metadata.bg != self.metadata_set[self.default_metadata].bg {
renderer.fill_quad( let color = shade(metadata.bg, is_focused);
mk_quad!(mk_pos_offset!(0.0, self.line_height), self.line_height), renderer.fill_quad(
cosmic_text_to_iced_color(color), mk_quad!(mk_pos_offset!(0.0, self.line_height), self.line_height),
); cosmic_text_to_iced_color(color),
);
}
if !metadata.flags.is_empty() { if !metadata.flags.is_empty() {
let style_line_height = let style_line_height =