diff --git a/Cargo.lock b/Cargo.lock index 9141a655..3e5c9044 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -630,6 +630,16 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +[[package]] +name = "calendrical_calculations" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dfe3bc6a50b4667fafdb6d9cf26731c5418c457e317d8166c972014facf9a5d" +dependencies = [ + "core_maths", + "displaydoc", +] + [[package]] name = "calloop" version = "0.13.0" @@ -878,6 +888,15 @@ dependencies = [ "libc", ] +[[package]] +name = "core_maths" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b02505ccb8c50b0aa21ace0fc08c3e53adebd4e58caa18a36152803c7709a3" +dependencies = [ + "libm", +] + [[package]] name = "cosmic-app-list" version = "0.1.0" @@ -1110,6 +1129,7 @@ dependencies = [ "chrono", "i18n-embed 0.14.1", "i18n-embed-fl 0.8.0", + "icu", "libcosmic", "once_cell", "rust-embed 8.4.0", @@ -2029,6 +2049,17 @@ dependencies = [ "toml 0.5.11", ] +[[package]] +name = "fixed_decimal" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc7fdec9d7f6671a3ebb3282c969962aba67c49f6abac5311959b65cafabc10" +dependencies = [ + "displaydoc", + "smallvec", + "writeable", +] + [[package]] name = "flate2" version = "1.0.30" @@ -2941,6 +2972,455 @@ dependencies = [ "window_clipboard", ] +[[package]] +name = "icu" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21be1c98fbdb29fff7e34b2939a4f30dad330d4cc20f7be1b3956e21032f67ba" +dependencies = [ + "icu_calendar", + "icu_casemap", + "icu_collator", + "icu_collections", + "icu_compactdecimal", + "icu_datetime", + "icu_decimal", + "icu_displaynames", + "icu_list", + "icu_locid", + "icu_locid_transform", + "icu_normalizer", + "icu_plurals", + "icu_properties", + "icu_provider", + "icu_relativetime", + "icu_segmenter", + "icu_timezone", + "icu_transliterate", +] + +[[package]] +name = "icu_calendar" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb932a690c92f87955e923106181ee0d5682e688ff37fb5c7b296e1fe806edb" +dependencies = [ + "calendrical_calculations", + "displaydoc", + "icu_calendar_data", + "icu_locid", + "icu_locid_transform", + "icu_provider", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_calendar_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22aec7d032735d9acb256eeef72adcac43c3b7572f19b51576a63d664b524ca2" + +[[package]] +name = "icu_casemap" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7988d4f2655012592ac5b027722a93fbe12ff2a86d3e0f9ae686aedba0984f5e" +dependencies = [ + "displaydoc", + "icu_casemap_data", + "icu_collections", + "icu_locid", + "icu_properties", + "icu_provider", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_casemap_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36409fb8ce6f87c408310d87396ac471cc7320e007e648814c607c60fe77cc5" + +[[package]] +name = "icu_collator" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2a45056e541cffde068f5c81ac1c0503b9ee2a4b967546422e509c5c653750" +dependencies = [ + "displaydoc", + "icu_collator_data", + "icu_collections", + "icu_locid", + "icu_locid_transform", + "icu_normalizer", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "zerovec", +] + +[[package]] +name = "icu_collator_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39774016c5b9ad006941f3196fea83ade662d9167eb573111c8f4cc9593e2999" + +[[package]] +name = "icu_collections" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "137d96353afc8544d437e8a99eceb10ab291352699573b0de5b08bda38c78c60" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_compactdecimal" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d3c22da90659121ae1e38bd0fb378d15179c820f56a0c278032fb4eb5a0ca60" +dependencies = [ + "displaydoc", + "fixed_decimal", + "icu_compactdecimal_data", + "icu_decimal", + "icu_locid_transform", + "icu_plurals", + "icu_provider", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_compactdecimal_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53eaf4902ee2e804f2583611722f6961fe85f31eab7ec790f47dde2d1cd494fb" + +[[package]] +name = "icu_datetime" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1508c7ed627cc0b031c81203eb98f34433e24b32b39d5b2c0238e4962a00957d" +dependencies = [ + "displaydoc", + "either", + "fixed_decimal", + "icu_calendar", + "icu_datetime_data", + "icu_decimal", + "icu_locid", + "icu_locid_transform", + "icu_plurals", + "icu_provider", + "icu_timezone", + "litemap", + "smallvec", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_datetime_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6abc569cb4ee80b30707566f05c5c9ed4bed765f91ce41e7f5a37c5e6a75b3f" + +[[package]] +name = "icu_decimal" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf994f9ed8061c17bb313f28fba6cffc736f0a16c7fab827efc9b73fd3f7778" +dependencies = [ + "displaydoc", + "fixed_decimal", + "icu_decimal_data", + "icu_locid", + "icu_locid_transform", + "icu_provider", + "writeable", +] + +[[package]] +name = "icu_decimal_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2de3548316b697c70f30dec1395c9212db09df1d86a27624ee24872b71326c" + +[[package]] +name = "icu_displaynames" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726c0d83ff52f05907275f39e5bb7949a92fa3d09538de60cf73ccf8ee89a613" +dependencies = [ + "icu_displaynames_data", + "icu_locid", + "icu_locid_transform", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_displaynames_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af40e6b723e5e6d9359cf0bb4e4ed6dfb9d6ab16b73b5c82b61f947e88bb30f6" + +[[package]] +name = "icu_list" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6c04ec71ad1bacdbfb47164d4801f80a0533d9340f94f1a880f521eff59f54" +dependencies = [ + "displaydoc", + "icu_list_data", + "icu_locid_transform", + "icu_provider", + "regex-automata 0.2.0", + "writeable", +] + +[[package]] +name = "icu_list_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f6afcf7a9a7fedece70b7f17d7a7ecdfb8df145d37ae46d0277cd1e3932532" + +[[package]] +name = "icu_locid" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0aa2536adc14c07e2a521e95512b75ed8ef832f0fdf9299d4a0a45d2be2a9d" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c17d8f6524fdca4471101dd71f0a132eb6382b5d6d7f2970441cb25f6f435a" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545c6c3e8bf9580e2dafee8de6f9ec14826aaf359787789c7724f1f85f47d3dc" + +[[package]] +name = "icu_normalizer" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183072b0ba2f336279c830a3d594a04168494a726c3c94b50c53d788178cf2c2" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3744fecc0df9ce19999cdaf1f9f3a48c253431ce1d67ef499128fe9d0b607ab" + +[[package]] +name = "icu_plurals" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d807b123eb2a9ae8f12080fb8cce479f5c8a761fba0bb5ab52da6dd5e31a03" +dependencies = [ + "displaydoc", + "fixed_decimal", + "icu_locid", + "icu_locid_transform", + "icu_plurals_data", + "icu_provider", + "zerovec", +] + +[[package]] +name = "icu_plurals_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3acd5f1f2f988ed2dae9316c3d3560dfe4e03a7516d142b4b89b92252ada41a" + +[[package]] +name = "icu_properties" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a89401989d8fdf571b829ce1022801367ec89affc7b1e162b79eff4ae029e69" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70a8b51ee5dd4ff8f20ee9b1dd1bc07afc110886a3747b1fec04cc6e5a15815" + +[[package]] +name = "icu_provider" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba58e782287eb6950247abbf11719f83f5d4e4a5c1f2cd490d30a334bc47c2f4" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2abdd3a62551e8337af119c5899e600ca0c88ec8f23a46c60ba216c803dcf1a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.64", +] + +[[package]] +name = "icu_relativetime" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47825312a5eb0790bad7b718fa8d41a8ea1e0ba597b4f7bb84bcfe97d7fc5aba" +dependencies = [ + "displaydoc", + "fixed_decimal", + "icu_decimal", + "icu_locid_transform", + "icu_plurals", + "icu_provider", + "icu_relativetime_data", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_relativetime_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b55cc15ea8981fbba78e9347d0c4003d4490c85f76e9adc7f270290046cae8" + +[[package]] +name = "icu_segmenter" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2dc1e8f4ba33a6a4956770ac5c08570f255d6605519fb3a859a0c0a270a2f8f" +dependencies = [ + "core_maths", + "displaydoc", + "icu_collections", + "icu_locid", + "icu_provider", + "icu_segmenter_data", + "utf8_iter", + "zerovec", +] + +[[package]] +name = "icu_segmenter_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3673d6698dcffce08cfe8fc5da3c11c3f2c663d5d6137fd58ab2cbf44235ab46" + +[[package]] +name = "icu_timezone" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35aabe571a7c653c0f543ff1512b8a1b2ad481cfa24b3d25115298d2ff3b50f" +dependencies = [ + "displaydoc", + "icu_calendar", + "icu_locid", + "icu_provider", + "icu_timezone_data", + "tinystr", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_timezone_data" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceee21e181cce2ab44e95923da6b3418df75369f570df82264c29c51ca398d4" + +[[package]] +name = "icu_transliterate" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355d06adc2c523000464c6838c824558ce771219341378e9e5577975aa792761" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid", + "icu_normalizer", + "icu_properties", + "icu_provider", + "icu_unicodeset_parse", + "litemap", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_unicodeset_parse" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebbb145196a0b3a17c7278a42eaea52b2b22c34edf4d8123364d00ae2aeb96d1" +dependencies = [ + "icu_collections", + "icu_properties", + "icu_provider", + "tinystr", + "zerovec", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -3355,6 +3835,12 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4" +[[package]] +name = "litemap" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d642685b028806386b2b6e75685faadd3eb65a85fff7df711ce18446a422da" + [[package]] name = "locale_config" version = "0.3.0" @@ -4405,6 +4891,15 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9368763f5a9b804326f3af749e16f9abf378d227bcdee7634b13d8f17793782" +dependencies = [ + "memchr", +] + [[package]] name = "regex-automata" version = "0.4.6" @@ -4963,6 +5458,12 @@ dependencies = [ "bitflags 2.5.0", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -5252,6 +5753,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" dependencies = [ "displaydoc", + "zerovec", ] [[package]] @@ -5654,6 +6156,18 @@ dependencies = [ "tiny-skia-path", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.8.0" @@ -6246,6 +6760,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad7bb64b8ef9c0aa27b6da38b452b0ee9fd82beaf276a87dd796fb55cbae14e" + [[package]] name = "x11rb" version = "0.13.1" @@ -6365,6 +6891,30 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" +[[package]] +name = "yoke" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e71b2e4f287f467794c671e2b8f8a5f3716b3c829079a1c44740148eff07e4" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.64", + "synstructure", +] + [[package]] name = "zbus" version = "3.15.1" @@ -6520,6 +7070,60 @@ dependencies = [ "syn 2.0.64", ] +[[package]] +name = "zerofrom" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655b0814c5c0b19ade497851070c640773304939a6c0fd5f5fb43da0696d05b7" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.64", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0594125a0574fb93059c92c588ab209cc036a23d1baeb3410fa9181bea551a0" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff4439ae91fb5c72b8abc12f3f2dbf51bd27e6eadb9f8a5bc8898dddb0e27ea" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4e5997cbf58990550ef1f0e5124a05e47e1ebd33a84af25739be6031a62c20" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.64", +] + [[package]] name = "zune-inflate" version = "0.2.54" diff --git a/cosmic-applet-time/Cargo.toml b/cosmic-applet-time/Cargo.toml index 0fa2700a..9ae0ea76 100644 --- a/cosmic-applet-time/Cargo.toml +++ b/cosmic-applet-time/Cargo.toml @@ -15,3 +15,4 @@ tokio = { version = "1.36.0", features = ["time"] } tracing-log.workspace = true tracing-subscriber.workspace = true tracing.workspace = true +icu = { version = "1.4.0", features = ["experimental", "compiled_data", "icu_datetime_experimental"]} \ No newline at end of file diff --git a/cosmic-applet-time/src/config.rs b/cosmic-applet-time/src/config.rs index c690e53a..18424c97 100644 --- a/cosmic-applet-time/src/config.rs +++ b/cosmic-applet-time/src/config.rs @@ -10,6 +10,7 @@ pub struct TimeAppletConfig { pub military_time: bool, pub first_day_of_week: u8, pub show_date_in_top_panel: bool, + pub show_weekday: bool, } impl Default for TimeAppletConfig { @@ -18,6 +19,7 @@ impl Default for TimeAppletConfig { military_time: false, first_day_of_week: 6, show_date_in_top_panel: true, + show_weekday: false, } } } diff --git a/cosmic-applet-time/src/window.rs b/cosmic-applet-time/src/window.rs index 7bad2cd8..7664e056 100644 --- a/cosmic-applet-time/src/window.rs +++ b/cosmic-applet-time/src/window.rs @@ -1,16 +1,21 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only +use std::borrow::Cow; +use std::str::FromStr; + +use chrono::{Datelike, DurationRound, Timelike}; use cosmic::applet::{menu_button, padded_control}; use cosmic::cctk::sctk::reexports::calloop; +use cosmic::iced::subscription; use cosmic::iced::wayland::popup::{destroy_popup, get_popup}; use cosmic::iced::{ - subscription, widget::{column, row, text, vertical_space}, window, Alignment, Length, Rectangle, Subscription, }; use cosmic::iced_core::alignment::{Horizontal, Vertical}; use cosmic::iced_style::application; +use cosmic::iced_widget::{horizontal_rule, Column}; use cosmic::widget::{button, container, divider, grid, horizontal_space, Button, Grid, Space}; use cosmic::{app, applet::cosmic_panel_config::PanelAnchor, Command}; use cosmic::{ @@ -18,7 +23,11 @@ use cosmic::{ Element, Theme, }; -use chrono::{DateTime, Datelike, DurationRound, Local, Months, NaiveDate, Weekday}; +use icu::calendar::DateTime; +use icu::datetime::options::components::{self, Bag}; +use icu::datetime::options::preferences; +use icu::datetime::{DateTimeFormatter, DateTimeFormatterOptions}; +use icu::locid::Locale; use crate::config::TimeAppletConfig; use crate::fl; @@ -27,23 +36,19 @@ use cosmic::applet::token::subscription::{ activation_token_subscription, TokenRequest, TokenUpdate, }; -#[allow(dead_code)] -#[derive(Debug, Clone, Copy)] -enum Every { - Minute, - Second, -} +/// In order to keep the understandable, the chrono types are not globals, +/// to avoid conflict with icu pub struct Window { core: cosmic::app::Core, popup: Option, - update_at: Every, - now: DateTime, - date_selected: NaiveDate, + now: chrono::DateTime, + date_selected: chrono::NaiveDate, rectangle_tracker: Option>, rectangle: Rectangle, token_tx: Option>, config: TimeAppletConfig, + locale: Locale, } #[derive(Debug, Clone)] @@ -60,6 +65,33 @@ pub enum Message { ConfigChanged(TimeAppletConfig), } +impl Window { + fn format(&self, bag: Bag, date: &D) -> String { + let options = DateTimeFormatterOptions::Components(bag); + + let dtf = + DateTimeFormatter::try_new_experimental(&self.locale.clone().into(), options).unwrap(); + + let datetime = DateTime::try_new_gregorian_datetime( + date.year(), + date.month() as u8, + date.day() as u8, + // hack cause we know that we will only use "now" + // when we need hours (NaiveDate don't support this functions) + self.now.hour() as u8, + self.now.minute() as u8, + self.now.second() as u8, + ) + .unwrap() + .to_iso() + .to_any(); + + dtf.format(&datetime) + .expect("can't format value") + .to_string() + } +} + impl cosmic::Application for Window { type Message = Message; type Executor = cosmic::SingleThreadExecutor; @@ -70,18 +102,38 @@ impl cosmic::Application for Window { core: app::Core, _flags: Self::Flags, ) -> (Self, cosmic::iced::Command>) { - let now = Local::now(); + fn get_local() -> Result> { + let locale = std::env::var("LANG")?; + let locale = locale + .split('.') + .next() + .ok_or(format!("Can't split the locale {locale}"))?; + + let locale = Locale::from_str(locale).map_err(|e| format!("{e:?}"))?; + Ok(locale) + } + + let locale = match get_local() { + Ok(locale) => locale, + Err(e) => { + tracing::error!("can't get locale {e}"); + Locale::default() + } + }; + + let now: chrono::prelude::DateTime = chrono::Local::now(); + ( Self { core, popup: None, - update_at: Every::Minute, now, - date_selected: NaiveDate::from(now.naive_local()), + date_selected: chrono::NaiveDate::from(now.naive_local()), rectangle_tracker: None, rectangle: Rectangle::default(), token_tx: None, config: TimeAppletConfig::default(), + locale, }, Command::none(), ) @@ -100,9 +152,22 @@ impl cosmic::Application for Window { } fn subscription(&self) -> Subscription { + fn time_subscription() -> Subscription<()> { + subscription::unfold("time-sub", (), move |()| async move { + let now = chrono::Local::now(); + let update_delay = chrono::TimeDelta::minutes(1); + + let duration = ((now + update_delay).duration_trunc(update_delay).unwrap() - now) + .to_std() + .unwrap(); + tokio::time::sleep(duration).await; + ((), ()) + }) + } + Subscription::batch(vec![ rectangle_tracker_subscription(0).map(|e| Message::Rectangle(e.1)), - time_subscription(self.update_at).map(|_| Message::Tick), + time_subscription().map(|_| Message::Tick), activation_token_subscription(0).map(Message::Token), self.core.watch_config(Self::APP_ID).map(|u| { for err in u.errors { @@ -122,7 +187,7 @@ impl cosmic::Application for Window { if let Some(p) = self.popup.take() { destroy_popup(p) } else { - self.date_selected = NaiveDate::from(self.now.naive_local()); + self.date_selected = chrono::NaiveDate::from(self.now.naive_local()); let new_id = window::Id::unique(); self.popup.replace(new_id); @@ -150,7 +215,7 @@ impl cosmic::Application for Window { } } Message::Tick => { - self.now = Local::now(); + self.now = chrono::Local::now(); Command::none() } Message::Rectangle(u) => { @@ -179,7 +244,10 @@ impl cosmic::Application for Window { Command::none() } Message::PreviousMonth => { - if let Some(date) = self.date_selected.checked_sub_months(Months::new(1)) { + if let Some(date) = self + .date_selected + .checked_sub_months(chrono::Months::new(1)) + { self.date_selected = date; } else { tracing::error!("invalid naivedate"); @@ -187,7 +255,10 @@ impl cosmic::Application for Window { Command::none() } Message::NextMonth => { - if let Some(date) = self.date_selected.checked_add_months(Months::new(1)) { + if let Some(date) = self + .date_selected + .checked_add_months(chrono::Months::new(1)) + { self.date_selected = date; } else { tracing::error!("invalid naivedate"); @@ -238,19 +309,41 @@ impl cosmic::Application for Window { self.core.applet.anchor, PanelAnchor::Top | PanelAnchor::Bottom ); + let button = cosmic::widget::button(if horizontal { - let format = match ( - self.config.military_time, - self.config.show_date_in_top_panel, - ) { - (true, true) => "%b %-d %H:%M", - (true, false) => "%H:%M", - (false, true) => "%b %-d %-I:%M %p", - (false, false) => "%-I:%M %p", + let mut time: Vec> = Vec::new(); + + if self.config.show_date_in_top_panel { + let mut date_bag = Bag::empty(); + + if self.config.show_weekday { + date_bag.weekday = Some(components::Text::Short); + } + + date_bag.day = Some(components::Day::NumericDayOfMonth); + date_bag.month = Some(components::Month::Long); + + time.push(format!("{} ", self.format(date_bag, &self.now)).into()); + } + + let mut time_bag = Bag::empty(); + + time_bag.hour = Some(components::Numeric::Numeric); + time_bag.minute = Some(components::Numeric::Numeric); + + let hour_cycle = if self.config.military_time { + preferences::HourCycle::H23 + } else { + preferences::HourCycle::H12 }; + + time_bag.preferences = Some(preferences::Bag::from_hour_cycle(hour_cycle)); + + time.push(self.format(time_bag, &self.now).into()); + Element::from( row!( - self.core.applet.text(self.now.format(format).to_string()), + self.core.applet.text(time.concat()), container(vertical_space(Length::Fixed( (self.core.applet.suggested_size(true).1 + 2 * self.core.applet.suggested_padding(true)) @@ -260,32 +353,54 @@ impl cosmic::Application for Window { .align_items(Alignment::Center), ) } else { - let mut date_time_col = if self.config.military_time { - column![ - self.core.applet.text(self.now.format("%H").to_string()), - self.core.applet.text(self.now.format("%M").to_string()) - ] - } else { - column![ - self.core.applet.text(self.now.format("%I").to_string()), - self.core.applet.text(self.now.format("%M").to_string()), - self.core.applet.text(self.now.format("%p").to_string()), - ] - } - .align_items(Alignment::Center) - .spacing(4); + // vertical layout + + let mut elements = Vec::new(); + if self.config.show_date_in_top_panel { - date_time_col = date_time_col.push(vertical_space(Length::Fixed(4.0))); - date_time_col = date_time_col.push( - // TODO better calendar icon? - icon::from_name("calendar-go-today-symbolic") - .size(self.core.applet.suggested_size(true).0) - .symbolic(true), - ); - for d in self.now.format("%x").to_string().split('/') { - date_time_col = date_time_col.push(self.core.applet.text(d.to_string())); + let mut date_bag = Bag::empty(); + + date_bag.day = Some(components::Day::NumericDayOfMonth); + date_bag.month = Some(components::Month::Long); + + let formated = self.format(date_bag, &self.now); + + for p in formated.split_whitespace() { + elements.push(self.core.applet.text(p.to_owned()).into()); } + + elements.push( + horizontal_rule(2) + .width(self.core.applet.suggested_size(true).0) + .into(), + ) + } + + let mut time_bag: Bag = Bag::empty(); + + time_bag.hour = Some(components::Numeric::Numeric); + time_bag.minute = Some(components::Numeric::Numeric); + + let hour_cycle = if self.config.military_time { + preferences::HourCycle::H23 + } else { + preferences::HourCycle::H12 + }; + + time_bag.preferences = Some(preferences::Bag::from_hour_cycle(hour_cycle)); + + let formated = self.format(time_bag, &self.now); + + // todo: split using formatToParts when it is implemented + // https://github.com/unicode-org/icu4x/issues/4936#issuecomment-2128812667 + for p in formated.split_whitespace().flat_map(|s| s.split(':')) { + elements.push(self.core.applet.text(p.to_owned()).into()); } + + let date_time_col = Column::with_children(elements) + .align_items(Alignment::Center) + .spacing(4); + Element::from( column!( date_time_col, @@ -314,8 +429,17 @@ impl cosmic::Application for Window { } fn view_window(&self, _id: window::Id) -> Element { - let date = text(self.date_selected.format("%B %-d, %Y").to_string()).size(18); - let day_of_week = text(self.date_selected.format("%A").to_string()).size(14); + let mut date_bag = Bag::empty(); + date_bag.month = Some(components::Month::Long); + date_bag.day = Some(components::Day::NumericDayOfMonth); + date_bag.year = Some(components::Year::Numeric); + + let date = text(self.format(date_bag, &self.date_selected)).size(18); + + let mut day_of_week_bag = Bag::empty(); + day_of_week_bag.weekday = Some(components::Text::Long); + + let day_of_week = text(self.format(day_of_week_bag, &self.date_selected)).size(14); let month_controls = row![ button::icon(icon::from_name("go-previous-symbolic")) @@ -327,12 +451,25 @@ impl cosmic::Application for Window { ]; // Calender + let mut calender: Grid<'_, Message> = grid().width(Length::Fill); - let mut first_day_of_week = - Weekday::try_from(self.config.first_day_of_week).unwrap_or(Weekday::Sun); + let mut first_day_of_week = chrono::Weekday::try_from(self.config.first_day_of_week) + .unwrap_or(chrono::Weekday::Sun); + + let first_day = get_calender_first( + self.date_selected.year(), + self.date_selected.month(), + first_day_of_week, + ); + + let mut weekday_bag = Bag::empty(); + weekday_bag.weekday = Some(components::Text::Short); + + let mut day_iter = first_day.iter_days(); + for _ in 0..7 { calender = calender.push( - text(first_day_of_week) + text(self.format(weekday_bag, &day_iter.next().unwrap())) .size(12) .width(Length::Fixed(36.0)) .horizontal_alignment(Horizontal::Center), @@ -342,12 +479,7 @@ impl cosmic::Application for Window { } calender = calender.insert_row(); - let monday = get_calender_first( - self.date_selected.year(), - self.date_selected.month(), - first_day_of_week, - ); - let mut day_iter = monday.iter_days(); + let mut day_iter = first_day.iter_days(); for i in 0..42 { if i > 0 && i % 7 == 0 { calender = calender.insert_row(); @@ -410,18 +542,3 @@ fn date_button(day: u32, is_month: bool, is_day: bool) -> Button<'static, Messag button } } - -fn time_subscription(update_at: Every) -> Subscription<()> { - subscription::unfold("time-sub", (), move |()| async move { - let now = Local::now(); - let update_delay = match update_at { - Every::Minute => chrono::TimeDelta::minutes(1), - Every::Second => chrono::TimeDelta::seconds(1), - }; - let duration = ((now + update_delay).duration_trunc(update_delay).unwrap() - now) - .to_std() - .unwrap(); - tokio::time::sleep(duration).await; - ((), ()) - }) -}