feat(time): formattable datetime strings with strftime

This commit is contained in:
Joshua Megnauth 2025-11-07 13:42:36 -05:00 committed by GitHub
parent ba1e379a75
commit bd98de8228
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 96 additions and 58 deletions

View file

@ -43,6 +43,11 @@ use icu::{
static AUTOSIZE_MAIN_ID: LazyLock<Id> = LazyLock::new(|| Id::new("autosize-main"));
// Specifiers for strftime that indicate seconds. Subsecond precision isn't supported by the applet
// so those specifiers aren't listed here. This list is non-exhaustive, and it's possible that %X
// and other specifiers have to be added depending on locales.
const STRFTIME_SECONDS: &[char] = &['S', 'T', '+', 's'];
fn get_system_locale() -> Locale {
for var in ["LC_TIME", "LC_ALL", "LANG"] {
if let Ok(locale_str) = std::env::var(var) {
@ -159,6 +164,16 @@ impl Window {
}
fn vertical_layout(&self) -> Element<'_, Message> {
let elements: Vec<Element<'_, Message>> = if !self.config.format_strftime.is_empty() {
// strftime formatter may override locale specific elements so it stands alone rather
// than using ICU to determine a format.
self.now
.format(&self.config.format_strftime)
.to_string()
.split_whitespace()
.map(|piece| self.core.applet.text(piece.to_owned()).into())
.collect()
} else {
let mut elements = Vec::new();
let date = self.now.naive_local();
let datetime = self.create_datetime(&date);
@ -199,6 +214,9 @@ impl Window {
elements.push(self.core.applet.text(p.to_owned()).into());
}
elements
};
let date_time_col = Column::with_children(elements)
.align_x(Alignment::Center)
.spacing(4);
@ -216,6 +234,9 @@ impl Window {
}
fn horizontal_layout(&self) -> Element<'_, Message> {
let formatted_date = if !self.config.format_strftime.is_empty() {
self.now.format(&self.config.format_strftime).to_string()
} else {
let datetime = self.create_datetime(&self.now);
let mut prefs = DateTimeFormatterPreferences::from(self.locale.clone());
prefs.hour_cycle = Some(if self.config.military_time {
@ -224,7 +245,7 @@ impl Window {
HourCycle::H12
});
let formatted_date = if self.config.show_date_in_top_panel {
if self.config.show_date_in_top_panel {
if self.config.show_weekday {
let mut fs = fieldsets::MDET::long();
if !self.config.show_seconds {
@ -253,6 +274,7 @@ impl Window {
.unwrap()
.format(&datetime)
.to_string()
}
};
Element::from(
@ -587,7 +609,20 @@ impl cosmic::Application for Window {
Message::ConfigChanged(c) => {
// Don't interrupt the tick subscription unless necessary
self.show_seconds_tx.send_if_modified(|show_seconds| {
if *show_seconds == c.show_seconds {
if !c.format_strftime.is_empty() {
if c.format_strftime.split('%').any(|s| {
STRFTIME_SECONDS.contains(&s.chars().next().unwrap_or_default())
}) && !*show_seconds
{
// The strftime formatter contains a seconds specifier. Force enable
// ticking per seconds internally regardless of the user setting.
// This does not change the user's setting. It's invisible to the user.
*show_seconds = true;
true
} else {
false
}
} else if *show_seconds == c.show_seconds {
false
} else {
*show_seconds = c.show_seconds;

View file

@ -11,6 +11,8 @@ pub struct TimeAppletConfig {
pub first_day_of_week: u8,
pub show_date_in_top_panel: bool,
pub show_weekday: bool,
#[serde(default, skip_serializing_if = "str::is_empty")]
pub format_strftime: String,
}
impl Default for TimeAppletConfig {
@ -21,6 +23,7 @@ impl Default for TimeAppletConfig {
first_day_of_week: 6,
show_date_in_top_panel: true,
show_weekday: false,
format_strftime: Default::default(),
}
}
}