From c0d1ab1fb80f27280e021010f8c383bc05e4f749 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 5 Jun 2024 08:51:26 -0600 Subject: [PATCH] Include builtin cosmic dark and light theme in color schemes list, fixes #206 --- Cargo.lock | 3 +- src/main.rs | 231 +++++++++++++++++++++++++----------------- src/menu.rs | 65 +++++++----- src/terminal_theme.rs | 4 +- 4 files changed, 180 insertions(+), 123 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b90112f..b26d2a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1208,13 +1208,14 @@ dependencies = [ [[package]] name = "cosmic-text" version = "0.11.2" -source = "git+https://github.com/pop-os/cosmic-text.git#5e82de11cf5c977ee82a5d631c05382f4acc7314" +source = "git+https://github.com/pop-os/cosmic-text.git#89503b254fb6a2014d3712568054b32925ea9008" dependencies = [ "bitflags 2.5.0", "fontdb", "libm", "log", "rangemap", + "rayon", "rustc-hash", "rustybuzz", "self_cell 1.0.4", diff --git a/src/main.rs b/src/main.rs index 1908721..815c121 100644 --- a/src/main.rs +++ b/src/main.rs @@ -272,9 +272,9 @@ pub enum Message { AppTheme(AppTheme), ColorSchemeCollapse, ColorSchemeDelete(ColorSchemeKind, ColorSchemeId), - ColorSchemeExpand(ColorSchemeKind, ColorSchemeId), - ColorSchemeExport(ColorSchemeKind, ColorSchemeId), - ColorSchemeExportResult(ColorSchemeKind, ColorSchemeId, DialogResult), + ColorSchemeExpand(ColorSchemeKind, Option), + ColorSchemeExport(ColorSchemeKind, Option), + ColorSchemeExportResult(ColorSchemeKind, Option, DialogResult), ColorSchemeImport(ColorSchemeKind), ColorSchemeImportResult(ColorSchemeKind, DialogResult), ColorSchemeRename(ColorSchemeKind, ColorSchemeId, String), @@ -401,7 +401,7 @@ pub struct App { startup_options: Option, term_config: term::Config, color_scheme_errors: Vec, - color_scheme_expanded: Option<(ColorSchemeKind, ColorSchemeId)>, + color_scheme_expanded: Option<(ColorSchemeKind, Option)>, color_scheme_renaming: Option<(ColorSchemeKind, ColorSchemeId, String)>, color_scheme_rename_id: widget::Id, color_scheme_tab_model: widget::segmented_button::SingleSelectModel, @@ -696,65 +696,67 @@ impl App { .into(), ); - if !self.config.color_schemes(color_scheme_kind).is_empty() { - let mut section = widget::settings::view_section(""); - for (color_scheme_name, color_scheme_id) in - self.config.color_scheme_names(color_scheme_kind) - { - let expanded = - self.color_scheme_expanded == Some((color_scheme_kind, color_scheme_id)); - let renaming = match &self.color_scheme_renaming { - Some((kind, id, value)) - if kind == &color_scheme_kind && id == &color_scheme_id => - { - Some(value) - } - _ => None, - }; - - let button = if expanded { - widget::button(icon_cache_get("view-more-symbolic", 16)) - .on_press(Message::ColorSchemeCollapse) - } else { - widget::button(icon_cache_get("view-more-symbolic", 16)).on_press( - Message::ColorSchemeExpand(color_scheme_kind, color_scheme_id), - ) + let mut section = widget::settings::view_section(""); + let builtin_name = format!("COSMIC {:?}", color_scheme_kind); + let color_scheme_names = self.config.color_scheme_names(color_scheme_kind); + for (color_scheme_name, color_scheme_id_opt) in std::iter::once((builtin_name, None)).chain( + color_scheme_names + .into_iter() + .map(|(name, id)| (name, Some(id))), + ) { + let expanded = + self.color_scheme_expanded == Some((color_scheme_kind, color_scheme_id_opt)); + let renaming = match &self.color_scheme_renaming { + Some((kind, id, value)) + if kind == &color_scheme_kind && Some(id) == color_scheme_id_opt.as_ref() => + { + Some(value) } - .style(style::Button::Icon); + _ => None, + }; - let mut popover = widget::popover(button); - if expanded { - let menu = menu::color_scheme_menu( - color_scheme_kind, - color_scheme_id, - &color_scheme_name, - ); - popover = popover - .popup(menu) - .position(widget::popover::Position::Bottom); - } - - let item = match renaming { - Some(value) => widget::settings::item_row(vec![ - widget::text_input("", value) - .id(self.color_scheme_rename_id.clone()) - .on_input(move |value| { - Message::ColorSchemeRename( - color_scheme_kind, - color_scheme_id, - value, - ) - }) - .on_submit(Message::ColorSchemeRenameSubmit) - .into(), - popover.into(), - ]), - None => widget::settings::item::builder(color_scheme_name).control(popover), - }; - section = section.add(item); + let button = if expanded { + widget::button(icon_cache_get("view-more-symbolic", 16)) + .on_press(Message::ColorSchemeCollapse) + } else { + widget::button(icon_cache_get("view-more-symbolic", 16)).on_press( + Message::ColorSchemeExpand(color_scheme_kind, color_scheme_id_opt), + ) } - sections.push(section.into()); + .style(style::Button::Icon); + + let mut popover = widget::popover(button); + if expanded { + let menu = menu::color_scheme_menu( + color_scheme_kind, + color_scheme_id_opt, + &color_scheme_name, + ); + popover = popover + .popup(menu) + .position(widget::popover::Position::Bottom); + } + + let item = match renaming { + Some(value) => widget::settings::item_row(vec![ + widget::text_input("", value) + .id(self.color_scheme_rename_id.clone()) + .on_input(move |value| { + Message::ColorSchemeRename( + color_scheme_kind, + color_scheme_id_opt.expect("trying to rename builtin color scheme"), + value, + ) + }) + .on_submit(Message::ColorSchemeRenameSubmit) + .into(), + popover.into(), + ]), + None => widget::settings::item::builder(color_scheme_name).control(popover), + }; + section = section.add(item); } + sections.push(section.into()); sections.push( widget::row::with_children(vec![ @@ -1520,24 +1522,27 @@ impl Application for App { .remove(&color_scheme_id); return self.save_color_schemes(color_scheme_kind); } - Message::ColorSchemeExport(color_scheme_kind, color_scheme_id) => { + Message::ColorSchemeExport(color_scheme_kind, color_scheme_id_opt) => { self.color_scheme_expanded = None; - if let Some(color_scheme) = self - .config - .color_schemes(color_scheme_kind) - .get(&color_scheme_id) - { + if let Some(color_scheme_name) = match color_scheme_id_opt { + Some(color_scheme_id) => self + .config + .color_schemes(color_scheme_kind) + .get(&color_scheme_id) + .map(|color_scheme| color_scheme.name.clone()), + None => Some(format!("COSMIC {:?}", color_scheme_kind)), + } { if self.dialog_opt.is_none() { let (dialog, command) = Dialog::new( DialogKind::SaveFile { - filename: format!("{}.ron", color_scheme.name), + filename: format!("{}.ron", color_scheme_name), }, None, Message::DialogMessage, move |result| { Message::ColorSchemeExportResult( color_scheme_kind, - color_scheme_id, + color_scheme_id_opt, result, ) }, @@ -1547,45 +1552,85 @@ impl Application for App { } } } - Message::ColorSchemeExportResult(color_scheme_kind, color_scheme_id, result) => { + Message::ColorSchemeExportResult(color_scheme_kind, color_scheme_id_opt, result) => { //TODO: show errors in UI self.dialog_opt = None; if let DialogResult::Open(paths) = result { let path = &paths[0]; - if let Some(color_scheme) = self - .config - .color_schemes(color_scheme_kind) - .get(&color_scheme_id) - { - match ron::ser::to_string_pretty( - &color_scheme, - ron::ser::PrettyConfig::new(), - ) { - Ok(ron) => { - if let Err(err) = fs::write(path, ron) { + match color_scheme_id_opt { + Some(color_scheme_id) => { + if let Some(color_scheme) = self + .config + .color_schemes(color_scheme_kind) + .get(&color_scheme_id) + { + match ron::ser::to_string_pretty( + &color_scheme, + ron::ser::PrettyConfig::new(), + ) { + Ok(ron) => { + if let Err(err) = fs::write(path, ron) { + log::error!( + "failed to export {:?} to {:?}: {}", + color_scheme_id, + path, + err + ); + } + } + Err(err) => { + log::error!( + "failed to serialize color scheme {:?}: {}", + color_scheme_id, + err + ); + } + } + } else { + log::error!("failed to find color scheme {:?}", color_scheme_id); + } + } + None => { + let name = format!("COSMIC {:?}", color_scheme_kind); + let color_scheme = match color_scheme_kind { + ColorSchemeKind::Dark => ColorScheme::from(( + name.as_str(), + &terminal_theme::cosmic_dark(), + )), + ColorSchemeKind::Light => ColorScheme::from(( + name.as_str(), + &terminal_theme::cosmic_light(), + )), + }; + //TODO: do not duplicate code + match ron::ser::to_string_pretty( + &color_scheme, + ron::ser::PrettyConfig::new(), + ) { + Ok(ron) => { + if let Err(err) = fs::write(path, ron) { + log::error!( + "failed to export {:?} to {:?}: {}", + color_scheme.name, + path, + err + ); + } + } + Err(err) => { log::error!( - "failed to export {:?} to {:?}: {}", - color_scheme_id, - path, + "failed to serialize color scheme {:?}: {}", + color_scheme.name, err ); } } - Err(err) => { - log::error!( - "failed to serialize color scheme {:?}: {}", - color_scheme_id, - err - ); - } } - } else { - log::error!("failed to find color scheme {:?}", color_scheme_id); } } } - Message::ColorSchemeExpand(color_scheme_kind, color_scheme_id) => { - self.color_scheme_expanded = Some((color_scheme_kind, color_scheme_id)); + Message::ColorSchemeExpand(color_scheme_kind, color_scheme_id_opt) => { + self.color_scheme_expanded = Some((color_scheme_kind, color_scheme_id_opt)); } Message::ColorSchemeImport(color_scheme_kind) => { if self.dialog_opt.is_none() { diff --git a/src/menu.rs b/src/menu.rs index c7f341d..e0d8b37 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -97,39 +97,50 @@ pub fn context_menu<'a>( pub fn color_scheme_menu<'a>( kind: ColorSchemeKind, - id: ColorSchemeId, + id_opt: Option, name: &str, ) -> Element<'a, Message> { let menu_item = |label, message| menu_button(vec![widget::text(label).into()]).on_press(message); - widget::container(column!( - menu_item( + let mut column = widget::column::with_capacity(if id_opt.is_some() { 3 } else { 1 }); + if let Some(id) = id_opt { + column = column.push(menu_item( fl!("rename"), - Message::ColorSchemeRename(kind, id, name.to_string()) - ), - menu_item(fl!("export"), Message::ColorSchemeExport(kind, id)), - menu_item(fl!("delete"), Message::ColorSchemeDelete(kind, id)), - )) - .padding(1) - //TODO: move style to libcosmic - .style(theme::Container::custom(|theme| { - let cosmic = theme.cosmic(); - let component = &cosmic.background.component; - widget::container::Appearance { - icon_color: Some(component.on.into()), - text_color: Some(component.on.into()), - background: Some(Background::Color(component.base.into())), - border: Border { - radius: 8.0.into(), - width: 1.0, - color: component.divider.into(), - }, - ..Default::default() - } - })) - .width(Length::Fixed(120.0)) - .into() + Message::ColorSchemeRename(kind, id, name.to_string()), + )); + } + column = column.push(menu_item( + fl!("export"), + Message::ColorSchemeExport(kind, id_opt), + )); + if let Some(id) = id_opt { + column = column.push(menu_item( + fl!("delete"), + Message::ColorSchemeDelete(kind, id), + )); + } + + widget::container(column) + .padding(1) + //TODO: move style to libcosmic + .style(theme::Container::custom(|theme| { + let cosmic = theme.cosmic(); + let component = &cosmic.background.component; + widget::container::Appearance { + icon_color: Some(component.on.into()), + text_color: Some(component.on.into()), + background: Some(Background::Color(component.base.into())), + border: Border { + radius: 8.0.into(), + width: 1.0, + color: component.divider.into(), + }, + ..Default::default() + } + })) + .width(Length::Fixed(120.0)) + .into() } pub fn menu_bar<'a>(config: &Config, key_binds: &HashMap) -> Element<'a, Message> { diff --git a/src/terminal_theme.rs b/src/terminal_theme.rs index 8df1dff..760334d 100644 --- a/src/terminal_theme.rs +++ b/src/terminal_theme.rs @@ -248,7 +248,7 @@ impl From<(&str, &Colors)> for ColorScheme { } } -fn cosmic_dark() -> Colors { +pub fn cosmic_dark() -> Colors { let mut colors = auto_colors(); let encode_rgb = |data: u32| -> Rgb { @@ -289,7 +289,7 @@ fn cosmic_dark() -> Colors { colors } -fn cosmic_light() -> Colors { +pub fn cosmic_light() -> Colors { let mut colors = auto_colors(); let encode_rgb = |data: u32| -> Rgb {