180 lines
5.4 KiB
Rust
180 lines
5.4 KiB
Rust
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
use cosmic::{
|
|
cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry},
|
|
theme,
|
|
};
|
|
use cosmic_text::{Metrics, Stretch, Weight};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use std::collections::BTreeMap;
|
|
use std::sync::OnceLock;
|
|
|
|
use crate::fl;
|
|
|
|
pub const CONFIG_VERSION: u64 = 1;
|
|
|
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
|
pub enum AppTheme {
|
|
Dark,
|
|
Light,
|
|
System,
|
|
}
|
|
|
|
impl AppTheme {
|
|
pub fn theme(&self) -> theme::Theme {
|
|
match self {
|
|
Self::Dark => theme::Theme::dark(),
|
|
Self::Light => theme::Theme::light(),
|
|
Self::System => theme::system_preference(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
|
#[serde(transparent)]
|
|
pub struct ProfileId(pub u64);
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
|
pub struct Profile {
|
|
pub name: String,
|
|
#[serde(default)]
|
|
pub command: String,
|
|
#[serde(default)]
|
|
pub syntax_theme_dark: String,
|
|
#[serde(default)]
|
|
pub syntax_theme_light: String,
|
|
#[serde(default)]
|
|
pub tab_title: String,
|
|
}
|
|
|
|
impl Default for Profile {
|
|
fn default() -> Self {
|
|
Self {
|
|
name: fl!("new-profile"),
|
|
command: String::new(),
|
|
syntax_theme_dark: "COSMIC Dark".to_string(),
|
|
syntax_theme_light: "COSMIC Light".to_string(),
|
|
tab_title: String::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
|
pub struct Config {
|
|
pub app_theme: AppTheme,
|
|
pub font_name: String,
|
|
pub font_size: u16,
|
|
pub font_weight: u16,
|
|
pub dim_font_weight: u16,
|
|
pub bold_font_weight: u16,
|
|
pub font_stretch: u16,
|
|
pub font_size_zoom_step_mul_100: u16,
|
|
pub opacity: u8,
|
|
pub profiles: BTreeMap<ProfileId, Profile>,
|
|
pub show_headerbar: bool,
|
|
pub use_bright_bold: bool,
|
|
pub syntax_theme_dark: String,
|
|
pub syntax_theme_light: String,
|
|
pub focus_follow_mouse: bool,
|
|
}
|
|
|
|
impl Default for Config {
|
|
fn default() -> Self {
|
|
Self {
|
|
app_theme: AppTheme::System,
|
|
bold_font_weight: Weight::BOLD.0,
|
|
dim_font_weight: Weight::NORMAL.0,
|
|
focus_follow_mouse: false,
|
|
font_name: "Fira Mono".to_string(),
|
|
font_size: 14,
|
|
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(),
|
|
syntax_theme_light: "COSMIC Light".to_string(),
|
|
use_bright_bold: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Config {
|
|
fn font_size_adjusted(&self, zoom_adj: i8) -> f32 {
|
|
let font_size = f32::from(self.font_size).max(1.0);
|
|
let adj = f32::from(zoom_adj);
|
|
let adj_step = f32::from(self.font_size_zoom_step_mul_100) / 100.0;
|
|
(font_size + adj * adj_step).max(1.0)
|
|
}
|
|
|
|
// Calculate metrics from font size
|
|
pub fn metrics(&self, zoom_adj: i8) -> Metrics {
|
|
let font_size = self.font_size_adjusted(zoom_adj);
|
|
let line_height = (font_size * 1.4).ceil();
|
|
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());
|
|
for (profile_id, profile) in self.profiles.iter() {
|
|
let mut name = profile.name.clone();
|
|
|
|
let mut copies = 1;
|
|
while profile_names.iter().find(|x| x.0 == name).is_some() {
|
|
copies += 1;
|
|
name = format!("{} ({})", profile.name, copies);
|
|
}
|
|
|
|
profile_names.push((name, *profile_id));
|
|
}
|
|
profile_names.sort_by(|a, b| lexical_sort::natural_lexical_cmp(&a.0, &b.0));
|
|
profile_names
|
|
}
|
|
|
|
// Get current syntax theme based on dark mode
|
|
pub fn syntax_theme(&self, profile_id_opt: Option<ProfileId>) -> &str {
|
|
let dark = self.app_theme.theme().theme_type.is_dark();
|
|
match profile_id_opt.and_then(|profile_id| self.profiles.get(&profile_id)) {
|
|
Some(profile) => {
|
|
if dark {
|
|
&profile.syntax_theme_dark
|
|
} else {
|
|
&profile.syntax_theme_light
|
|
}
|
|
}
|
|
None => {
|
|
if dark {
|
|
&self.syntax_theme_dark
|
|
} else {
|
|
&self.syntax_theme_light
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn typed_font_stretch(&self) -> Stretch {
|
|
macro_rules! populate_num_typed_map {
|
|
($($stretch:ident,)+) => {
|
|
let mut map = BTreeMap::new();
|
|
$(map.insert(Stretch::$stretch.to_number(), Stretch::$stretch);)+
|
|
map
|
|
};
|
|
}
|
|
|
|
static NUM_TO_TYPED_MAP: OnceLock<BTreeMap<u16, Stretch>> = OnceLock::new();
|
|
|
|
NUM_TO_TYPED_MAP.get_or_init(|| {
|
|
populate_num_typed_map! {
|
|
UltraCondensed, ExtraCondensed, Condensed, SemiCondensed,
|
|
Normal, SemiExpanded, Expanded, ExtraExpanded, UltraExpanded,
|
|
}
|
|
})[&self.font_stretch]
|
|
}
|
|
}
|