feat(bar): add changing icons based on the time

This commit adds a little Easter egg on the time widget.

Use the `changing_icon` setting to enable this feature.

Based on the current time, the widget will use different icons to
indicate certain activities of the day.

00:00 MOON
06:00 ALARM
06:01 BREAD
06:30 BARBELL
08:00 COFFEE
08:30 CLOCK
12:00 HAMBURGER
12:30 CLOCK_AFTERNOON
18:00 FORK_KNIFE
18:30 MOON_STARS
This commit is contained in:
Csaba
2025-03-08 16:33:26 +01:00
committed by LGUG2Z
parent 8e588d0284
commit acf780767c
5 changed files with 122 additions and 27 deletions

1
Cargo.lock generated
View File

@@ -2729,6 +2729,7 @@ dependencies = [
"image",
"komorebi-client",
"komorebi-themes",
"lazy_static",
"netdev",
"num",
"num-derive",

View File

@@ -21,6 +21,7 @@ egui-phosphor = "0.9"
font-loader = "0.11"
hotwatch = { workspace = true }
image = "0.25"
lazy_static = { workspace = true }
netdev = "0.32"
num = "0.4"
num-derive = "0.4"

View File

@@ -2,6 +2,7 @@ use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::widget::BarWidget;
use chrono::Local;
use chrono_tz::Tz;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
@@ -39,7 +40,7 @@ impl CustomModifiers {
}
// get the strftime value of modifier
let formatted_modifier = chrono::Local::now().format(modifier).to_string();
let formatted_modifier = Local::now().format(modifier).to_string();
// find the gotten value in the original output
if let Some(pos) = modified_output.find(&formatted_modifier) {
@@ -143,7 +144,7 @@ impl Date {
fn output(&mut self) -> String {
let formatted = match &self.timezone {
Some(timezone) => match timezone.parse::<Tz>() {
Ok(tz) => chrono::Local::now()
Ok(tz) => Local::now()
.with_timezone(&tz)
.format(&self.format.fmt_string())
.to_string()
@@ -151,7 +152,7 @@ impl Date {
.to_string(),
Err(_) => format!("Invalid timezone: {}", timezone),
},
None => chrono::Local::now()
None => Local::now()
.format(&self.format.fmt_string())
.to_string()
.trim()

View File

@@ -3,6 +3,8 @@ use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::widget::BarWidget;
use chrono::Local;
use chrono::NaiveTime;
use chrono_tz::Tz;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
@@ -15,9 +17,57 @@ use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::Vec2;
use eframe::epaint::StrokeKind;
use lazy_static::lazy_static;
use serde::Deserialize;
use serde::Serialize;
lazy_static! {
static ref TIME_RANGES: Vec<(&'static str, NaiveTime)> = {
vec![
(
egui_phosphor::regular::MOON,
NaiveTime::from_hms_opt(0, 0, 0).expect("invalid"),
),
(
egui_phosphor::regular::ALARM,
NaiveTime::from_hms_opt(6, 0, 0).expect("invalid"),
),
(
egui_phosphor::regular::BREAD,
NaiveTime::from_hms_opt(6, 1, 0).expect("invalid"),
),
(
egui_phosphor::regular::BARBELL,
NaiveTime::from_hms_opt(6, 30, 0).expect("invalid"),
),
(
egui_phosphor::regular::COFFEE,
NaiveTime::from_hms_opt(8, 0, 0).expect("invalid"),
),
(
egui_phosphor::regular::CLOCK,
NaiveTime::from_hms_opt(8, 30, 0).expect("invalid"),
),
(
egui_phosphor::regular::HAMBURGER,
NaiveTime::from_hms_opt(12, 0, 0).expect("invalid"),
),
(
egui_phosphor::regular::CLOCK_AFTERNOON,
NaiveTime::from_hms_opt(12, 30, 0).expect("invalid"),
),
(
egui_phosphor::regular::FORK_KNIFE,
NaiveTime::from_hms_opt(18, 0, 0).expect("invalid"),
),
(
egui_phosphor::regular::MOON_STARS,
NaiveTime::from_hms_opt(18, 30, 0).expect("invalid"),
),
]
};
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct TimeConfig {
@@ -40,6 +90,8 @@ pub struct TimeConfig {
///}
/// ```
pub timezone: Option<String>,
/// Change the icon depending on the time. The default icon is used between 8:30 and 12:00. (default: false)
pub changing_icon: Option<bool>,
}
impl From<TimeConfig> for Time {
@@ -49,6 +101,7 @@ impl From<TimeConfig> for Time {
format: value.format,
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::Icon),
timezone: value.timezone,
changing_icon: value.changing_icon.unwrap_or_default(),
}
}
}
@@ -104,26 +157,55 @@ pub struct Time {
pub format: TimeFormat,
label_prefix: LabelPrefix,
timezone: Option<String>,
changing_icon: bool,
}
impl Time {
fn output(&mut self) -> String {
match &self.timezone {
fn output(&mut self) -> (String, String) {
let (formatted, current_time) = match &self.timezone {
Some(timezone) => match timezone.parse::<Tz>() {
Ok(tz) => chrono::Local::now()
.with_timezone(&tz)
.format(&self.format.fmt_string())
.to_string()
.trim()
.to_string(),
Err(_) => format!("Invalid timezone: {}", timezone),
Ok(tz) => {
let dt = Local::now().with_timezone(&tz);
(
dt.format(&self.format.fmt_string())
.to_string()
.trim()
.to_string(),
Some(dt.time()),
)
}
Err(_) => (format!("Invalid timezone: {:?}", timezone), None),
},
None => chrono::Local::now()
.format(&self.format.fmt_string())
.to_string()
.trim()
.to_string(),
None => {
let dt = Local::now();
(
dt.format(&self.format.fmt_string())
.to_string()
.trim()
.to_string(),
Some(dt.time()),
)
}
};
if current_time.is_none() {
return (
formatted,
egui_phosphor::regular::WARNING_CIRCLE.to_string(),
);
}
let current_range = match &self.changing_icon {
true => TIME_RANGES
.iter()
.rev()
.find(|&(_, start)| current_time.unwrap() > *start)
.cloned(),
false => None,
}
.unwrap_or((egui_phosphor::regular::CLOCK, NaiveTime::default()));
(formatted, current_range.0.to_string())
}
fn paint_binary_circle(
@@ -306,15 +388,13 @@ impl BarWidget for Time {
fn render(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) {
if self.enable {
let mut output = self.output();
if !output.is_empty() {
let use_binary_circle = output.starts_with('c');
let use_binary_rectangle = output.starts_with('r');
if !output.0.is_empty() {
let use_binary_circle = output.0.starts_with('c');
let use_binary_rectangle = output.0.starts_with('r');
let mut layout_job = LayoutJob::simple(
match self.label_prefix {
LabelPrefix::Icon | LabelPrefix::IconAndText => {
egui_phosphor::regular::CLOCK.to_string()
}
LabelPrefix::Icon | LabelPrefix::IconAndText => output.1,
LabelPrefix::None | LabelPrefix::Text => String::new(),
},
config.icon_font_id.clone(),
@@ -323,12 +403,12 @@ impl BarWidget for Time {
);
if let LabelPrefix::Text | LabelPrefix::IconAndText = self.label_prefix {
output.insert_str(0, "TIME: ");
output.0.insert_str(0, "TIME: ");
}
if !use_binary_circle && !use_binary_rectangle {
layout_job.append(
&output,
&output.0,
10.0,
TextFormat {
font_id: config.text_font_id.clone(),
@@ -351,9 +431,9 @@ impl BarWidget for Time {
if use_binary_circle || use_binary_rectangle {
let ordered_output = if is_reversed {
output.chars().rev().collect()
output.0.chars().rev().collect()
} else {
output
output.0
};
for (section_index, section) in

View File

@@ -896,6 +896,10 @@
"format"
],
"properties": {
"changing_icon": {
"description": "Change the icon depending on the time. The default icon is used between 8:30 and 12:00. (default: false)",
"type": "boolean"
},
"enable": {
"description": "Enable the Time widget",
"type": "boolean"
@@ -2302,6 +2306,10 @@
"format"
],
"properties": {
"changing_icon": {
"description": "Change the icon depending on the time. The default icon is used between 8:30 and 12:00. (default: false)",
"type": "boolean"
},
"enable": {
"description": "Enable the Time widget",
"type": "boolean"
@@ -3641,6 +3649,10 @@
"format"
],
"properties": {
"changing_icon": {
"description": "Change the icon depending on the time. The default icon is used between 8:30 and 12:00. (default: false)",
"type": "boolean"
},
"enable": {
"description": "Enable the Time widget",
"type": "boolean"