diff --git a/i18n/en/cosmic_term.ftl b/i18n/en/cosmic_term.ftl index cc36317..f6079aa 100644 --- a/i18n/en/cosmic_term.ftl +++ b/i18n/en/cosmic_term.ftl @@ -22,6 +22,7 @@ light = Light syntax-dark = Color scheme dark syntax-light = Color scheme light default-zoom-step = Zoom steps +opacity = Background opacity ### Font font = Font diff --git a/src/config.rs b/src/config.rs index 95c788d..dd0427c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -70,6 +70,7 @@ pub struct Config { pub bold_font_weight: u16, pub font_stretch: u16, pub font_size_zoom_step_mul_100: u16, + pub opacity: u8, pub profiles: BTreeMap, pub show_headerbar: bool, pub use_bright_bold: bool, @@ -90,6 +91,7 @@ impl Default for Config { font_size_zoom_step_mul_100: 100, font_stretch: Stretch::Normal.to_number(), font_weight: Weight::NORMAL.0, + opacity: 100, profiles: BTreeMap::new(), show_headerbar: true, syntax_theme_dark: "COSMIC Dark".to_string(), @@ -114,6 +116,10 @@ impl Config { 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 pub fn profile_names(&self) -> Vec<(String, ProfileId)> { let mut profile_names = Vec::<(String, ProfileId)>::with_capacity(self.profiles.len()); diff --git a/src/main.rs b/src/main.rs index da64fb0..64a6883 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ use cosmic::{ use cosmic_text::{fontdb::FaceInfo, Family, Stretch, Weight}; use std::{ any::TypeId, + cmp, collections::{BTreeMap, BTreeSet, HashMap}, env, process, sync::{atomic::Ordering, Mutex}, @@ -266,6 +267,7 @@ pub enum Message { PaneResized(pane_grid::ResizeEvent), Modifiers(Modifiers), MouseEnter(pane_grid::Pane), + Opacity(u8), Paste(Option), PasteValue(Option, String), ProfileCollapse(ProfileId), @@ -728,8 +730,14 @@ impl App { 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")) .add( @@ -934,6 +942,7 @@ impl Application for App { /// Creates the application, and optionally emits command on initialize. fn init(mut core: Core, flags: Self::Flags) -> (Self, Command) { + core.window.content_container = false; core.window.show_headerbar = flags.config.show_headerbar; // Update font name from config @@ -1106,6 +1115,33 @@ impl Application for App { /// Handle application events here. fn update(&mut self, message: Self::Message) -> Command { + // 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.[](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 { Message::AppTheme(app_theme) => { self.config.app_theme = app_theme; @@ -1291,6 +1327,9 @@ impl Application for App { self.pane_model.focus = pane; return self.update_focus(); } + Message::Opacity(opacity) => { + config_set!(opacity, cmp::min(100, opacity)); + } Message::PaneClicked(pane) => { self.pane_model.focus = pane; return self.update_title(Some(pane)); @@ -1443,10 +1482,7 @@ impl Application for App { } } Message::FocusFollowMouse(focus_follow_mouse) => { - if focus_follow_mouse != self.config.focus_follow_mouse { - self.config.focus_follow_mouse = focus_follow_mouse; - return self.save_config(); - } + config_set!(focus_follow_mouse, focus_follow_mouse); } Message::SystemThemeChange => { return self.update_config(); @@ -1751,10 +1787,11 @@ impl Application for App { fn view(&self) -> Element { let cosmic_theme = self.core().system_theme().cosmic(); let cosmic_theme::Spacing { space_xxs, .. } = cosmic_theme.spacing; + { // Update terminal window color //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 data = (bytes[2] as u32) | ((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 mut tab_column = widget::column::with_capacity(1); @@ -1798,9 +1836,13 @@ impl Application for App { .unwrap_or_else(widget::Id::unique); match tab_model.data::>(entity) { Some(terminal) => { - let mut terminal_box = terminal_box(terminal).id(terminal_id).on_context_menu( - move |position_opt| Message::TabContextMenu(pane, position_opt), - ); + let mut terminal_box = terminal_box(terminal) + .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 { terminal_box = @@ -1891,11 +1933,11 @@ impl Application for App { }) .width(Length::Fill) .height(Length::Fill) - .spacing(space_xxs) .on_click(Message::PaneClicked) .on_resize(space_xxs, Message::PaneResized) .on_drag(Message::PaneDragged); + //TODO: apply window border radius xs at bottom of window pane_grid.into() } diff --git a/src/terminal_box.rs b/src/terminal_box.rs index e5f944f..0d6d7b8 100644 --- a/src/terminal_box.rs +++ b/src/terminal_box.rs @@ -44,11 +44,13 @@ use crate::{terminal::Metadata, Terminal, TerminalScroll}; pub struct TerminalBox<'a, Message> { terminal: &'a Mutex, id: Option, + border: Border, padding: Padding, click_timing: Duration, context_menu: Option, on_context_menu: Option) -> Message + 'a>>, on_mouse_enter: Option Message + 'a>>, + opacity: Option, mouse_inside_boundary: Option, } @@ -60,11 +62,13 @@ where Self { terminal, id: None, + border: Border::default(), padding: Padding::new(0.0), click_timing: Duration::from_millis(500), context_menu: None, on_context_menu: None, on_mouse_enter: None, + opacity: None, mouse_inside_boundary: None, } } @@ -74,6 +78,11 @@ where self } + pub fn border>(mut self, border: B) -> Self { + self.border = border.into(); + self + } + pub fn padding>(mut self, padding: P) -> Self { self.padding = padding.into(); self @@ -101,6 +110,11 @@ where self.on_mouse_enter = Some(Box::new(on_mouse_enter)); self } + + pub fn opacity(mut self, opacity: f32) -> Self { + self.opacity = Some(opacity); + self + } } pub fn terminal_box(terminal: &Mutex) -> TerminalBox<'_, Message> @@ -248,17 +262,18 @@ where renderer.fill_quad( Quad { - bounds: Rectangle::new( - view_position, - Size::new(view_w as f32 + scrollbar_w, view_h as f32), - ), + bounds: layout.bounds(), + border: self.border, ..Default::default() }, Color::new( background_color.r() as f32 / 255.0, background_color.g() 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, is_focused: bool, ) { - if self.metadata == self.default_metadata { - return; - } - let cosmic_text_to_iced_color = |color: cosmic_text::Color| { Color::new( color.r() as f32 / 255.0, @@ -339,11 +350,13 @@ where } let metadata = &self.metadata_set[self.metadata]; - let color = shade(metadata.bg, is_focused); - renderer.fill_quad( - mk_quad!(mk_pos_offset!(0.0, self.line_height), self.line_height), - cosmic_text_to_iced_color(color), - ); + if metadata.bg != self.metadata_set[self.default_metadata].bg { + let color = shade(metadata.bg, is_focused); + renderer.fill_quad( + mk_quad!(mk_pos_offset!(0.0, self.line_height), self.line_height), + cosmic_text_to_iced_color(color), + ); + } if !metadata.flags.is_empty() { let style_line_height =