From 218ee49a8be677e395603619a60e39f9597f86f9 Mon Sep 17 00:00:00 2001 From: 2ndbeam <2ndbeam@disroot.org> Date: Thu, 18 Sep 2025 16:30:37 +0300 Subject: [PATCH] Pseudo pseudorandom color picker --- Cargo.toml | 2 +- src/lib.rs | 10 +++++++++- src/main.rs | 17 ++++++++++------- ui/app-window.slint | 1 - ui/record.slint | 1 - ui/theme.slint | 43 +++++++++++++++++++++++++++++++++++++++++++ ui/timeline.slint | 16 ++++++++++++---- 7 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 ui/theme.slint diff --git a/Cargo.toml b/Cargo.toml index 49e34c7..7675ffc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,4 @@ toml = "0.9.5" slint-build = "1.12.1" [profile.release] -opt-level = "s" +opt-level = 3 diff --git a/src/lib.rs b/src/lib.rs index bdf3eb3..f63008d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ use config::Config; -use std::path::PathBuf; +use std::{hash::{DefaultHasher, Hash, Hasher}, path::PathBuf}; pub mod config; pub mod log; @@ -13,3 +13,11 @@ pub fn load_config() -> Config { } 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.count_ones() / 4) as i32 +} diff --git a/src/main.rs b/src/main.rs index 3285d0f..19cb68d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use std::{error::Error, rc::Rc, sync::{Arc, Mutex}}; -use aliveline::{config::Config, load_config, log::{Event, Log}}; +use aliveline::{color_id_from_name, config::Config, load_config, log::{Event, Log}}; use chrono::{Datelike, Timelike}; use slint::{Model, ModelRc, SharedString, ToSharedString, VecModel, Weak}; use toml::value::{Date as TomlDate, Time}; @@ -22,7 +22,8 @@ impl From for TimelineEvent { start, duration: end - start, label: event.name.to_shared_string(), - finished: event.finished + finished: event.finished, + color_id: color_id_from_name(event.name) } } } @@ -143,8 +144,9 @@ fn main() -> Result<(), Box> { let event = TimelineEvent { duration: 0, finished: false, - label: event_name, - start: offset + label: event_name.clone(), + start: offset, + color_id: color_id_from_name(event_name.to_string()) }; { @@ -177,8 +179,9 @@ fn main() -> Result<(), Box> { let new_event = TimelineEvent { duration: offset - event.start, finished: true, - label: event.label, - start: event.start + label: event.label.clone(), + start: event.start, + color_id: color_id_from_name(event.label.to_string()) }; { @@ -215,7 +218,7 @@ fn main() -> Result<(), Box> { let maybe_unfinished_event = log_guard.events.iter().find(|event| !event.finished); match maybe_unfinished_event { Some(unfinished_event) => Some(Event::new(unfinished_event.name.clone(), 0, 0, false)), - None => None + _ => None } }; diff --git a/ui/app-window.slint b/ui/app-window.slint index 373e060..7137014 100644 --- a/ui/app-window.slint +++ b/ui/app-window.slint @@ -31,7 +31,6 @@ export component AppWindow inherits Window { property<[string]> combo-spans: ["1 Hour", "4 Hours", "8 Hours", "24 Hours"]; title: "Aliveline"; - TabWidget { Tab { title: "Record"; diff --git a/ui/record.slint b/ui/record.slint index 1dff7b4..4d7090c 100644 --- a/ui/record.slint +++ b/ui/record.slint @@ -16,7 +16,6 @@ export component RecordWidget inherits VerticalBox { property event-name: ""; property minimized: false; property combo-index: 0; - tl := Timeline { preferred-height: 100%; updating: true; diff --git a/ui/theme.slint b/ui/theme.slint new file mode 100644 index 0000000..30b211a --- /dev/null +++ b/ui/theme.slint @@ -0,0 +1,43 @@ +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 c9fef81..2e9db7f 100644 --- a/ui/timeline.slint +++ b/ui/timeline.slint @@ -1,8 +1,11 @@ +import { Palette } from "theme.slint"; + export struct TimelineEvent { start: int, duration: int, finished: bool, - label: string + label: string, + color-id: int } global TimeString { @@ -26,6 +29,7 @@ global TimeString { export component Timeline inherits Rectangle { callback new-day-started; callback clicked <=> ta.clicked; + background: Palette.background; in-out property updating: true; in-out property<[TimelineEvent]> events: []; @@ -52,7 +56,6 @@ export component Timeline inherits Rectangle { preferred-height: 100%; } - background: gray; border-width: 1px; border-color: black; Rectangle { @@ -63,19 +66,21 @@ export component Timeline inherits Rectangle { height: parent.height / 2; border-color: black; border-width: 1px; - background: purple; + background: Palette.timeline; } Text { x: 0; y: parent.height - self.height; text: TimeString.from(visible-offset - visible-time); + color: Palette.background-text; } Text { x: parent.width - self.width; y: parent.height - self.height; text: TimeString.from(visible-offset); + color: Palette.background-text; } for event in events: timeline-event := Rectangle { @@ -91,13 +96,14 @@ export component Timeline inherits Rectangle { visible: self.width > 0 && self.real-x < parent.width; border-color: black; border-width: 1px; - background: red; + background: Palette.event-colors[event.color-id]; Text { x: 0; y: -self.height; text: event.label; visible: timeline-event.visible; + color: Palette.background-text; } start-txt := Text { x: 0; @@ -108,6 +114,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]; } end-txt := Text { x: timeline-event.width - self.width; @@ -116,6 +123,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]; } } @children