Apply qt theme

This commit is contained in:
Adil Hanney 2026-02-09 22:23:09 +00:00
parent 4fb4542d69
commit 3712406972
No known key found for this signature in database
2 changed files with 130 additions and 2 deletions

View file

@ -17,6 +17,8 @@ pub enum OutputError {
Io(std::io::Error), Io(std::io::Error),
#[error("Missing config directory")] #[error("Missing config directory")]
MissingConfigDir, MissingConfigDir,
#[error("Missing data directory")]
MissingDataDir,
#[error("Serde Error: {0}")] #[error("Serde Error: {0}")]
Serde(#[from] serde_json::Error), Serde(#[from] serde_json::Error),
} }

View file

@ -14,7 +14,7 @@ impl Theme {
#[cold] #[cold]
/// Some high-level documentation for this file can be found at: /// Some high-level documentation for this file can be found at:
/// https://web.archive.org/web/20250402234329/https://docs.kde.org/stable5/en/plasma-workspace/kcontrol/colors/ /// https://web.archive.org/web/20250402234329/https://docs.kde.org/stable5/en/plasma-workspace/kcontrol/colors/
pub fn as_qt_color_scheme(&self) -> String { pub fn as_qt(&self) -> String {
let mut output = "# GENERATED BY COSMIC\n".to_string(); let mut output = "# GENERATED BY COSMIC\n".to_string();
// Usually, disabled elements will have strongly reduced contrast and are often notably darker or lighter // Usually, disabled elements will have strongly reduced contrast and are often notably darker or lighter
@ -179,10 +179,136 @@ inactiveForeground=161,169,177
output output
} }
/// Write the color scheme to the appropriate directory.
/// Should be written in `~/.local/share/color-schemes/`.
///
/// See the docs: https://develop.kde.org/docs/plasma/#color-scheme
///
/// # Errors
///
/// Returns an `OutputError` if there is an error writing the colors file.
#[cold]
pub fn write_qt(&self) -> Result<(), OutputError> {
let colors = self.as_qt();
let Some(mut data_dir) = dirs::data_dir() else {
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 mut file = File::create(data_dir.join(file_name)).map_err(OutputError::Io)?;
file.write_all(colors.as_bytes()).map_err(OutputError::Io)?;
Ok(())
}
/// Apply the color scheme by copying it to `~/.config/kdeglobals`.
///
/// See the docs: https://develop.kde.org/docs/plasma/#color-scheme
///
/// # Errors
///
/// Returns an `OutputError` if there is an error applying the color scheme.
#[cold]
pub fn apply_qt(is_dark: bool) -> Result<(), OutputError> {
let Some(config_dir) = dirs::config_dir() else {
return Err(OutputError::MissingConfigDir);
};
let Some(mut data_dir) = dirs::data_dir() else {
return Err(OutputError::MissingDataDir);
};
data_dir.push("color-schemes");
let src_file = data_dir.join(if is_dark {
"CosmicDark.colors"
} else {
"CosmicLight.colors"
});
let dest_file = config_dir.join("kdeglobals");
#[cfg(target_family = "unix")]
{
use std::os::unix::fs::symlink;
Self::backup_non_cosmic_kdeglobals(&dest_file).map_err(OutputError::Io)?;
if dest_file.exists() {
// Currently we overwrite the file with our symlink.
// Maybe in the future we could merge kdeglobals with our color scheme ini?
fs::remove_file(&dest_file).map_err(OutputError::Io)?;
}
symlink(src_file, dest_file).map_err(OutputError::Io)?
}
Ok(())
}
/// Reset the applied qt colors by deleting the `~/.config/kdeglobals` file.
///
/// This does not restore the backed up kdeglobals file.
///
/// # Errors
///
/// Returns an `OutputError` if there is an error resetting the CSS file.
#[cold]
pub fn reset_qt() -> Result<(), OutputError> {
let Some(config_dir) = dirs::config_dir() else {
return Err(OutputError::MissingConfigDir);
};
let dest_file = config_dir.join("kdeglobals");
if Self::is_cosmic_kdeglobals(&dest_file).map_err(OutputError::Io)?.unwrap_or_default() {
fs::remove_file(dest_file).map_err(OutputError::Io)?
}
Ok(())
}
#[cold]
fn backup_non_cosmic_kdeglobals(path: &Path) -> io::Result<()> {
if !Self::is_cosmic_kdeglobals(path)?.unwrap_or(true) {
let backup_path = path.with_extension("bak");
fs::rename(path, &backup_path)?;
}
Ok(())
}
#[cold]
fn is_cosmic_kdeglobals(path: &Path) -> io::Result<Option<bool>> {
if !path.exists() {
return Ok(None);
}
if let Ok(metadata) = fs::symlink_metadata(path) {
if !metadata.file_type().is_symlink() {
return Ok(Some(false));
}
let Ok(target) = fs::read_link(path) else {
return Ok(Some(false));
};
let Some(target_filename) = target.file_name() else {
return Ok(Some(false));
};
return Ok(Some(
target_filename == "CosmicDark.colors" || target_filename == "CosmicLight.colors",
));
}
Ok(Some(false))
}
} }
/// Formats a color in the form `r,g,b` e.g. `255,255,255` /// Formats a color in the form `r,g,b` e.g. `255,255,255`
pub fn to_rgb(c: Srgba) -> String { fn to_rgb(c: Srgba) -> String {
let c_u8: Rgba<palette::encoding::Srgb, u8> = c.into_format(); let c_u8: Rgba<palette::encoding::Srgb, u8> = c.into_format();
format!("{},{},{}", c_u8.red, c_u8.green, c_u8.blue) format!("{},{},{}", c_u8.red, c_u8.green, c_u8.blue)
} }