feat: automatic dates; timezone setting support

This commit is contained in:
Michael Murphy 2024-08-02 17:59:09 +02:00 committed by GitHub
parent a9dda39526
commit 36542ea0b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 593 additions and 147 deletions

370
Cargo.lock generated
View file

@ -287,7 +287,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093"
dependencies = [
"async-fs 2.1.2",
"async-fs",
"async-net",
"enumflags2",
"futures-channel",
@ -377,18 +377,6 @@ dependencies = [
"slab",
]
[[package]]
name = "async-fs"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
dependencies = [
"async-lock 2.8.0",
"autocfg",
"blocking",
"futures-lite 1.13.0",
]
[[package]]
name = "async-fs"
version = "2.1.2"
@ -1485,7 +1473,6 @@ dependencies = [
"cosmic-settings-config",
"cosmic-settings-page",
"cosmic-settings-system",
"cosmic-settings-time",
"cosmic-settings-wallpaper",
"derivative",
"derive_setters",
@ -1498,6 +1485,7 @@ dependencies = [
"hostname1-zbus",
"i18n-embed",
"i18n-embed-fl",
"icu",
"image 0.25.2",
"itertools 0.13.0",
"itoa",
@ -1513,6 +1501,7 @@ dependencies = [
"static_init",
"sunrise",
"tachyonix",
"timedate-zbus",
"tokio",
"tracing",
"tracing-subscriber",
@ -1571,16 +1560,6 @@ dependencies = [
"sysinfo",
]
[[package]]
name = "cosmic-settings-time"
version = "0.1.0"
dependencies = [
"icu_calendar",
"icu_timezone",
"timedate-zbus",
"zbus 3.15.2",
]
[[package]]
name = "cosmic-settings-wallpaper"
version = "0.1.0"
@ -2163,6 +2142,17 @@ dependencies = [
"toml 0.5.11",
]
[[package]]
name = "fixed_decimal"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0febbeb1118a9ecdee6e4520ead6b54882e843dd0592ad233247dbee84c53db8"
dependencies = [
"displaydoc",
"smallvec",
"writeable",
]
[[package]]
name = "flate2"
version = "1.0.30"
@ -3136,6 +3126,30 @@ dependencies = [
"objc2",
]
[[package]]
name = "icu"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff5e3018d703f168b00dcefa540a65f1bbc50754ae32f3f5f0e43fe5ee51502"
dependencies = [
"icu_calendar",
"icu_casemap",
"icu_collator",
"icu_collections",
"icu_datetime",
"icu_decimal",
"icu_experimental",
"icu_list",
"icu_locid",
"icu_locid_transform",
"icu_normalizer",
"icu_plurals",
"icu_properties",
"icu_provider",
"icu_segmenter",
"icu_timezone",
]
[[package]]
name = "icu_calendar"
version = "1.5.2"
@ -3159,6 +3173,171 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e009b7f0151ee6fb28c40b1283594397e0b7183820793e9ace3dcd13db126d0"
[[package]]
name = "icu_casemap"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff0c8ae9f8d31b12e27fc385ff9ab1f3cd9b17417c665c49e4ec958c37da75f"
dependencies = [
"displaydoc",
"icu_casemap_data",
"icu_collections",
"icu_locid",
"icu_properties",
"icu_provider",
"writeable",
"zerovec",
]
[[package]]
name = "icu_casemap_data"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d57966d5ab748f74513be4046867f9a20e801e2775d41f91d04a0f560b61f08"
[[package]]
name = "icu_collator"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d370371887d31d56f361c3eaa15743e54f13bc677059c9191c77e099ed6966b2"
dependencies = [
"displaydoc",
"icu_collator_data",
"icu_collections",
"icu_locid_transform",
"icu_normalizer",
"icu_properties",
"icu_provider",
"smallvec",
"utf16_iter",
"utf8_iter",
"zerovec",
]
[[package]]
name = "icu_collator_data"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee3f88741364b7d6269cce6827a3e6a8a2cf408a78f766c9224ab479d5e4ae5"
[[package]]
name = "icu_collections"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_datetime"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d115efb85e08df3fd77e77f52e7e087545a783fffba8be80bfa2102f306b1780"
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.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ba7e7f7a01269b9afb0a39eff4f8676f693b55f509b3120e43a0350a9f88bea"
[[package]]
name = "icu_decimal"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb8fd98f86ec0448d85e1edf8884e4e318bb2e121bd733ec929a05c0a5e8b0eb"
dependencies = [
"displaydoc",
"fixed_decimal",
"icu_decimal_data",
"icu_locid_transform",
"icu_provider",
"writeable",
]
[[package]]
name = "icu_decimal_data"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d424c994071c6f5644f999925fc868c85fec82295326e75ad5017bc94b41523"
[[package]]
name = "icu_experimental"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "844ad7b682a165c758065d694bc4d74ac67f176da1c499a04d85d492c0f193b7"
dependencies = [
"displaydoc",
"fixed_decimal",
"icu_collections",
"icu_decimal",
"icu_experimental_data",
"icu_locid",
"icu_locid_transform",
"icu_normalizer",
"icu_pattern",
"icu_plurals",
"icu_properties",
"icu_provider",
"litemap",
"num-bigint",
"num-rational",
"num-traits",
"smallvec",
"tinystr",
"writeable",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_experimental_data"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c178b9a34083fca5bd70d61f647575335e9c197d0f30c38e8ccd187babc69d0"
[[package]]
name = "icu_list"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfeda1d7775b6548edd4e8b7562304a559a91ed56ab56e18961a053f367c365"
dependencies = [
"displaydoc",
"icu_list_data",
"icu_locid_transform",
"icu_provider",
"regex-automata 0.2.0",
"writeable",
]
[[package]]
name = "icu_list_data"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1825170d2c6679cb20dbd96a589d034e49f698aed9a2ef4fafc9a0101ed298f"
[[package]]
name = "icu_locid"
version = "1.5.0"
@ -3192,6 +3371,84 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
[[package]]
name = "icu_normalizer"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
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.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
[[package]]
name = "icu_pattern"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7f36aafd098d6717de34e668a8120822275c1fba22b936e757b7de8a2fd7e4"
dependencies = [
"displaydoc",
"either",
"writeable",
"yoke",
"zerofrom",
]
[[package]]
name = "icu_plurals"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a70e7c025dbd5c501b0a5c188cd11666a424f0dadcd4f0a95b7dafde3b114"
dependencies = [
"displaydoc",
"fixed_decimal",
"icu_locid_transform",
"icu_plurals_data",
"icu_provider",
"zerovec",
]
[[package]]
name = "icu_plurals_data"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e3e8f775b215d45838814a090a2227247a7431d74e9156407d9c37f6ef0f208"
[[package]]
name = "icu_properties"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
dependencies = [
"displaydoc",
"icu_collections",
"icu_locid_transform",
"icu_properties_data",
"icu_provider",
"tinystr",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
[[package]]
name = "icu_provider"
version = "1.5.0"
@ -3220,6 +3477,28 @@ dependencies = [
"syn 2.0.72",
]
[[package]]
name = "icu_segmenter"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a717725612346ffc2d7b42c94b820db6908048f39434504cb130e8b46256b0de"
dependencies = [
"core_maths",
"displaydoc",
"icu_collections",
"icu_locid",
"icu_provider",
"icu_segmenter_data",
"utf8_iter",
"zerovec",
]
[[package]]
name = "icu_segmenter_data"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f739ee737260d955e330bc83fdeaaf1631f7fb7ed218761d3c04bb13bb7d79df"
[[package]]
name = "icu_timezone"
version = "1.5.0"
@ -5015,6 +5294,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.7"
@ -6024,10 +6312,9 @@ dependencies = [
[[package]]
name = "timedate-zbus"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "609052c48ae1d9e27999ac4a159843a290bffab5fdc13856889b940aefc185b7"
source = "git+https://github.com/pop-os/dbus-settings-bindings#cd21ddcb1b5cbfc80ab84b34d3c8b1ff3d81179a"
dependencies = [
"zbus 3.15.2",
"zbus 4.4.0",
]
[[package]]
@ -6532,12 +6819,24 @@ 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-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "utf8parse"
version = "0.2.2"
@ -7339,11 +7638,20 @@ 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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
dependencies = [
"either",
]
[[package]]
name = "wyz"
@ -7515,15 +7823,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6"
dependencies = [
"async-broadcast 0.5.1",
"async-executor",
"async-fs 1.6.0",
"async-io 1.13.0",
"async-lock 2.8.0",
"async-process 1.8.1",
"async-recursion",
"async-task",
"async-trait",
"blocking",
"byteorder",
"derivative",
"enumflags2",
@ -7558,7 +7860,7 @@ checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
dependencies = [
"async-broadcast 0.7.1",
"async-executor",
"async-fs 2.1.2",
"async-fs",
"async-io 2.3.3",
"async-lock 3.4.0",
"async-process 2.2.3",

View file

@ -20,7 +20,6 @@ cosmic-randr.workspace = true
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon" }
cosmic-settings-page = { path = "../page" }
cosmic-settings-system = { path = "../pages/system" }
cosmic-settings-time = { path = "../pages/time" }
cosmic-settings-wallpaper = { path = "../pages/wallpapers" }
derivative = "2.2.0"
derive_setters = "0.1.6"
@ -31,6 +30,7 @@ futures = { package = "futures-lite", version = "2.3.0" }
generator = "=0.8.1"
hostname-validator = "1.1.1"
hostname1-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings" }
timedate-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings" }
i18n-embed-fl = "0.8.0"
image = "0.25"
itertools = "0.13.0"
@ -55,6 +55,10 @@ zbus = { version = "4.4.0", features = ["tokio"] }
tachyonix = "0.3.0"
slab = "0.4.9"
[dependencies.icu]
version = "1.4.0"
features = ["experimental", "compiled_data", "icu_datetime_experimental"]
[dependencies.i18n-embed]
version = "0.14.1"
features = ["fluent-system", "desktop-requester"]

View file

@ -1,7 +1,7 @@
use crate::app;
use cosmic::{
cosmic_config::{self, ConfigGet, ConfigSet},
iced,
Command,
};
use cosmic_comp_config::input::{
AccelConfig, AccelProfile, ClickMethod, InputConfig, ScrollConfig, ScrollMethod, TapButtonMap,
@ -93,7 +93,7 @@ impl Page {
}
#[allow(clippy::too_many_lines)]
pub fn update(&mut self, message: Message) -> iced::Command<app::Message> {
pub fn update(&mut self, message: Message) -> Command<app::Message> {
match message {
Message::SetAcceleration(value, touchpad) => {
let profile = if value {
@ -152,7 +152,7 @@ impl Page {
select_model.activate(entity);
let Some(left_entity) = select_model.entity_at(1) else {
return cosmic::Command::none();
return Command::none();
};
let left_handed = select_model.active() == left_entity;
@ -173,7 +173,7 @@ impl Page {
}
}
cosmic::Command::none()
Command::none()
}
}

View file

@ -1,53 +1,85 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use std::str::FromStr;
use chrono::{Datelike, Timelike};
use cosmic::{
cosmic_config::{self, ConfigGet, ConfigSet},
iced::{widget::horizontal_space, Length},
widget::{dropdown, settings},
Apply,
widget::{self, dropdown, settings},
Apply, Command,
};
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use icu::{
calendar::{DateTime, Iso},
datetime::DateTimeFormatter,
locid::Locale,
};
use slab::Slab;
use slotmap::SlotMap;
pub use timedate_zbus::TimeDateProxy;
use tracing::error;
crate::cache_dynamic_lazy! {
static WEEKDAYS: [String; 4] = [fl!("time-format", "friday"), fl!("time-format", "saturday"), fl!("time-format", "sunday"), fl!("time-format", "monday")];
}
#[derive(Debug, Clone)]
pub struct Info {
pub ntp_enabled: bool,
pub timezone_id: Option<usize>,
pub timezone_list: Vec<String>,
}
pub struct Page {
config: cosmic_config::Config,
auto: bool,
auto_timezone: bool,
military_time: bool,
cosmic_applet_config: cosmic_config::Config,
first_day_of_week: usize,
military_time: bool,
ntp_enabled: bool,
show_date_in_top_panel: bool,
local_time: Option<DateTime<Iso>>,
timezone: Option<usize>,
timezone_list: Vec<String>,
formatted_date: String,
}
impl Default for Page {
fn default() -> Self {
let config = cosmic_config::Config::new("com.system76.CosmicAppletTime", 1).unwrap();
let military_time = config.get("military_time").unwrap_or_else(|err| {
error!(?err, "Failed to read config 'military_time'");
false
});
let first_day_of_week = config.get("first_day_of_week").unwrap_or_else(|err| {
error!(?err, "Failed to read config 'first_day_of_week'");
6
});
let show_date_in_top_panel = config.get("show_date_in_top_panel").unwrap_or_else(|err| {
error!(?err, "Failed to read config 'show_date_in_top_panel'");
true
});
let cosmic_applet_config =
cosmic_config::Config::new("com.system76.CosmicAppletTime", 1).unwrap();
let military_time = cosmic_applet_config
.get("military_time")
.unwrap_or_else(|err| {
error!(?err, "Failed to read config 'military_time'");
false
});
let first_day_of_week = cosmic_applet_config
.get("first_day_of_week")
.unwrap_or_else(|err| {
error!(?err, "Failed to read config 'first_day_of_week'");
6
});
let show_date_in_top_panel = cosmic_applet_config
.get("show_date_in_top_panel")
.unwrap_or_else(|err| {
error!(?err, "Failed to read config 'show_date_in_top_panel'");
true
});
Self {
config,
auto: false,
auto_timezone: false,
military_time,
cosmic_applet_config,
first_day_of_week,
formatted_date: String::new(),
local_time: None,
military_time,
ntp_enabled: false,
show_date_in_top_panel,
timezone: None,
timezone_list: Vec::new(),
}
}
}
@ -69,42 +101,168 @@ impl page::Page<crate::pages::Message> for Page {
.title(fl!("time-date"))
.description(fl!("time-date", "desc"))
}
fn on_enter(
&mut self,
_page: cosmic_settings_page::Entity,
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
) -> Command<crate::pages::Message> {
cosmic::command::future(async move {
let client = match zbus::Connection::system().await {
Ok(client) => client,
Err(why) => {
return Message::Error(why.to_string());
}
};
let timedate_proxy = match TimeDateProxy::new(&client).await {
Ok(timedate_proxy) => timedate_proxy,
Err(why) => {
return Message::Error(why.to_string());
}
};
let can_ntp = timedate_proxy.can_ntp().await.unwrap_or_default();
let ntp_enabled = can_ntp && timedate_proxy.ntp().await.unwrap_or_default();
let timezone_list = timedate_proxy.list_timezones().await.unwrap_or_default();
let timezone = timedate_proxy.timezone().await.unwrap_or_default();
Message::Refresh(Info {
ntp_enabled,
timezone_id: timezone_list.iter().position(|tz| tz == &timezone),
timezone_list,
})
})
.map(crate::pages::Message::DateAndTime)
}
}
impl Page {
pub fn update(&mut self, message: Message) {
pub fn update(&mut self, message: Message) -> Command<crate::Message> {
match message {
Message::Automatic(enable) => self.auto = enable,
Message::AutomaticTimezone(enable) => self.auto_timezone = enable,
Message::Automatic(enable) => {
self.ntp_enabled = enable;
tokio::task::spawn(async move {
let client = match zbus::Connection::system().await {
Ok(client) => client,
Err(why) => {
tracing::error!(?why, "zbus client error");
return;
}
};
let timedate_proxy = match TimeDateProxy::new(&client).await {
Ok(timedate_proxy) => timedate_proxy,
Err(why) => {
tracing::error!(?why, "zbus client error");
return;
}
};
_ = timedate_proxy.set_ntp(enable, true).await;
});
}
Message::MilitaryTime(enable) => {
self.military_time = enable;
if let Err(err) = self.config.set("military_time", enable) {
self.update_local_time();
if let Err(err) = self.cosmic_applet_config.set("military_time", enable) {
error!(?err, "Failed to set config 'military_time'");
}
}
Message::FirstDayOfWeek(weekday) => {
self.first_day_of_week = weekday;
if let Err(err) = self.config.set("first_day_of_week", weekday) {
if let Err(err) = self.cosmic_applet_config.set("first_day_of_week", weekday) {
error!(?err, "Failed to set config 'first_day_of_week'");
}
}
Message::ShowDate(enable) => {
self.show_date_in_top_panel = enable;
if let Err(err) = self.config.set("show_date_in_top_panel", enable) {
if let Err(err) = self
.cosmic_applet_config
.set("show_date_in_top_panel", enable)
{
error!(?err, "Failed to set config 'show_date_in_top_panel'");
}
}
Message::Timezone(timezone_id) => {
self.timezone = Some(timezone_id);
if let Some(timezone) = self.timezone_list.get(timezone_id).cloned() {
return cosmic::command::future(async move {
let client = match zbus::Connection::system().await {
Ok(client) => client,
Err(why) => {
return Message::Error(why.to_string());
}
};
let timedate_proxy = match TimeDateProxy::new(&client).await {
Ok(timedate_proxy) => timedate_proxy,
Err(why) => {
return Message::Error(why.to_string());
}
};
match timedate_proxy.set_timezone(&timezone, true).await {
Ok(_) => Message::UpdateTime,
Err(why) => Message::Error(why.to_string()),
}
})
.map(crate::pages::Message::DateAndTime)
.map(crate::Message::PageMessage);
}
}
Message::Error(why) => {
tracing::error!(why, "failed to set timezone");
}
Message::UpdateTime => self.update_local_time(),
Message::Refresh(info) => {
self.ntp_enabled = info.ntp_enabled;
self.timezone_list = info.timezone_list;
self.timezone = info.timezone_id;
self.update_local_time();
}
Message::None => (),
}
Command::none()
}
pub fn update_local_time(&mut self) {
self.local_time = Some(update_local_time());
self.formatted_date = match self.local_time {
Some(ref time) => format_date(time, self.military_time),
None => fl!("unknown"),
}
}
}
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
pub enum Message {
Automatic(bool),
AutomaticTimezone(bool),
Error(String),
MilitaryTime(bool),
None,
FirstDayOfWeek(usize),
Refresh(Info),
ShowDate(bool),
Timezone(usize),
UpdateTime,
}
impl page::AutoBind<crate::pages::Message> for Page {}
@ -122,11 +280,11 @@ fn date() -> Section<crate::pages::Message> {
settings::view_section(&section.title)
.add(
settings::item::builder(&*section.descriptions[auto])
.toggler(page.auto, Message::Automatic),
.toggler(page.ntp_enabled, Message::Automatic),
)
.add(settings::item(
&*section.descriptions[title],
horizontal_space(Length::Fill),
widget::text(&page.formatted_date),
))
.apply(cosmic::Element::from)
.map(crate::pages::Message::DateAndTime)
@ -183,8 +341,6 @@ fn format() -> Section<crate::pages::Message> {
fn timezone() -> Section<crate::pages::Message> {
let mut descriptions = Slab::new();
let auto = descriptions.insert(fl!("time-zone", "auto"));
let auto_info = descriptions.insert(fl!("time-zone", "auto-info"));
let time_zone = descriptions.insert(fl!("time-zone"));
Section::default()
@ -192,18 +348,66 @@ fn timezone() -> Section<crate::pages::Message> {
.descriptions(descriptions)
.view::<Page>(move |_binder, page, section| {
settings::view_section(&section.title)
// Automatic timezone toggle
.add(
settings::item::builder(&*section.descriptions[auto])
.description(&*section.descriptions[auto_info])
.toggler(page.auto_timezone, Message::AutomaticTimezone),
)
// Time zone select
.add(
settings::item::builder(&*section.descriptions[time_zone])
.control(horizontal_space(Length::Fill)),
settings::item::builder(&*section.descriptions[time_zone]).control(
widget::dropdown(&page.timezone_list, page.timezone, Message::Timezone),
),
)
.apply(cosmic::Element::from)
.map(crate::pages::Message::DateAndTime)
})
}
fn locale() -> Result<Locale, Box<dyn std::error::Error>> {
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)
}
fn format_date(date: &DateTime<Iso>, military: bool) -> String {
let Ok(locale) = locale() else {
return String::new();
};
let mut bag = icu::datetime::options::components::Bag::empty();
bag.year = Some(icu::datetime::options::components::Year::Numeric);
bag.day = Some(icu::datetime::options::components::Day::NumericDayOfMonth);
bag.month = Some(icu::datetime::options::components::Month::Long);
bag.hour = Some(icu::datetime::options::components::Numeric::Numeric);
bag.minute = Some(icu::datetime::options::components::Numeric::Numeric);
bag.preferences = Some(icu::datetime::options::preferences::Bag::from_hour_cycle(
if military {
icu::datetime::options::preferences::HourCycle::H23
} else {
icu::datetime::options::preferences::HourCycle::H12
},
));
let dtf = DateTimeFormatter::try_new_experimental(&locale.into(), bag.into()).unwrap();
dtf.format(&date.to_any())
.expect("can't format value")
.to_string()
}
fn update_local_time() -> DateTime<Iso> {
let now = chrono::Local::now();
DateTime::try_new_gregorian_datetime(
now.year(),
now.month() as u8,
now.day() as u8,
now.hour() as u8,
now.minute() as u8,
now.second() as u8,
)
.unwrap()
.to_iso()
}

View file

@ -1,14 +0,0 @@
[package]
name = "cosmic-settings-time"
version = "0.1.0"
edition = "2021"
[dependencies]
icu_calendar = "1.5.2"
icu_timezone = "1.5.0"
timedate-zbus = "0.1.0"
[dependencies.zbus]
version = "3.15.2"
default-features = false
features = ["tokio"]

View file

@ -1,50 +0,0 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use std::time::SystemTime;
use icu_calendar::{types::IsoSecond, DateTime, Iso};
use icu_timezone::CustomTimeZone;
use timedate_zbus::TimeDateProxy;
pub struct Info {
pub can_ntp: bool,
pub timezone: CustomTimeZone,
pub local_time: DateTime<Iso>,
}
impl Info {
pub async fn load(proxy: &TimeDateProxy<'_>) -> Option<Info> {
let can_ntp = proxy.can_ntp().await.unwrap_or_default();
let Ok(timezone) = proxy
.timezone()
.await
.unwrap_or_default()
.parse::<CustomTimeZone>()
else {
return None;
};
let Ok(duration) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) else {
return None;
};
let seconds = duration.as_secs();
let Ok(iso_seconds) = IsoSecond::try_from((seconds % 60) as u8) else {
return None;
};
#[allow(clippy::cast_possible_truncation)]
let mut local_time = DateTime::from_minutes_since_local_unix_epoch((seconds / 60) as i32);
local_time.time.second = iso_seconds;
Some(Info {
can_ntp,
timezone,
local_time,
})
}
}