From ba7bc67b6cef1451153f6f71eb9401216ec62973 Mon Sep 17 00:00:00 2001 From: 2ndbeam <2ndbeam@disroot.org> Date: Fri, 10 Apr 2026 18:54:19 +0300 Subject: [PATCH] feat: Reworked configuration - Added colors.aliases table - Colors can also be defined with aliases - Event color is calculated with priorities - Added paths table - Renamed log_path as paths.logs - Renamed colors.background_text as colors.text - Added colors.fallback field - Default values are now taken from source config.toml on compilation --- Cargo.lock | 49 ++++++++++++++------ Cargo.toml | 2 +- build.rs | 1 - config.toml | 93 ++++++++++++++++++------------------- src/config.rs | 104 ------------------------------------------ src/config/color.rs | 99 ++++++++++++++++++++++++++++++++++++++++ src/config/default.rs | 53 +++++++++++++++++++++ src/config/mod.rs | 57 +++++++++++++++++++++++ src/lib.rs | 8 +--- src/log.rs | 14 +++--- src/main.rs | 76 +++++++++++++++++------------- ui/theme.slint | 38 --------------- ui/timeline.slint | 9 ++-- 13 files changed, 347 insertions(+), 256 deletions(-) delete mode 100644 src/config.rs create mode 100644 src/config/color.rs create mode 100644 src/config/default.rs create mode 100644 src/config/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b44fe7e..c585b42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,7 +161,7 @@ dependencies = [ "serde", "slint", "slint-build", - "toml", + "toml 1.1.2+spec-1.1.0", ] [[package]] @@ -722,9 +722,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.59" +version = "1.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ "find-msvc-tools", "jobserver", @@ -1685,9 +1685,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" dependencies = [ "color_quant", "weezl", @@ -1841,6 +1841,12 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + [[package]] name = "heck" version = "0.5.0" @@ -2333,12 +2339,12 @@ checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" [[package]] name = "indexmap" -version = "2.13.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -2588,14 +2594,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ "bitflags 2.11.0", "libc", "plain", - "redox_syscall 0.7.3", + "redox_syscall 0.7.4", ] [[package]] @@ -3822,9 +3828,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" dependencies = [ "bitflags 2.11.0", ] @@ -4174,7 +4180,7 @@ dependencies = [ "regex", "serde_json", "tar", - "toml", + "toml 0.9.12+spec-1.1.0", ] [[package]] @@ -4680,6 +4686,21 @@ dependencies = [ "winnow 0.7.15", ] +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.1", +] + [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0b1de00..8a492a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" chrono = "0.4.42" serde = "1.0.219" slint = "1.15.1" -toml = "0.9.5" +toml = "1.1.2" [build-dependencies] slint-build = "1.15.1" diff --git a/build.rs b/build.rs index 237d430..0ac588d 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,4 @@ fn main() { - let mut style = String::from("cosmic"); #[cfg(all(feature = "dark", feature = "light"))] diff --git a/config.toml b/config.toml index 1bfb6b9..93d766b 100644 --- a/config.toml +++ b/config.toml @@ -1,53 +1,50 @@ -# This is the default config for Aliveline. -# Note: All colors are of format 0xAARRGGBB +# Default Aliveline config -# Path where logs are saved. May be relative to config dir or absolute. -log_path = "logs" +# Paths may be relative to config directory or absolute +[paths] +logs = "logs" -# Colors used for events. For now Aliveline expects to have exactly 16 colors, but this is subject to change in future. -event_colors = [ - 0xff_97f9f9, - 0xff_a4def9, - 0xff_c1e0f7, - 0xff_cfbae1, - 0xff_c59fc9, - 0xff_4e3d42, - 0xff_c9d5b5, - 0xff_2d82b7, - 0xff_556f44, - 0xff_772e25, - 0xff_c44536, - 0xff_7c6a0a, - 0xff_babd8d, - 0xff_ffdac6, - 0xff_fa9500, - 0xff_eb6424 -] +# Colors may be defined either as alias (string) or as 0xAARRGGBB (integer) -# Colors used for event colors. Aliveline expects it to have same size as events. -text_colors = [ - 0xff_000000, - 0xff_000000, - 0xff_000000, - 0xff_000000, - 0xff_000000, - 0xff_ffffff, - 0xff_000000, - 0xff_000000, - 0xff_000000, - 0xff_ffffff, - 0xff_000000, - 0xff_000000, - 0xff_000000, - 0xff_000000, - 0xff_000000, - 0xff_000000 -] +# Aliases must be declared here, otherwise fallback color is used +[colors.aliases] +background = 0xFF_808080 +timeline = 0xFF_A9A9A9 +black = 0xFF_000000 +white = 0xFF_FFFFFF +"very aggressive pink color to use with fallback so you definitely notice it" = 0xFF_FF00E7 [colors] -# Color behind the timeline -background = 0xFF_808080 -# Color of the base timeline -timeline = 0xFF_a9a9a9 -# Color of background text (timestamps, event names, etc.) -background_text = 0xFF_000000 +# Timeline background +background = "background" +# Timeline foreground +timeline = "timeline" +# Background text (timestamps, event names, etc.) +text = "black" +# Used when alias was not found +fallback = "very aggressive pink color to use with fallback so you definitely notice it" + +# Event colors are chosen pseudorandomly from this array, respecting each color's priority +# Event color consists of: +# - Background color (default: black) +# - Text color (default: same as colors.text) +# - Priority (default: 1) +# Full example: { background = "color", text = "color", priority = 1337 } +events = [ + { background = 0xff_97f9f9 }, + { background = 0xff_a4def9 }, + { background = 0xff_c1e0f7 }, + { background = 0xff_cfbae1 }, + { background = 0xff_c59fc9 }, + { background = 0xff_4e3d42, text = "white" }, + { background = 0xff_c9d5b5 }, + { background = 0xff_2d82b7 }, + { background = 0xff_556f44 }, + { background = 0xff_772e25, text = "white" }, + { background = 0xff_c44536 }, + { background = 0xff_7c6a0a }, + { background = 0xff_babd8d }, + { background = 0xff_ffdac6 }, + { background = 0xff_fa9500 }, + { background = 0xff_eb6424 }, +] diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index 22ce814..0000000 --- a/src/config.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::path::PathBuf; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)] -#[serde(default)] -pub struct Colors { - pub background: u32, - pub timeline: u32, - pub background_text: u32, -} - -impl Default for Colors { - fn default() -> Self { - Colors { - background: 0xff_808080, - timeline: 0xff_a9a9a9, - background_text: 0xff_000000, - } - } -} - -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] -pub struct Config { - /// directory, where config is located - #[serde(skip)] - pub conf_path: PathBuf, - pub log_path: PathBuf, - #[serde(default)] - pub colors: Colors, - #[serde(default)] - pub event_colors: Vec, - #[serde(default)] - pub text_colors: Vec, -} - -impl Default for Config { - fn default() -> Self { - let conf_path = PathBuf::new(); - let colors: Colors = Default::default(); - let event_colors: Vec = vec![ - 0xff_97f9f9, - 0xff_a4def9, - 0xff_c1e0f7, - 0xff_cfbae1, - 0xff_c59fc9, - 0xff_4e3d42, - 0xff_c9d5b5, - 0xff_2d82b7, - 0xff_556f44, - 0xff_772e25, - 0xff_c44536, - 0xff_7c6a0a, - 0xff_babd8d, - 0xff_ffdac6, - 0xff_fa9500, - 0xff_eb6424 - ]; - let text_colors: Vec = vec![ - 0xff000000, - 0xff000000, - 0xff000000, - 0xff000000, - 0xff000000, - 0xffffffff, - 0xff000000, - 0xff000000, - 0xff000000, - 0xffffffff, - 0xff000000, - 0xff000000, - 0xff000000, - 0xff000000, - 0xff000000, - 0xff000000 - ]; - Config { - conf_path, - log_path: PathBuf::from("./logs"), - colors, - event_colors, - text_colors, - } - } -} - -impl Config { - pub fn new(conf_path: PathBuf) -> Self { - let conf_dir: PathBuf = conf_path.parent().unwrap().into(); - Config { - conf_path: conf_dir, - ..Default::default() - } - } - - pub fn load(path: PathBuf) -> Option { - if let Ok(toml_string) = std::fs::read_to_string(path.clone()) { - if let Ok(mut conf) = toml::from_str::(&toml_string) { - conf.conf_path = path.parent().unwrap().into(); - return Some(conf); - } - } - None - } -} diff --git a/src/config/color.rs b/src/config/color.rs new file mode 100644 index 0000000..b819aa2 --- /dev/null +++ b/src/config/color.rs @@ -0,0 +1,99 @@ +use std::{collections::HashMap, hash::{DefaultHasher, Hash, Hasher}}; +use serde::{Serialize, Deserialize}; +use slint::Color; + +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] +#[serde(untagged)] +pub enum ConfigColor { + Raw(u32), + Alias(String), +} + +impl Default for ConfigColor { + fn default() -> Self { + Self::Raw(0xFF000000) + } +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] +#[serde(default)] +pub struct EventColor { + pub background: ConfigColor, + pub text: Option, + pub priority: u64, +} + +impl Default for EventColor { + fn default() -> Self { + Self { + background: ConfigColor::default(), + text: None, + priority: 1, + } + } +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] +#[serde(default)] +pub struct Colors { + pub aliases: HashMap, + + pub background: ConfigColor, + pub timeline: ConfigColor, + pub text: ConfigColor, + pub fallback: ConfigColor, + + pub events: Vec, +} + +impl Default for Colors { + fn default() -> Self { + super::default::default_config().colors.into() + } +} + +#[inline(always)] +fn argb(value: u32) -> Color { + Color::from_argb_encoded(value) +} + +impl Colors { + #[inline(always)] + pub fn fallback_color(&self) -> Color { + self.try_get_color(&self.fallback).unwrap_or_default() + } + + pub fn try_get_color(&self, color: &ConfigColor) -> Option { + match color { + ConfigColor::Raw(color) => Some(argb(*color)), + ConfigColor::Alias(alias) => self.aliases.get(alias).map(|c| argb(*c)), + } + } + + pub fn get_color(&self, color: &ConfigColor) -> Color { + self.try_get_color(color).unwrap_or_else(|| self.fallback_color()) + } + + pub fn event_color_for(&self, text: &str) -> &EventColor { + let priority_sum: u64 = self.events.iter().map(|e| e.priority).sum(); + + let mut s = DefaultHasher::new(); + text.hash(&mut s); + let hash = s.finish(); + + let mut chosen = hash % priority_sum; + + for (id, color) in self.events.iter().enumerate() { + match chosen.checked_sub(color.priority) { + Some(new_count) => { + chosen = new_count; + }, + None => { + return &self.events[id]; + }, + } + } + + unreachable!() + } +} diff --git a/src/config/default.rs b/src/config/default.rs new file mode 100644 index 0000000..73b0588 --- /dev/null +++ b/src/config/default.rs @@ -0,0 +1,53 @@ +use std::collections::HashMap; + +use crate::config::color::{ConfigColor, EventColor}; + +use super::*; + +const DEFAULT_CFG: &'static str = include_str!("../../config.toml"); + +#[derive(Deserialize)] +pub(super) struct DefaultConfig { + pub(super) colors: DefaultColors, + pub(super) paths: DefaultPaths, +} + +#[derive(Deserialize)] +pub struct DefaultColors { + pub aliases: HashMap, + + pub background: ConfigColor, + pub timeline: ConfigColor, + pub text: ConfigColor, + pub fallback: ConfigColor, + + pub events: Vec, +} + +impl From for Colors { + fn from(value: DefaultColors) -> Self { + Self { + aliases: value.aliases, + background: value.background, + timeline: value.timeline, + text: value.text, + fallback: value.fallback, + events: value.events + } + } +} + +#[derive(Deserialize)] +pub struct DefaultPaths { + pub logs: PathBuf, +} + +impl From for Paths { + fn from(value: DefaultPaths) -> Self { + Self { logs: value.logs } + } +} + +pub(super) fn default_config() -> DefaultConfig { + toml::de::from_str(DEFAULT_CFG).unwrap() +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..cf5a592 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,57 @@ +use std::path::PathBuf; +use serde::{Deserialize, Serialize}; +use color::Colors; + +pub mod color; +mod default; + +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] +#[serde(default)] +pub struct Paths { + pub logs: PathBuf, +} + +impl Default for Paths { + fn default() -> Self { + default::default_config().paths.into() + } +} + +#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq, Debug)] +#[serde(default)] +pub struct Config { + /// directory, where config is located + #[serde(skip)] + pub conf_path: PathBuf, + pub colors: Colors, + pub paths: Paths, +} + +impl Config { + pub fn new(conf_path: PathBuf) -> Self { + let conf_dir: PathBuf = conf_path.parent().unwrap().into(); + Config { + conf_path: conf_dir, + ..Default::default() + } + } + + fn replace_missing_fields(&mut self) { + let default = Config::default(); + + if self.colors.events.len() == 0 { + self.colors.events = default.colors.events; + } + } + + pub fn load(path: PathBuf) -> Option { + if let Ok(toml_string) = std::fs::read_to_string(path.clone()) { + if let Ok(mut conf) = toml::from_str::(&toml_string) { + conf.replace_missing_fields(); + conf.conf_path = path.parent().unwrap().into(); + return Some(conf); + } + } + None + } +} diff --git a/src/lib.rs b/src/lib.rs index df734c9..0740372 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ use config::Config; -use std::{hash::{DefaultHasher, Hash, Hasher}, path::PathBuf}; +use std::path::PathBuf; pub mod config; pub mod log; @@ -45,9 +45,3 @@ pub fn load_config() -> Config { println!("Using config path ./config.toml"); Config::new(PathBuf::from("./config.toml")) } -/// Get random-like color id in range 0..16 by computing string hash -pub fn color_id_from_name(name: String) -> i32 { - let mut s = DefaultHasher::new(); - name.hash(&mut s); - let hash = s.finish(); - (hash % 16) as i32 } diff --git a/src/log.rs b/src/log.rs index 8f0bcbb..9d42ad1 100644 --- a/src/log.rs +++ b/src/log.rs @@ -46,12 +46,12 @@ impl Log { } fn get_log_dir(config: &Config) -> PathBuf { - if config.log_path.is_relative() { + if config.paths.logs.is_relative() { let mut path = config.conf_path.clone(); - path.push(&config.log_path); + path.push(&config.paths.logs); return path; } else { - return config.log_path.clone(); + return config.paths.logs.clone(); } } @@ -77,14 +77,14 @@ impl Event { let start = Time { hour: (start / 3600) as u8, minute: ((start / 3600) / 60) as u8, - second: (start % 60) as u8, - nanosecond: 0 + second: Some((start % 60) as u8), + nanosecond: None, }; let end = Time { hour: (end / 3600) as u8, minute: ((end % 3600) / 60) as u8, - second: (end % 60) as u8, - nanosecond: 0 + second: Some((end % 60) as u8), + nanosecond: None, }; Event { name, start, end, finished } } diff --git a/src/main.rs b/src/main.rs index dd5c9a3..3b8ba68 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,27 +3,40 @@ use std::{error::Error, rc::Rc, sync::{Arc, Mutex}}; -use aliveline::{color_id_from_name, config::Config, load_config, log::{Event, Log}}; +use aliveline::{config::{Config, color::Colors}, load_config, log::{Event, Log}}; use chrono::{Datelike, Timelike}; use slint::{Color, Model, ModelRc, SharedString, ToSharedString, VecModel, Weak}; use toml::value::{Date as TomlDate, Time}; slint::include_modules!(); -impl From for TimelineEvent { - fn from(event: Event) -> Self { +impl TimelineEvent { + pub fn colors(colors: &Colors, text: &str) -> (Color, Color) { + let event_colors = colors.event_color_for(text); + let background_color = colors.get_color(&event_colors.background); + let text_color = match &event_colors.text { + Some(color) => colors.get_color(color), + None => colors.get_color(&colors.text), + }; + (background_color, text_color) + } + + pub fn from_event(event: Event, colors: &Colors) -> Self { let start = (event.start.hour as i32) * 3600 + (event.start.minute as i32) * 60 - + (event.start.second as i32); + + (event.start.second.unwrap() as i32); let end = (event.end.hour as i32) * 3600 + (event.end.minute as i32) * 60 - + (event.end.second as i32); + + (event.end.second.unwrap() as i32); + let (background_color, text_color) = TimelineEvent::colors(colors, event.name.as_str()); + TimelineEvent { start, duration: end - start, label: event.name.to_shared_string(), finished: event.finished, - color_id: color_id_from_name(event.name) + background_color, + text_color, } } } @@ -33,27 +46,27 @@ impl From for Event { let start = Time { hour: (event.start / 3600) as u8, minute: ((event.start % 3600) / 60) as u8, - second: (event.start % 60) as u8, - nanosecond: 0 + second: Some((event.start % 60) as u8), + nanosecond: None, }; let endsecs = event.start + event.duration; let end = Time { hour: (endsecs / 3600) as u8, minute: ((endsecs % 3600) / 60) as u8, - second: (endsecs % 60) as u8, - nanosecond: 0 + second: Some((endsecs % 60) as u8), + nanosecond: None, }; Event { start, end, name: event.label.to_string(), finished: event.finished } } } -fn load_log(ui_weak: Weak, log: Arc>) { +fn load_log(ui_weak: Weak, log: Arc>, config: Arc) { let ui = ui_weak.unwrap(); let log_guard = log.lock().expect("Log shouldn't be used twice"); let events: Vec = (*log_guard) .events .iter() - .map(|event| TimelineEvent::from((*event).clone())) + .map(|event| TimelineEvent::from_event(event.clone(), &config.colors)) .collect(); let in_progress = events.iter().any(|event| !event.finished); let model: ModelRc = Rc::new(VecModel::from(events)).into(); @@ -67,21 +80,9 @@ fn load_log(ui_weak: Weak, log: Arc>) { fn load_colors(ui_weak: Weak, config: Arc) { let ui = ui_weak.unwrap(); let pal = ui.global::(); - pal.set_background(Color::from_argb_encoded(config.colors.background)); - pal.set_timeline(Color::from_argb_encoded(config.colors.timeline)); - pal.set_background_text(Color::from_argb_encoded(config.colors.background_text)); - - // This looks like war crime - let event_colors_rc: ModelRc = Rc::new(VecModel::from( - config.event_colors.iter() - .map(|value| Color::from_argb_encoded(*value)).collect::>() - )).into(); - pal.set_event_colors(event_colors_rc); - let event_text_rc: ModelRc = Rc::new(VecModel::from( - config.text_colors.iter() - .map(|value| Color::from_argb_encoded(*value)).collect::>() - )).into(); - pal.set_event_text(event_text_rc); + pal.set_background(config.colors.get_color(&config.colors.background)); + pal.set_timeline(config.colors.get_color(&config.colors.timeline)); + pal.set_background_text(config.colors.get_color(&config.colors.text)); } fn main() -> Result<(), Box> { @@ -101,7 +102,8 @@ fn main() -> Result<(), Box> { let ui_weak = ui.as_weak(); let log = writing_log.clone(); - load_log(ui_weak, log); + let config_arc = config.clone(); + load_log(ui_weak, log, config_arc); let ui_weak = ui.as_weak(); let config_arc = config.clone(); @@ -122,7 +124,7 @@ fn main() -> Result<(), Box> { let events: Vec = Log::load_from(&config, date) .events .iter() - .map(|event| TimelineEvent::from((*event).clone())) + .map(|event| TimelineEvent::from_event(event.clone(), &config.colors)) .collect(); let model: ModelRc = Rc::new(VecModel::from(events)).into(); let mut state = ui.get_review_state(); @@ -165,6 +167,7 @@ fn main() -> Result<(), Box> { ui.on_start_new_event({ let ui_weak = ui.as_weak(); let log = writing_log.clone(); + let config = config.clone(); move |event_name: SharedString| { let ui = ui_weak.unwrap(); @@ -172,13 +175,16 @@ fn main() -> Result<(), Box> { let events = state.events.as_any() .downcast_ref::>() .unwrap(); + + let (background_color, text_color) = TimelineEvent::colors(&config.colors, event_name.as_str()); let event = TimelineEvent { duration: 0, finished: false, label: event_name.clone(), start: state.offset, - color_id: color_id_from_name(event_name.to_string()) + background_color, + text_color, }; { @@ -195,6 +201,7 @@ fn main() -> Result<(), Box> { ui.on_stop_event({ let ui_weak = ui.as_weak(); let log = writing_log.clone(); + let config = config.clone(); move || { let ui = ui_weak.unwrap(); let state = ui.get_record_state(); @@ -207,12 +214,16 @@ fn main() -> Result<(), Box> { .unwrap(); let event = events.row_data(event_id) .expect("stop-event called without unfinished events"); + + let (background_color, text_color) = TimelineEvent::colors(&config.colors, event.label.as_str()); + let new_event = TimelineEvent { duration: state.offset - event.start, finished: true, label: event.label.clone(), start: event.start, - color_id: color_id_from_name(event.label.to_string()) + background_color, + text_color, }; { @@ -263,6 +274,7 @@ fn main() -> Result<(), Box> { ui.on_new_day_started({ let ui_weak = ui.as_weak(); let log = writing_log.clone(); + let config = config.clone(); move || { let ui = ui_weak.unwrap(); @@ -297,7 +309,7 @@ fn main() -> Result<(), Box> { } } - load_log(ui.as_weak(), log.clone()); + load_log(ui.as_weak(), log.clone(), config.clone()); ui.invoke_save_log(); } }); diff --git a/ui/theme.slint b/ui/theme.slint index 30b211a..03a7282 100644 --- a/ui/theme.slint +++ b/ui/theme.slint @@ -2,42 +2,4 @@ export global Palette { in-out property background: gray; in-out property timeline: darkgray; in-out property background-text: black; - // Note: these colors were almost randomly picked - in-out property<[color]> event-colors: [ - #97f9f9, - #a4def9, - #c1e0f7, - #cfbae1, - #c59fc9, - #4e3d42, - #c9d5b5, - #2d82b7, - #556f44, - #772e25, - #c44536, - #7c6a0a, - #babd8d, - #ffdac6, - #fa9500, - #eb6424 - ]; - - in-out property <[color]> event-text: [ - #000000, - #000000, - #000000, - #000000, - #000000, - #ffffff, - #000000, - #000000, - #000000, - #ffffff, - #000000, - #000000, - #000000, - #000000, - #000000, - #000000 - ]; } diff --git a/ui/timeline.slint b/ui/timeline.slint index 1fa828b..94753fc 100644 --- a/ui/timeline.slint +++ b/ui/timeline.slint @@ -6,7 +6,8 @@ export struct TimelineEvent { duration: int, finished: bool, label: string, - color-id: int, + background-color: color, + text-color: color, } export struct TimelineState { @@ -69,7 +70,7 @@ export component Timeline inherits Rectangle { visible: self.width > 0 && self.real-x < parent.width; border-color: black; border-width: 1px; - background: Palette.event-colors[event.color-id]; + background: event.background-color; Text { x: 0; @@ -87,7 +88,7 @@ export component Timeline inherits Rectangle { visible: timeline-event.visible && (self.width * 2 < timeline-event.width || (!end-txt.visible && self.width < timeline-event.width)); - color: Palette.event-text[event.color-id]; + color: event.text-color; } end-txt := Text { x: timeline-event.width - self.width; @@ -96,7 +97,7 @@ export component Timeline inherits Rectangle { TimeString.from(event.start + event.duration) : TimeString.from(visible-offset); visible: timeline-event.visible && timeline-event.width - self.width * 2 > 0; - color: Palette.event-text[event.color-id]; + color: event.text-color; } } @children