feat(ui): add configurable Iced themes
This commit is contained in:
parent
73545520c8
commit
4bf14baea4
52 changed files with 3058 additions and 5665 deletions
|
|
@ -18,6 +18,8 @@ use crate::ui::window::plot::PlotDialogState;
|
|||
pub struct AppConfig {
|
||||
/// Input modes, backup, plugin lists, viewport background colours, …
|
||||
pub settings: UserSettings,
|
||||
/// Iced theme selection and the six base colours used by a custom theme.
|
||||
pub theme: UiThemeConfig,
|
||||
/// Recent-files list + retained count.
|
||||
pub recent: RecentConfig,
|
||||
/// Last selected section on the tabbed Start page.
|
||||
|
|
@ -35,6 +37,7 @@ impl Default for AppConfig {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
settings: UserSettings::default(),
|
||||
theme: UiThemeConfig::default(),
|
||||
recent: RecentConfig::default(),
|
||||
start: StartConfig::default(),
|
||||
statusbar: StatusBarConfig::default(),
|
||||
|
|
@ -44,6 +47,136 @@ impl Default for AppConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct UiThemeConfig {
|
||||
pub name: String,
|
||||
pub palette: UiThemePalette,
|
||||
}
|
||||
|
||||
impl Default for UiThemeConfig {
|
||||
fn default() -> Self {
|
||||
let theme = iced::Theme::Dark;
|
||||
Self {
|
||||
name: theme.to_string(),
|
||||
palette: UiThemePalette::from_iced(theme.palette()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UiThemeConfig {
|
||||
pub fn to_iced(&self) -> iced::Theme {
|
||||
if self.name == "Custom" {
|
||||
iced::Theme::custom("Custom", self.palette.to_iced())
|
||||
} else {
|
||||
builtin_theme(&self.name).unwrap_or(iced::Theme::Dark)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct UiThemePalette {
|
||||
pub background: [u8; 3],
|
||||
pub text: [u8; 3],
|
||||
pub primary: [u8; 3],
|
||||
pub success: [u8; 3],
|
||||
pub warning: [u8; 3],
|
||||
pub danger: [u8; 3],
|
||||
}
|
||||
|
||||
impl Default for UiThemePalette {
|
||||
fn default() -> Self {
|
||||
Self::from_iced(iced::Theme::Dark.palette())
|
||||
}
|
||||
}
|
||||
|
||||
impl UiThemePalette {
|
||||
pub fn from_iced(palette: iced::theme::Palette) -> Self {
|
||||
Self {
|
||||
background: color_to_rgb(palette.background),
|
||||
text: color_to_rgb(palette.text),
|
||||
primary: color_to_rgb(palette.primary),
|
||||
success: color_to_rgb(palette.success),
|
||||
warning: color_to_rgb(palette.warning),
|
||||
danger: color_to_rgb(palette.danger),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_iced(self) -> iced::theme::Palette {
|
||||
iced::theme::Palette {
|
||||
background: rgb_to_color(self.background),
|
||||
text: rgb_to_color(self.text),
|
||||
primary: rgb_to_color(self.primary),
|
||||
success: rgb_to_color(self.success),
|
||||
warning: rgb_to_color(self.warning),
|
||||
danger: rgb_to_color(self.danger),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hex_values(self) -> [String; 6] {
|
||||
[
|
||||
rgb_to_hex(self.background),
|
||||
rgb_to_hex(self.text),
|
||||
rgb_to_hex(self.primary),
|
||||
rgb_to_hex(self.success),
|
||||
rgb_to_hex(self.warning),
|
||||
rgb_to_hex(self.danger),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn set_hex(&mut self, index: usize, value: &str) -> bool {
|
||||
let Some(rgb) = parse_hex(value) else {
|
||||
return false;
|
||||
};
|
||||
match index {
|
||||
0 => self.background = rgb,
|
||||
1 => self.text = rgb,
|
||||
2 => self.primary = rgb,
|
||||
3 => self.success = rgb,
|
||||
4 => self.warning = rgb,
|
||||
5 => self.danger = rgb,
|
||||
_ => return false,
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn builtin_theme(name: &str) -> Option<iced::Theme> {
|
||||
iced::Theme::ALL
|
||||
.iter()
|
||||
.find(|theme| theme.to_string() == name)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn color_to_rgb(color: iced::Color) -> [u8; 3] {
|
||||
[
|
||||
(color.r * 255.0).round() as u8,
|
||||
(color.g * 255.0).round() as u8,
|
||||
(color.b * 255.0).round() as u8,
|
||||
]
|
||||
}
|
||||
|
||||
fn rgb_to_color(rgb: [u8; 3]) -> iced::Color {
|
||||
iced::Color::from_rgb8(rgb[0], rgb[1], rgb[2])
|
||||
}
|
||||
|
||||
fn rgb_to_hex(rgb: [u8; 3]) -> String {
|
||||
format!("#{:02X}{:02X}{:02X}", rgb[0], rgb[1], rgb[2])
|
||||
}
|
||||
|
||||
fn parse_hex(value: &str) -> Option<[u8; 3]> {
|
||||
let value = value.trim().strip_prefix('#').unwrap_or(value.trim());
|
||||
if value.len() != 6 {
|
||||
return None;
|
||||
}
|
||||
Some([
|
||||
u8::from_str_radix(&value[0..2], 16).ok()?,
|
||||
u8::from_str_radix(&value[2..4], 16).ok()?,
|
||||
u8::from_str_radix(&value[4..6], 16).ok()?,
|
||||
])
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct RecentConfig {
|
||||
|
|
|
|||
Loading…
Reference in a new issue