Skip to content

Commit c9ebe05

Browse files
feat(time): Option to show seconds on the clock
Closes: pop-os#496 Requires: pop-os/cosmic-settings#509 I based some of this code off of `i3status-rust` to double check that I handled it efficiently.
1 parent 0d0a13a commit c9ebe05

File tree

2 files changed

+70
-11
lines changed

2 files changed

+70
-11
lines changed

cosmic-applet-time/src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use cosmic::cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, Cosmi
77
#[version = 1]
88
pub struct TimeAppletConfig {
99
pub military_time: bool,
10+
pub show_seconds: bool,
1011
pub first_day_of_week: u8,
1112
pub show_date_in_top_panel: bool,
1213
pub show_weekday: bool,
@@ -16,6 +17,7 @@ impl Default for TimeAppletConfig {
1617
fn default() -> Self {
1718
Self {
1819
military_time: false,
20+
show_seconds: false,
1921
first_day_of_week: 6,
2022
show_date_in_top_panel: true,
2123
show_weekday: false,

cosmic-applet-time/src/window.rs

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use cosmic::{
2525
Command, Element, Theme,
2626
};
2727
use timedate_zbus::TimeDateProxy;
28+
use tokio::{sync::watch, time};
2829

2930
use icu::{
3031
calendar::DateTime,
@@ -56,6 +57,8 @@ pub struct Window {
5657
rectangle: Rectangle,
5758
token_tx: Option<calloop::channel::Sender<TokenRequest>>,
5859
config: TimeAppletConfig,
60+
show_seconds_rx: watch::Receiver<bool>,
61+
show_seconds_tx: watch::Sender<bool>,
5962
locale: Locale,
6063
}
6164

@@ -136,6 +139,9 @@ impl cosmic::Application for Window {
136139
// timezone is ever externally changed
137140
let now: chrono::DateTime<chrono::FixedOffset> = chrono::Local::now().fixed_offset();
138141

142+
// Synch `show_seconds` from the config within the time subscription
143+
let (show_seconds_tx, show_seconds_rx) = watch::channel(false);
144+
139145
(
140146
Self {
141147
core,
@@ -147,6 +153,8 @@ impl cosmic::Application for Window {
147153
rectangle: Rectangle::default(),
148154
token_tx: None,
149155
config: TimeAppletConfig::default(),
156+
show_seconds_tx,
157+
show_seconds_rx,
150158
locale,
151159
},
152160
Command::none(),
@@ -166,16 +174,53 @@ impl cosmic::Application for Window {
166174
}
167175

168176
fn subscription(&self) -> Subscription<Message> {
169-
fn time_subscription() -> Subscription<()> {
170-
subscription::unfold("time-sub", (), move |()| async move {
171-
let now = chrono::Local::now();
172-
let update_delay = chrono::TimeDelta::minutes(1);
173-
174-
let duration = ((now + update_delay).duration_trunc(update_delay).unwrap() - now)
175-
.to_std()
176-
.unwrap();
177-
tokio::time::sleep(duration).await;
178-
((), ())
177+
fn time_subscription(mut show_seconds: watch::Receiver<bool>) -> Subscription<()> {
178+
subscription::channel("time-sub", 1, |mut output| async move {
179+
let mut period = if *show_seconds.borrow_and_update() {
180+
1
181+
} else {
182+
60
183+
};
184+
let mut timer = time::interval(time::Duration::from_secs(period));
185+
timer.set_missed_tick_behavior(time::MissedTickBehavior::Skip);
186+
187+
loop {
188+
tokio::select! {
189+
_ = timer.tick() => {
190+
#[cfg(debug_assertions)]
191+
if let Err(err) = output.send(()).await {
192+
tracing::error!(?err, "Failed sending tick request to applet");
193+
}
194+
#[cfg(not(debug_assertions))]
195+
let _ = output.send(()).await;
196+
197+
// Calculate a delta if we're ticking per minute to keep ticks stable
198+
// Based on i3status-rust
199+
let current = chrono::Local::now().second() as u64 % period;
200+
if current != 0 {
201+
timer.reset_after(time::Duration::from_secs(period - current));
202+
}
203+
},
204+
// Update timer if the user toggles show_seconds
205+
Ok(()) = show_seconds.changed() => {
206+
let seconds = *show_seconds.borrow_and_update();
207+
if seconds {
208+
period = 1;
209+
timer = time::interval(time::Duration::from_secs(period));
210+
} else {
211+
period = 60;
212+
let delta = time::Duration::from_secs(period - chrono::Local::now().second() as u64 % period);
213+
let now = time::Instant::now();
214+
// Start ticking from the next minute to update the time properly
215+
let start = now + delta;
216+
let period = time::Duration::from_secs(period);
217+
timer = time::interval_at(start, period);
218+
}
219+
220+
timer.set_missed_tick_behavior(time::MissedTickBehavior::Skip);
221+
}
222+
}
223+
}
179224
})
180225
}
181226

@@ -221,7 +266,7 @@ impl cosmic::Application for Window {
221266

222267
Subscription::batch(vec![
223268
rectangle_tracker_subscription(0).map(|e| Message::Rectangle(e.1)),
224-
time_subscription().map(|_| Message::Tick),
269+
time_subscription(self.show_seconds_rx.clone()).map(|_| Message::Tick),
225270
activation_token_subscription(0).map(Message::Token),
226271
timezone_subscription(),
227272
self.core.watch_config(Self::APP_ID).map(|u| {
@@ -356,6 +401,14 @@ impl cosmic::Application for Window {
356401
Command::none()
357402
}
358403
Message::ConfigChanged(c) => {
404+
self.show_seconds_tx.send_if_modified(|show_seconds| {
405+
if *show_seconds == c.show_seconds {
406+
false
407+
} else {
408+
*show_seconds = c.show_seconds;
409+
true
410+
}
411+
});
359412
self.config = c;
360413
Command::none()
361414
}
@@ -396,6 +449,10 @@ impl cosmic::Application for Window {
396449

397450
time_bag.hour = Some(components::Numeric::Numeric);
398451
time_bag.minute = Some(components::Numeric::Numeric);
452+
time_bag.second = self
453+
.config
454+
.show_seconds
455+
.then_some(components::Numeric::Numeric);
399456

400457
let hour_cycle = if self.config.military_time {
401458
preferences::HourCycle::H23

0 commit comments

Comments
 (0)