Compare commits
No commits in common. "master" and "v0.3.0" have entirely different histories.
13 changed files with 256 additions and 347 deletions
49
Cargo.lock
generated
49
Cargo.lock
generated
|
|
@ -161,7 +161,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"slint",
|
"slint",
|
||||||
"slint-build",
|
"slint-build",
|
||||||
"toml 1.1.2+spec-1.1.0",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -722,9 +722,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.60"
|
version = "1.2.59"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20"
|
checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
"jobserver",
|
"jobserver",
|
||||||
|
|
@ -1685,9 +1685,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gif"
|
name = "gif"
|
||||||
version = "0.14.2"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159"
|
checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"color_quant",
|
"color_quant",
|
||||||
"weezl",
|
"weezl",
|
||||||
|
|
@ -1841,12 +1841,6 @@ dependencies = [
|
||||||
"foldhash 0.2.0",
|
"foldhash 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.17.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
|
@ -2339,12 +2333,12 @@ checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.14.0"
|
version = "2.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.17.0",
|
"hashbrown 0.16.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
@ -2594,14 +2588,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.16"
|
version = "0.1.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c"
|
checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
"libc",
|
"libc",
|
||||||
"plain",
|
"plain",
|
||||||
"redox_syscall 0.7.4",
|
"redox_syscall 0.7.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3828,9 +3822,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.7.4"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a"
|
checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
]
|
]
|
||||||
|
|
@ -4180,7 +4174,7 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tar",
|
"tar",
|
||||||
"toml 0.9.12+spec-1.1.0",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4686,21 +4680,6 @@ dependencies = [
|
||||||
"winnow 0.7.15",
|
"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]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.7.5+spec-1.1.0"
|
version = "0.7.5+spec-1.1.0"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||||
chrono = "0.4.42"
|
chrono = "0.4.42"
|
||||||
serde = "1.0.219"
|
serde = "1.0.219"
|
||||||
slint = "1.15.1"
|
slint = "1.15.1"
|
||||||
toml = "1.1.2"
|
toml = "0.9.5"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
slint-build = "1.15.1"
|
slint-build = "1.15.1"
|
||||||
|
|
|
||||||
1
build.rs
1
build.rs
|
|
@ -1,4 +1,5 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
||||||
let mut style = String::from("cosmic");
|
let mut style = String::from("cosmic");
|
||||||
|
|
||||||
#[cfg(all(feature = "dark", feature = "light"))]
|
#[cfg(all(feature = "dark", feature = "light"))]
|
||||||
|
|
|
||||||
93
config.toml
93
config.toml
|
|
@ -1,50 +1,53 @@
|
||||||
# Default Aliveline config
|
# This is the default config for Aliveline.
|
||||||
|
# Note: All colors are of format 0xAARRGGBB
|
||||||
|
|
||||||
# Paths may be relative to config directory or absolute
|
# Path where logs are saved. May be relative to config dir or absolute.
|
||||||
[paths]
|
log_path = "logs"
|
||||||
logs = "logs"
|
|
||||||
|
|
||||||
# Colors may be defined either as alias (string) or as 0xAARRGGBB (integer)
|
# 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
|
||||||
|
]
|
||||||
|
|
||||||
# Aliases must be declared here, otherwise fallback color is used
|
# Colors used for event colors. Aliveline expects it to have same size as events.
|
||||||
[colors.aliases]
|
text_colors = [
|
||||||
background = 0xFF_808080
|
0xff_000000,
|
||||||
timeline = 0xFF_A9A9A9
|
0xff_000000,
|
||||||
black = 0xFF_000000
|
0xff_000000,
|
||||||
white = 0xFF_FFFFFF
|
0xff_000000,
|
||||||
"very aggressive pink color to use with fallback so you definitely notice it" = 0xFF_FF00E7
|
0xff_000000,
|
||||||
|
0xff_ffffff,
|
||||||
|
0xff_000000,
|
||||||
|
0xff_000000,
|
||||||
|
0xff_000000,
|
||||||
|
0xff_ffffff,
|
||||||
|
0xff_000000,
|
||||||
|
0xff_000000,
|
||||||
|
0xff_000000,
|
||||||
|
0xff_000000,
|
||||||
|
0xff_000000,
|
||||||
|
0xff_000000
|
||||||
|
]
|
||||||
|
|
||||||
[colors]
|
[colors]
|
||||||
# Timeline background
|
# Color behind the timeline
|
||||||
background = "background"
|
background = 0xFF_808080
|
||||||
# Timeline foreground
|
# Color of the base timeline
|
||||||
timeline = "timeline"
|
timeline = 0xFF_a9a9a9
|
||||||
# Background text (timestamps, event names, etc.)
|
# Color of background text (timestamps, event names, etc.)
|
||||||
text = "black"
|
background_text = 0xFF_000000
|
||||||
# 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 },
|
|
||||||
]
|
|
||||||
|
|
|
||||||
104
src/config.rs
Normal file
104
src/config.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
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<u32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub text_colors: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
let conf_path = PathBuf::new();
|
||||||
|
let colors: Colors = Default::default();
|
||||||
|
let event_colors: Vec<u32> = 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<u32> = 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<Self> {
|
||||||
|
if let Ok(toml_string) = std::fs::read_to_string(path.clone()) {
|
||||||
|
if let Ok(mut conf) = toml::from_str::<Config>(&toml_string) {
|
||||||
|
conf.conf_path = path.parent().unwrap().into();
|
||||||
|
return Some(conf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
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<ConfigColor>,
|
|
||||||
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<String, u32>,
|
|
||||||
|
|
||||||
pub background: ConfigColor,
|
|
||||||
pub timeline: ConfigColor,
|
|
||||||
pub text: ConfigColor,
|
|
||||||
pub fallback: ConfigColor,
|
|
||||||
|
|
||||||
pub events: Vec<EventColor>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Color> {
|
|
||||||
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!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
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<String, u32>,
|
|
||||||
|
|
||||||
pub background: ConfigColor,
|
|
||||||
pub timeline: ConfigColor,
|
|
||||||
pub text: ConfigColor,
|
|
||||||
pub fallback: ConfigColor,
|
|
||||||
|
|
||||||
pub events: Vec<EventColor>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DefaultColors> 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<DefaultPaths> for Paths {
|
|
||||||
fn from(value: DefaultPaths) -> Self {
|
|
||||||
Self { logs: value.logs }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn default_config() -> DefaultConfig {
|
|
||||||
toml::de::from_str(DEFAULT_CFG).unwrap()
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
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<Self> {
|
|
||||||
if let Ok(toml_string) = std::fs::read_to_string(path.clone()) {
|
|
||||||
if let Ok(mut conf) = toml::from_str::<Config>(&toml_string) {
|
|
||||||
conf.replace_missing_fields();
|
|
||||||
conf.conf_path = path.parent().unwrap().into();
|
|
||||||
return Some(conf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use std::path::PathBuf;
|
use std::{hash::{DefaultHasher, Hash, Hasher}, path::PathBuf};
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod log;
|
pub mod log;
|
||||||
|
|
@ -45,3 +45,9 @@ pub fn load_config() -> Config {
|
||||||
println!("Using config path ./config.toml");
|
println!("Using config path ./config.toml");
|
||||||
Config::new(PathBuf::from("./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 }
|
||||||
|
|
|
||||||
14
src/log.rs
14
src/log.rs
|
|
@ -46,12 +46,12 @@ impl Log {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_log_dir(config: &Config) -> PathBuf {
|
fn get_log_dir(config: &Config) -> PathBuf {
|
||||||
if config.paths.logs.is_relative() {
|
if config.log_path.is_relative() {
|
||||||
let mut path = config.conf_path.clone();
|
let mut path = config.conf_path.clone();
|
||||||
path.push(&config.paths.logs);
|
path.push(&config.log_path);
|
||||||
return path;
|
return path;
|
||||||
} else {
|
} else {
|
||||||
return config.paths.logs.clone();
|
return config.log_path.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,14 +77,14 @@ impl Event {
|
||||||
let start = Time {
|
let start = Time {
|
||||||
hour: (start / 3600) as u8,
|
hour: (start / 3600) as u8,
|
||||||
minute: ((start / 3600) / 60) as u8,
|
minute: ((start / 3600) / 60) as u8,
|
||||||
second: Some((start % 60) as u8),
|
second: (start % 60) as u8,
|
||||||
nanosecond: None,
|
nanosecond: 0
|
||||||
};
|
};
|
||||||
let end = Time {
|
let end = Time {
|
||||||
hour: (end / 3600) as u8,
|
hour: (end / 3600) as u8,
|
||||||
minute: ((end % 3600) / 60) as u8,
|
minute: ((end % 3600) / 60) as u8,
|
||||||
second: Some((end % 60) as u8),
|
second: (end % 60) as u8,
|
||||||
nanosecond: None,
|
nanosecond: 0
|
||||||
};
|
};
|
||||||
Event { name, start, end, finished }
|
Event { name, start, end, finished }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
76
src/main.rs
76
src/main.rs
|
|
@ -3,40 +3,27 @@
|
||||||
|
|
||||||
use std::{error::Error, rc::Rc, sync::{Arc, Mutex}};
|
use std::{error::Error, rc::Rc, sync::{Arc, Mutex}};
|
||||||
|
|
||||||
use aliveline::{config::{Config, color::Colors}, load_config, log::{Event, Log}};
|
use aliveline::{color_id_from_name, config::Config, load_config, log::{Event, Log}};
|
||||||
use chrono::{Datelike, Timelike};
|
use chrono::{Datelike, Timelike};
|
||||||
use slint::{Color, Model, ModelRc, SharedString, ToSharedString, VecModel, Weak};
|
use slint::{Color, Model, ModelRc, SharedString, ToSharedString, VecModel, Weak};
|
||||||
use toml::value::{Date as TomlDate, Time};
|
use toml::value::{Date as TomlDate, Time};
|
||||||
|
|
||||||
slint::include_modules!();
|
slint::include_modules!();
|
||||||
|
|
||||||
impl TimelineEvent {
|
impl From<Event> for TimelineEvent {
|
||||||
pub fn colors(colors: &Colors, text: &str) -> (Color, Color) {
|
fn from(event: Event) -> Self {
|
||||||
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
|
let start = (event.start.hour as i32) * 3600
|
||||||
+ (event.start.minute as i32) * 60
|
+ (event.start.minute as i32) * 60
|
||||||
+ (event.start.second.unwrap() as i32);
|
+ (event.start.second as i32);
|
||||||
let end = (event.end.hour as i32) * 3600
|
let end = (event.end.hour as i32) * 3600
|
||||||
+ (event.end.minute as i32) * 60
|
+ (event.end.minute as i32) * 60
|
||||||
+ (event.end.second.unwrap() as i32);
|
+ (event.end.second as i32);
|
||||||
let (background_color, text_color) = TimelineEvent::colors(colors, event.name.as_str());
|
|
||||||
|
|
||||||
TimelineEvent {
|
TimelineEvent {
|
||||||
start,
|
start,
|
||||||
duration: end - start,
|
duration: end - start,
|
||||||
label: event.name.to_shared_string(),
|
label: event.name.to_shared_string(),
|
||||||
finished: event.finished,
|
finished: event.finished,
|
||||||
background_color,
|
color_id: color_id_from_name(event.name)
|
||||||
text_color,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,27 +33,27 @@ impl From<TimelineEvent> for Event {
|
||||||
let start = Time {
|
let start = Time {
|
||||||
hour: (event.start / 3600) as u8,
|
hour: (event.start / 3600) as u8,
|
||||||
minute: ((event.start % 3600) / 60) as u8,
|
minute: ((event.start % 3600) / 60) as u8,
|
||||||
second: Some((event.start % 60) as u8),
|
second: (event.start % 60) as u8,
|
||||||
nanosecond: None,
|
nanosecond: 0
|
||||||
};
|
};
|
||||||
let endsecs = event.start + event.duration;
|
let endsecs = event.start + event.duration;
|
||||||
let end = Time {
|
let end = Time {
|
||||||
hour: (endsecs / 3600) as u8,
|
hour: (endsecs / 3600) as u8,
|
||||||
minute: ((endsecs % 3600) / 60) as u8,
|
minute: ((endsecs % 3600) / 60) as u8,
|
||||||
second: Some((endsecs % 60) as u8),
|
second: (endsecs % 60) as u8,
|
||||||
nanosecond: None,
|
nanosecond: 0
|
||||||
};
|
};
|
||||||
Event { start, end, name: event.label.to_string(), finished: event.finished }
|
Event { start, end, name: event.label.to_string(), finished: event.finished }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_log(ui_weak: Weak<AppWindow>, log: Arc<Mutex<Log>>, config: Arc<Config>) {
|
fn load_log(ui_weak: Weak<AppWindow>, log: Arc<Mutex<Log>>) {
|
||||||
let ui = ui_weak.unwrap();
|
let ui = ui_weak.unwrap();
|
||||||
let log_guard = log.lock().expect("Log shouldn't be used twice");
|
let log_guard = log.lock().expect("Log shouldn't be used twice");
|
||||||
let events: Vec<TimelineEvent> = (*log_guard)
|
let events: Vec<TimelineEvent> = (*log_guard)
|
||||||
.events
|
.events
|
||||||
.iter()
|
.iter()
|
||||||
.map(|event| TimelineEvent::from_event(event.clone(), &config.colors))
|
.map(|event| TimelineEvent::from((*event).clone()))
|
||||||
.collect();
|
.collect();
|
||||||
let in_progress = events.iter().any(|event| !event.finished);
|
let in_progress = events.iter().any(|event| !event.finished);
|
||||||
let model: ModelRc<TimelineEvent> = Rc::new(VecModel::from(events)).into();
|
let model: ModelRc<TimelineEvent> = Rc::new(VecModel::from(events)).into();
|
||||||
|
|
@ -80,9 +67,21 @@ fn load_log(ui_weak: Weak<AppWindow>, log: Arc<Mutex<Log>>, config: Arc<Config>)
|
||||||
fn load_colors(ui_weak: Weak<AppWindow>, config: Arc<Config>) {
|
fn load_colors(ui_weak: Weak<AppWindow>, config: Arc<Config>) {
|
||||||
let ui = ui_weak.unwrap();
|
let ui = ui_weak.unwrap();
|
||||||
let pal = ui.global::<Palette>();
|
let pal = ui.global::<Palette>();
|
||||||
pal.set_background(config.colors.get_color(&config.colors.background));
|
pal.set_background(Color::from_argb_encoded(config.colors.background));
|
||||||
pal.set_timeline(config.colors.get_color(&config.colors.timeline));
|
pal.set_timeline(Color::from_argb_encoded(config.colors.timeline));
|
||||||
pal.set_background_text(config.colors.get_color(&config.colors.text));
|
pal.set_background_text(Color::from_argb_encoded(config.colors.background_text));
|
||||||
|
|
||||||
|
// This looks like war crime
|
||||||
|
let event_colors_rc: ModelRc<Color> = Rc::new(VecModel::from(
|
||||||
|
config.event_colors.iter()
|
||||||
|
.map(|value| Color::from_argb_encoded(*value)).collect::<Vec<Color>>()
|
||||||
|
)).into();
|
||||||
|
pal.set_event_colors(event_colors_rc);
|
||||||
|
let event_text_rc: ModelRc<Color> = Rc::new(VecModel::from(
|
||||||
|
config.text_colors.iter()
|
||||||
|
.map(|value| Color::from_argb_encoded(*value)).collect::<Vec<Color>>()
|
||||||
|
)).into();
|
||||||
|
pal.set_event_text(event_text_rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
@ -102,8 +101,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
let ui_weak = ui.as_weak();
|
let ui_weak = ui.as_weak();
|
||||||
let log = writing_log.clone();
|
let log = writing_log.clone();
|
||||||
let config_arc = config.clone();
|
load_log(ui_weak, log);
|
||||||
load_log(ui_weak, log, config_arc);
|
|
||||||
|
|
||||||
let ui_weak = ui.as_weak();
|
let ui_weak = ui.as_weak();
|
||||||
let config_arc = config.clone();
|
let config_arc = config.clone();
|
||||||
|
|
@ -124,7 +122,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let events: Vec<TimelineEvent> = Log::load_from(&config, date)
|
let events: Vec<TimelineEvent> = Log::load_from(&config, date)
|
||||||
.events
|
.events
|
||||||
.iter()
|
.iter()
|
||||||
.map(|event| TimelineEvent::from_event(event.clone(), &config.colors))
|
.map(|event| TimelineEvent::from((*event).clone()))
|
||||||
.collect();
|
.collect();
|
||||||
let model: ModelRc<TimelineEvent> = Rc::new(VecModel::from(events)).into();
|
let model: ModelRc<TimelineEvent> = Rc::new(VecModel::from(events)).into();
|
||||||
let mut state = ui.get_review_state();
|
let mut state = ui.get_review_state();
|
||||||
|
|
@ -167,7 +165,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
ui.on_start_new_event({
|
ui.on_start_new_event({
|
||||||
let ui_weak = ui.as_weak();
|
let ui_weak = ui.as_weak();
|
||||||
let log = writing_log.clone();
|
let log = writing_log.clone();
|
||||||
let config = config.clone();
|
|
||||||
move |event_name: SharedString| {
|
move |event_name: SharedString| {
|
||||||
let ui = ui_weak.unwrap();
|
let ui = ui_weak.unwrap();
|
||||||
|
|
||||||
|
|
@ -175,16 +172,13 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let events = state.events.as_any()
|
let events = state.events.as_any()
|
||||||
.downcast_ref::<VecModel<TimelineEvent>>()
|
.downcast_ref::<VecModel<TimelineEvent>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (background_color, text_color) = TimelineEvent::colors(&config.colors, event_name.as_str());
|
|
||||||
|
|
||||||
let event = TimelineEvent {
|
let event = TimelineEvent {
|
||||||
duration: 0,
|
duration: 0,
|
||||||
finished: false,
|
finished: false,
|
||||||
label: event_name.clone(),
|
label: event_name.clone(),
|
||||||
start: state.offset,
|
start: state.offset,
|
||||||
background_color,
|
color_id: color_id_from_name(event_name.to_string())
|
||||||
text_color,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -201,7 +195,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
ui.on_stop_event({
|
ui.on_stop_event({
|
||||||
let ui_weak = ui.as_weak();
|
let ui_weak = ui.as_weak();
|
||||||
let log = writing_log.clone();
|
let log = writing_log.clone();
|
||||||
let config = config.clone();
|
|
||||||
move || {
|
move || {
|
||||||
let ui = ui_weak.unwrap();
|
let ui = ui_weak.unwrap();
|
||||||
let state = ui.get_record_state();
|
let state = ui.get_record_state();
|
||||||
|
|
@ -214,16 +207,12 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let event = events.row_data(event_id)
|
let event = events.row_data(event_id)
|
||||||
.expect("stop-event called without unfinished events");
|
.expect("stop-event called without unfinished events");
|
||||||
|
|
||||||
let (background_color, text_color) = TimelineEvent::colors(&config.colors, event.label.as_str());
|
|
||||||
|
|
||||||
let new_event = TimelineEvent {
|
let new_event = TimelineEvent {
|
||||||
duration: state.offset - event.start,
|
duration: state.offset - event.start,
|
||||||
finished: true,
|
finished: true,
|
||||||
label: event.label.clone(),
|
label: event.label.clone(),
|
||||||
start: event.start,
|
start: event.start,
|
||||||
background_color,
|
color_id: color_id_from_name(event.label.to_string())
|
||||||
text_color,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -274,7 +263,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
ui.on_new_day_started({
|
ui.on_new_day_started({
|
||||||
let ui_weak = ui.as_weak();
|
let ui_weak = ui.as_weak();
|
||||||
let log = writing_log.clone();
|
let log = writing_log.clone();
|
||||||
let config = config.clone();
|
|
||||||
move || {
|
move || {
|
||||||
let ui = ui_weak.unwrap();
|
let ui = ui_weak.unwrap();
|
||||||
|
|
||||||
|
|
@ -309,7 +297,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
load_log(ui.as_weak(), log.clone(), config.clone());
|
load_log(ui.as_weak(), log.clone());
|
||||||
ui.invoke_save_log();
|
ui.invoke_save_log();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,42 @@ export global Palette {
|
||||||
in-out property<color> background: gray;
|
in-out property<color> background: gray;
|
||||||
in-out property<color> timeline: darkgray;
|
in-out property<color> timeline: darkgray;
|
||||||
in-out property<color> background-text: black;
|
in-out property<color> 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
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@ export struct TimelineEvent {
|
||||||
duration: int,
|
duration: int,
|
||||||
finished: bool,
|
finished: bool,
|
||||||
label: string,
|
label: string,
|
||||||
background-color: color,
|
color-id: int,
|
||||||
text-color: color,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export struct TimelineState {
|
export struct TimelineState {
|
||||||
|
|
@ -70,7 +69,7 @@ export component Timeline inherits Rectangle {
|
||||||
visible: self.width > 0 && self.real-x < parent.width;
|
visible: self.width > 0 && self.real-x < parent.width;
|
||||||
border-color: black;
|
border-color: black;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
background: event.background-color;
|
background: Palette.event-colors[event.color-id];
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
x: 0;
|
x: 0;
|
||||||
|
|
@ -88,7 +87,7 @@ export component Timeline inherits Rectangle {
|
||||||
visible: timeline-event.visible &&
|
visible: timeline-event.visible &&
|
||||||
(self.width * 2 < timeline-event.width ||
|
(self.width * 2 < timeline-event.width ||
|
||||||
(!end-txt.visible && self.width < timeline-event.width));
|
(!end-txt.visible && self.width < timeline-event.width));
|
||||||
color: event.text-color;
|
color: Palette.event-text[event.color-id];
|
||||||
}
|
}
|
||||||
end-txt := Text {
|
end-txt := Text {
|
||||||
x: timeline-event.width - self.width;
|
x: timeline-event.width - self.width;
|
||||||
|
|
@ -97,7 +96,7 @@ export component Timeline inherits Rectangle {
|
||||||
TimeString.from(event.start + event.duration) :
|
TimeString.from(event.start + event.duration) :
|
||||||
TimeString.from(visible-offset);
|
TimeString.from(visible-offset);
|
||||||
visible: timeline-event.visible && timeline-event.width - self.width * 2 > 0;
|
visible: timeline-event.visible && timeline-event.width - self.width * 2 > 0;
|
||||||
color: event.text-color;
|
color: Palette.event-text[event.color-id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@children
|
@children
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue