From 990e2e291b33379f985bae0f81e46292b3d0e9cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vuka=C5=A1in=20Vojinovi=C4=87?= <150025636+git-f0x@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:32:21 +0100 Subject: [PATCH] refactor(calendar): use `jiff` instead of `chrono` This refactors the calendar widget to use `jiff` instead of `chrono`. Also mostly matches the design of the widget to the time applet. --- Cargo.toml | 2 +- examples/calendar/Cargo.toml | 6 +- examples/calendar/src/main.rs | 8 +- i18n/en/libcosmic.ftl | 21 ++-- src/widget/calendar.rs | 197 +++++++++++++++------------------- 5 files changed, 111 insertions(+), 123 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index feaa8c7..4aaf9d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,7 +104,7 @@ async-fs = { version = "2.2", optional = true } async-std = { version = "1.13", optional = true } auto_enums = "0.8.7" cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "d0e95be", optional = true } -chrono = "0.4.43" +jiff = "0.2" cosmic-config = { path = "cosmic-config" } cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true } # Internationalization diff --git a/examples/calendar/Cargo.toml b/examples/calendar/Cargo.toml index 59b23c0..b728682 100644 --- a/examples/calendar/Cargo.toml +++ b/examples/calendar/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "calendar" -version = "0.1.0" -edition = "2021" +version = "1.0.0" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = "0.4.42" +jiff = "0.2" [dependencies.libcosmic] path = "../../" diff --git a/examples/calendar/src/main.rs b/examples/calendar/src/main.rs index 589bc1f..240684c 100644 --- a/examples/calendar/src/main.rs +++ b/examples/calendar/src/main.rs @@ -3,10 +3,10 @@ //! Calendar widget example -use chrono::NaiveDate; use cosmic::app::{Core, Settings, Task}; use cosmic::widget::calendar::CalendarModel; -use cosmic::{executor, iced, ApplicationExt, Element}; +use cosmic::{ApplicationExt, Element, executor, iced}; +use jiff::civil::{Date, Weekday}; /// Runs application with these settings #[rustfmt::skip] @@ -19,7 +19,7 @@ fn main() -> Result<(), Box> { /// Messages that are used specifically by our [`App`]. #[derive(Clone, Debug)] pub enum Message { - DateSelected(NaiveDate), + DateSelected(Date), PrevMonth, NextMonth, } @@ -92,7 +92,7 @@ impl cosmic::Application for App { |date| Message::DateSelected(date), || Message::PrevMonth, || Message::NextMonth, - chrono::Weekday::Sun, + Weekday::Sunday, ); content = content.push(calendar); diff --git a/i18n/en/libcosmic.ftl b/i18n/en/libcosmic.ftl index 119ac38..257fc44 100644 --- a/i18n/en/libcosmic.ftl +++ b/i18n/en/libcosmic.ftl @@ -23,10 +23,17 @@ september = September { $year } october = October { $year } november = November { $year } december = December { $year } -monday = Mon -tuesday = Tue -wednesday = Wed -thursday = Thu -friday = Fri -saturday = Sat -sunday = Sun +monday = Monday +mon = Mon +tuesday = Tuesday +tue = Tue +wednesday = Wednesday +wed = Wed +thursday = Thursday +thu = Thu +friday = Friday +fri = Fri +saturday = Saturday +sat = Sat +sunday = Sunday +sun = Sun diff --git a/src/widget/calendar.rs b/src/widget/calendar.rs index 7ee0620..ea10fdd 100644 --- a/src/widget/calendar.rs +++ b/src/widget/calendar.rs @@ -3,19 +3,20 @@ //! A widget that displays an interactive calendar. -use std::cmp; - use crate::fl; -use crate::iced_core::{Alignment, Length, Padding}; -use crate::widget::{Grid, button, column, grid, icon, row, text}; +use crate::iced_core::{Alignment, Length}; +use crate::widget::{button, column, grid, icon, row, text}; use apply::Apply; -use chrono::{Datelike, Days, Local, Month, Months, NaiveDate, Weekday}; use iced::alignment::Vertical; +use jiff::{ + ToSpan, + civil::{Date, Weekday}, +}; /// A widget that displays an interactive calendar. pub fn calendar( model: &CalendarModel, - on_select: impl Fn(NaiveDate) -> M + 'static, + on_select: impl Fn(Date) -> M + 'static, on_prev: impl Fn() -> M + 'static, on_next: impl Fn() -> M + 'static, first_day_of_week: Weekday, @@ -29,61 +30,40 @@ pub fn calendar( } } -pub fn set_day(date_selected: NaiveDate, day: u32) -> NaiveDate { - let current = date_selected.day(); - - let new_date = match current.cmp(&day) { - cmp::Ordering::Less => date_selected.checked_add_days(Days::new((day - current) as u64)), - - cmp::Ordering::Greater => date_selected.checked_sub_days(Days::new((current - day) as u64)), - - _ => None, - }; - - if let Some(new) = new_date { - new - } else { - date_selected - } +pub fn set_day(date_selected: Date, day: i8) -> Date { + date_selected + .with() + .day(day) + .build() + .unwrap_or(date_selected) } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct CalendarModel { - pub selected: NaiveDate, - pub visible: NaiveDate, + pub selected: Date, + pub visible: Date, } impl CalendarModel { pub fn now() -> Self { - let now = Local::now(); - let naive_now = NaiveDate::from(now.naive_local()); + let now = jiff::Zoned::now().date(); CalendarModel { - selected: naive_now, - visible: naive_now, + selected: now, + visible: now, } } #[inline] - pub fn new(selected: NaiveDate, visible: NaiveDate) -> Self { + pub fn new(selected: Date, visible: Date) -> Self { CalendarModel { selected, visible } } pub fn show_prev_month(&mut self) { - let prev_month_date = self - .visible - .checked_sub_months(Months::new(1)) - .expect("valid naivedate"); - - self.visible = prev_month_date; + self.visible = self.visible.checked_sub(1.month()).expect("valid date"); } pub fn show_next_month(&mut self) { - let next_month_date = self - .visible - .checked_add_months(Months::new(1)) - .expect("valid naivedate"); - - self.visible = next_month_date; + self.visible = self.visible.checked_add(1.month()).expect("valid date"); } #[inline] @@ -99,7 +79,7 @@ impl CalendarModel { } #[inline] - pub fn set_selected_visible(&mut self, selected: NaiveDate) { + pub fn set_selected_visible(&mut self, selected: Date) { self.selected = selected; self.visible = self.selected; } @@ -107,7 +87,7 @@ impl CalendarModel { pub struct Calendar<'a, M> { model: &'a CalendarModel, - on_select: Box M>, + on_select: Box M>, on_prev: Box M>, on_next: Box M>, first_day_of_week: Weekday, @@ -121,45 +101,57 @@ where macro_rules! translate_month { ($month:expr, $year:expr) => {{ match $month { - chrono::Month::January => fl!("january", year = $year), - chrono::Month::February => fl!("february", year = $year), - chrono::Month::March => fl!("march", year = $year), - chrono::Month::April => fl!("april", year = $year), - chrono::Month::May => fl!("may", year = $year), - chrono::Month::June => fl!("june", year = $year), - chrono::Month::July => fl!("july", year = $year), - chrono::Month::August => fl!("august", year = $year), - chrono::Month::September => fl!("september", year = $year), - chrono::Month::October => fl!("october", year = $year), - chrono::Month::November => fl!("november", year = $year), - chrono::Month::December => fl!("december", year = $year), + 1 => fl!("january", year = $year), + 2 => fl!("february", year = $year), + 3 => fl!("march", year = $year), + 4 => fl!("april", year = $year), + 5 => fl!("may", year = $year), + 6 => fl!("june", year = $year), + 7 => fl!("july", year = $year), + 8 => fl!("august", year = $year), + 9 => fl!("september", year = $year), + 10 => fl!("october", year = $year), + 11 => fl!("november", year = $year), + 12 => fl!("december", year = $year), + _ => unreachable!(), } }}; } macro_rules! translate_weekday { - ($weekday:expr) => {{ + ($weekday:expr, short) => {{ match $weekday { - Weekday::Mon => fl!("monday"), - Weekday::Tue => fl!("tuesday"), - Weekday::Wed => fl!("wednesday"), - Weekday::Thu => fl!("thursday"), - Weekday::Fri => fl!("friday"), - Weekday::Sat => fl!("saturday"), - Weekday::Sun => fl!("sunday"), + Weekday::Monday => fl!("mon"), + Weekday::Tuesday => fl!("tue"), + Weekday::Wednesday => fl!("wed"), + Weekday::Thursday => fl!("thu"), + Weekday::Friday => fl!("fri"), + Weekday::Saturday => fl!("sat"), + Weekday::Sunday => fl!("sun"), + } + }}; + ($weekday:expr, long) => {{ + match $weekday { + Weekday::Monday => fl!("monday"), + Weekday::Tuesday => fl!("tuesday"), + Weekday::Wednesday => fl!("wednesday"), + Weekday::Thursday => fl!("thursday"), + Weekday::Friday => fl!("friday"), + Weekday::Saturday => fl!("saturday"), + Weekday::Sunday => fl!("sunday"), } }}; } let date = text(translate_month!( - Month::try_from(this.model.visible.month() as u8) - .expect("Previously valid month is suddenly invalid"), + this.model.visible.month(), this.model.visible.year() )) .size(18); - let day = text::body(translate_weekday!(this.model.visible.weekday())); + let day = text::body(translate_weekday!(this.model.visible.weekday(), long)); let month_controls = row::with_capacity(2) + .spacing(8) .push( icon::from_name("go-previous-symbolic") .apply(button::icon) @@ -171,46 +163,49 @@ where .on_press((this.on_next)()), ); - // Calender - let mut calendar_grid: Grid<'_, Message> = - grid().padding([0, 12].into()).width(Length::Fill); + // Calendar + let mut calendar_grid = grid().padding([0, 12].into()).width(Length::Fill); let mut first_day_of_week = this.first_day_of_week; for _ in 0..7 { calendar_grid = calendar_grid.push( - text(translate_weekday!(first_day_of_week)) - .size(12) - .width(Length::Fixed(36.0)) + text::caption(translate_weekday!(first_day_of_week, short)) + .width(Length::Fixed(44.0)) .align_x(Alignment::Center), ); - first_day_of_week = first_day_of_week.succ(); + first_day_of_week = first_day_of_week.next(); } calendar_grid = calendar_grid.insert_row(); - let monday = get_calender_first( + let first = get_calendar_first( this.model.visible.year(), this.model.visible.month(), - first_day_of_week, + this.first_day_of_week, ); - let mut day_iter = monday.iter_days(); + + let today = jiff::Zoned::now().date(); for i in 0..42 { if i > 0 && i % 7 == 0 { calendar_grid = calendar_grid.insert_row(); } - let date = day_iter.next().unwrap(); - let is_currently_viewed_month = date.month() == this.model.visible.month() - && date.year_ce() == this.model.visible.year_ce(); - let is_currently_selected_month = date.month() == this.model.selected.month() - && date.year_ce() == this.model.selected.year_ce(); + let date = first + .checked_add(i.days()) + .expect("valid date in calendar range"); + let is_currently_viewed_month = + date.first_of_month() == this.model.visible.first_of_month(); + let is_currently_selected_month = + date.first_of_month() == this.model.selected.first_of_month(); let is_currently_selected_day = date.day() == this.model.selected.day() && is_currently_selected_month; + let is_today = date == today; calendar_grid = calendar_grid.push(date_button( date, is_currently_viewed_month, is_currently_selected_day, + is_today, &this.on_select, )); } @@ -225,9 +220,8 @@ where .padding([12, 20]) .into(), calendar_grid.into(), - padded_control(crate::widget::divider::horizontal::default()).into(), ]) - .width(315) + .width(360) .padding([8, 0]); Self::new(content_list) @@ -235,21 +229,24 @@ where } fn date_button( - date: NaiveDate, + date: Date, is_currently_viewed_month: bool, is_currently_selected_day: bool, - on_select: &dyn Fn(NaiveDate) -> Message, + is_today: bool, + on_select: &dyn Fn(Date) -> Message, ) -> crate::widget::Button<'static, Message> { let style = if is_currently_selected_day { button::ButtonClass::Suggested + } else if is_today { + button::ButtonClass::Standard } else { button::ButtonClass::Text }; let button = button::custom(text(format!("{}", date.day())).center()) .class(style) - .height(Length::Fixed(36.0)) - .width(Length::Fixed(36.0)); + .height(Length::Fixed(44.0)) + .width(Length::Fixed(44.0)); if is_currently_viewed_month { button.on_press((on_select)(set_day(date, date.day()))) @@ -258,26 +255,10 @@ fn date_button( } } -/// Gets the first date that will be visible on the calender +/// Gets the first date that will be visible on the calendar #[must_use] -pub fn get_calender_first(year: i32, month: u32, from_weekday: Weekday) -> NaiveDate { - let date = NaiveDate::from_ymd_opt(year, month, 1).unwrap(); - let num_days = (date.weekday() as u32 + 7 - from_weekday as u32) % 7; // chrono::Weekday.num_days_from - date.checked_sub_days(Days::new(num_days as u64)).unwrap() -} - -// TODO: Refactor to use same function from applet module. -fn padded_control<'a, Message>( - content: impl Into>, -) -> crate::widget::container::Container<'a, Message, crate::Theme, crate::Renderer> { - crate::widget::container(content) - .padding(menu_control_padding()) - .width(Length::Fill) -} - -#[inline] -fn menu_control_padding() -> Padding { - let guard = crate::theme::THEME.lock().unwrap(); - let cosmic = guard.cosmic(); - [cosmic.space_xxs(), cosmic.space_m()].into() +pub fn get_calendar_first(year: i16, month: i8, from_weekday: Weekday) -> Date { + let date = Date::new(year, month, 1).expect("valid date"); + let num_days = date.weekday().since(from_weekday); + date.checked_sub(num_days.days()).expect("valid date") }