feat: configure qt5ct and qt6ct
This commit is contained in:
parent
7ba564795a
commit
3b1452d354
4 changed files with 145 additions and 27 deletions
|
|
@ -29,3 +29,4 @@ cosmic-config = { path = "../cosmic-config/", default-features = false, features
|
||||||
] }
|
] }
|
||||||
dirs.workspace = true
|
dirs.workspace = true
|
||||||
thiserror = "2.0.16"
|
thiserror = "2.0.16"
|
||||||
|
configparser = "3.1.0"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ use crate::Theme;
|
||||||
/// Module for outputting the Cosmic gtk4 theme type as CSS
|
/// Module for outputting the Cosmic gtk4 theme type as CSS
|
||||||
pub mod gtk4_output;
|
pub mod gtk4_output;
|
||||||
|
|
||||||
|
/// Module for configuring qt5ct and qt6ct to use our qt theme
|
||||||
|
pub mod qt56ct_output;
|
||||||
/// Module for outputting the Cosmic qt theme type as kdeglobals
|
/// Module for outputting the Cosmic qt theme type as kdeglobals
|
||||||
pub mod qt_output;
|
pub mod qt_output;
|
||||||
|
|
||||||
|
|
@ -21,6 +23,8 @@ pub enum OutputError {
|
||||||
MissingDataDir,
|
MissingDataDir,
|
||||||
#[error("Serde Error: {0}")]
|
#[error("Serde Error: {0}")]
|
||||||
Serde(#[from] serde_json::Error),
|
Serde(#[from] serde_json::Error),
|
||||||
|
#[error("Ini Error: {0}")]
|
||||||
|
Ini(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
|
|
@ -28,9 +32,11 @@ impl Theme {
|
||||||
pub fn apply_exports(&self) -> Result<(), OutputError> {
|
pub fn apply_exports(&self) -> Result<(), OutputError> {
|
||||||
let gtk_res = Theme::apply_gtk(self.is_dark);
|
let gtk_res = Theme::apply_gtk(self.is_dark);
|
||||||
let qt_res = Theme::apply_qt(self.is_dark);
|
let qt_res = Theme::apply_qt(self.is_dark);
|
||||||
|
let qt56ct_res = Theme::apply_qt56ct(self.is_dark);
|
||||||
let vs_res = self.clone().apply_vs_code();
|
let vs_res = self.clone().apply_vs_code();
|
||||||
gtk_res?;
|
gtk_res?;
|
||||||
qt_res?;
|
qt_res?;
|
||||||
|
qt56ct_res?;
|
||||||
vs_res?;
|
vs_res?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -41,8 +47,10 @@ impl Theme {
|
||||||
pub fn apply_exports_static(is_dark: bool) -> Result<(), OutputError> {
|
pub fn apply_exports_static(is_dark: bool) -> Result<(), OutputError> {
|
||||||
let gtk_res = Theme::apply_gtk(is_dark);
|
let gtk_res = Theme::apply_gtk(is_dark);
|
||||||
let qt_res = Theme::apply_qt(is_dark);
|
let qt_res = Theme::apply_qt(is_dark);
|
||||||
|
let qt56ct_res = Theme::apply_qt56ct(is_dark);
|
||||||
gtk_res?;
|
gtk_res?;
|
||||||
qt_res?;
|
qt_res?;
|
||||||
|
qt56ct_res?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
113
cosmic-theme/src/output/qt56ct_output.rs
Normal file
113
cosmic-theme/src/output/qt56ct_output.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
use crate::Theme;
|
||||||
|
use configparser::ini::Ini;
|
||||||
|
use std::{
|
||||||
|
fs::{self, File},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::OutputError;
|
||||||
|
|
||||||
|
impl Theme {
|
||||||
|
/// The "version" of this theme.
|
||||||
|
///
|
||||||
|
/// To avoid repeatedly overwriting the user's config, we use a version system.
|
||||||
|
///
|
||||||
|
/// Increment this value when changes to qt{5,6}ct.conf are needed.
|
||||||
|
/// If the config's version is outdated, we update several sections.
|
||||||
|
/// Otherwise, only the light/dark mode is updated.
|
||||||
|
const COSMIC_QT_VERSION: u64 = 1;
|
||||||
|
|
||||||
|
/// Edits qt{5,6}ct.conf to use COSMIC styles if needed.
|
||||||
|
#[cold]
|
||||||
|
pub fn apply_qt56ct(is_dark: bool) -> Result<(), OutputError> {
|
||||||
|
let qt5ct_res = Self::apply_ct("qt5ct", is_dark);
|
||||||
|
let qt6ct_res = Self::apply_ct("qt6ct", is_dark);
|
||||||
|
qt5ct_res?;
|
||||||
|
qt6ct_res?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
#[must_use]
|
||||||
|
#[cold]
|
||||||
|
fn apply_ct(ct: &str, is_dark: bool) -> Result<(), OutputError> {
|
||||||
|
let path = Self::get_conf_path(ct)?;
|
||||||
|
let file_content = fs::read_to_string(&path).map_err(OutputError::Io)?;
|
||||||
|
let mut ini = Ini::new_cs();
|
||||||
|
ini.read(file_content).map_err(OutputError::Ini)?;
|
||||||
|
|
||||||
|
let old_version = ini
|
||||||
|
.getuint("Appearance", "cosmic_qt_version")
|
||||||
|
.map_err(OutputError::Ini)?
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let color_scheme_path = Self::get_qt_colors_path(is_dark)?;
|
||||||
|
let icon_theme = if is_dark { "breeze-dark" } else { "breeze" };
|
||||||
|
|
||||||
|
ini.set(
|
||||||
|
"Appearance",
|
||||||
|
"cosmic_qt_version",
|
||||||
|
Some(Theme::COSMIC_QT_VERSION.to_string()),
|
||||||
|
);
|
||||||
|
|
||||||
|
if old_version < Theme::COSMIC_QT_VERSION {
|
||||||
|
// Config is outdated, update it unconditionally!
|
||||||
|
|
||||||
|
ini.setstr(
|
||||||
|
"Appearance",
|
||||||
|
"color_scheme_path",
|
||||||
|
color_scheme_path.to_str(),
|
||||||
|
);
|
||||||
|
// Enable the above color scheme, instead of using the default color scheme of e.g. Breeze
|
||||||
|
ini.setstr("Appearance", "custom_palette", Some("true"));
|
||||||
|
// COSMIC icons are stuck in light mode, so use breeze icons instead
|
||||||
|
ini.setstr("Appearance", "icon_theme", Some(icon_theme));
|
||||||
|
// Use COSMIC dialogs instead of KDE's
|
||||||
|
ini.setstr("Appearance", "standard_dialogs", Some("xdgdesktopportal"));
|
||||||
|
|
||||||
|
// TODO: Add fonts section to match COSMIC
|
||||||
|
} else {
|
||||||
|
// Config is not outdated, check before updating light/dark mode only!
|
||||||
|
|
||||||
|
let old_color_scheme_path = ini
|
||||||
|
.get("Appearance", "color_scheme_path")
|
||||||
|
.unwrap_or_else(|| "CosmicPlease".to_owned());
|
||||||
|
if old_color_scheme_path.contains("Cosmic") {
|
||||||
|
ini.setstr(
|
||||||
|
"Appearance",
|
||||||
|
"color_scheme_path",
|
||||||
|
color_scheme_path.to_str(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let old_icon_theme = ini
|
||||||
|
.get("Appearance", "icon_theme")
|
||||||
|
.unwrap_or_else(|| "breeze".to_owned());
|
||||||
|
if old_icon_theme.contains("breeze") {
|
||||||
|
ini.setstr("Appearance", "icon_theme", Some(icon_theme));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ini.write(path).map_err(OutputError::Io)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the file paths of the form `~/.config/ct/ct.conf`:
|
||||||
|
/// e.g. `~/.config/qt6ct/qt6ct.conf`.
|
||||||
|
///
|
||||||
|
/// The file and its parent directory are created if they don't exist.
|
||||||
|
fn get_conf_path(ct: &str) -> Result<PathBuf, OutputError> {
|
||||||
|
let Some(mut config_dir) = dirs::config_dir() else {
|
||||||
|
return Err(OutputError::MissingConfigDir);
|
||||||
|
};
|
||||||
|
config_dir.push(&ct);
|
||||||
|
if !config_dir.exists() {
|
||||||
|
fs::create_dir_all(&config_dir).map_err(OutputError::Io)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_path = config_dir.join(ct.to_owned() + ".conf");
|
||||||
|
if !file_path.exists() {
|
||||||
|
File::create_new(&file_path).map_err(OutputError::Io)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(file_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -125,7 +125,7 @@ impl Theme {
|
||||||
} else {
|
} else {
|
||||||
"COSMIC Light"
|
"COSMIC Light"
|
||||||
};
|
};
|
||||||
// COSMIC icons are stuck in light mode, so use breeze instead
|
// COSMIC icons are stuck in light mode, so use breeze icons instead
|
||||||
let icons_theme = if self.is_dark {
|
let icons_theme = if self.is_dark {
|
||||||
"breeze-dark"
|
"breeze-dark"
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -207,22 +207,7 @@ widgetStyle=qt6ct-style
|
||||||
#[cold]
|
#[cold]
|
||||||
pub fn write_qt(&self) -> Result<(), OutputError> {
|
pub fn write_qt(&self) -> Result<(), OutputError> {
|
||||||
let colors = self.as_qt();
|
let colors = self.as_qt();
|
||||||
let Some(mut data_dir) = dirs::data_dir() else {
|
let file_path = Self::get_qt_colors_path(self.is_dark)?;
|
||||||
return Err(OutputError::MissingDataDir);
|
|
||||||
};
|
|
||||||
|
|
||||||
let file_name = if self.is_dark {
|
|
||||||
"CosmicDark.colors"
|
|
||||||
} else {
|
|
||||||
"CosmicLight.colors"
|
|
||||||
};
|
|
||||||
|
|
||||||
data_dir.push("color-schemes");
|
|
||||||
if !data_dir.exists() {
|
|
||||||
std::fs::create_dir_all(&data_dir).map_err(OutputError::Io)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let file_path = data_dir.join(file_name);
|
|
||||||
let tmp_file_path = file_path.with_extension("colors.new");
|
let tmp_file_path = file_path.with_extension("colors.new");
|
||||||
|
|
||||||
// Write to tmp_file_path first, then move it to file_path
|
// Write to tmp_file_path first, then move it to file_path
|
||||||
|
|
@ -251,16 +236,7 @@ widgetStyle=qt6ct-style
|
||||||
let Some(config_dir) = dirs::config_dir() else {
|
let Some(config_dir) = dirs::config_dir() else {
|
||||||
return Err(OutputError::MissingConfigDir);
|
return Err(OutputError::MissingConfigDir);
|
||||||
};
|
};
|
||||||
let Some(mut data_dir) = dirs::data_dir() else {
|
let src_file = Self::get_qt_colors_path(is_dark)?;
|
||||||
return Err(OutputError::MissingDataDir);
|
|
||||||
};
|
|
||||||
data_dir.push("color-schemes");
|
|
||||||
|
|
||||||
let src_file = data_dir.join(if is_dark {
|
|
||||||
"CosmicDark.colors"
|
|
||||||
} else {
|
|
||||||
"CosmicLight.colors"
|
|
||||||
});
|
|
||||||
|
|
||||||
let kdeglobals_file = config_dir.join("kdeglobals");
|
let kdeglobals_file = config_dir.join("kdeglobals");
|
||||||
|
|
||||||
|
|
@ -304,6 +280,26 @@ widgetStyle=qt6ct-style
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a path like `~/.config/color-schemes/CosmicDark.colors`
|
||||||
|
pub fn get_qt_colors_path(is_dark: bool) -> Result<PathBuf, OutputError> {
|
||||||
|
let Some(mut data_dir) = dirs::data_dir() else {
|
||||||
|
return Err(OutputError::MissingDataDir);
|
||||||
|
};
|
||||||
|
|
||||||
|
let file_name = if is_dark {
|
||||||
|
"CosmicDark.colors"
|
||||||
|
} else {
|
||||||
|
"CosmicLight.colors"
|
||||||
|
};
|
||||||
|
|
||||||
|
data_dir.push("color-schemes");
|
||||||
|
if !data_dir.exists() {
|
||||||
|
std::fs::create_dir_all(&data_dir).map_err(OutputError::Io)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(data_dir.join(file_name))
|
||||||
|
}
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
fn backup_non_cosmic_kdeglobals(path: &Path) -> io::Result<()> {
|
fn backup_non_cosmic_kdeglobals(path: &Path) -> io::Result<()> {
|
||||||
if !Self::is_cosmic_kdeglobals(path)?.unwrap_or(true) {
|
if !Self::is_cosmic_kdeglobals(path)?.unwrap_or(true) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue