//! Time display widget
//!
//! Shows elapsed time, remaining time, or countdown.
use gtk4::glib;
use gtk4::prelude::*;
use gtk4::subclass::prelude::*;
use std::cell::RefCell;
mod imp {
use super::*;
#[derive(Default)]
pub struct TimeDisplay {
pub label: RefCell>,
pub total_secs: RefCell >,
pub remaining_secs: RefCell >,
}
#[glib::object_subclass]
impl ObjectSubclass for TimeDisplay {
const NAME: &'static str = "ShepherdTimeDisplay";
type Type = super::TimeDisplay;
type ParentType = gtk4::Box;
}
impl ObjectImpl for TimeDisplay {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.set_orientation(gtk4::Orientation::Horizontal);
obj.set_spacing(4);
// Time icon
let icon = gtk4::Image::from_icon_name("preferences-system-time-symbolic");
icon.set_pixel_size(20);
obj.append(&icon);
// Time label
let label = gtk4::Label::new(Some("--:--"));
label.add_css_class("time-display");
obj.append(&label);
*self.label.borrow_mut() = Some(label);
}
}
impl WidgetImpl for TimeDisplay {}
impl BoxImpl for TimeDisplay {}
}
glib::wrapper! {
pub struct TimeDisplay(ObjectSubclass)
@extends gtk4::Box, gtk4::Widget,
@implements gtk4::Accessible, gtk4::Buildable, gtk4::ConstraintTarget, gtk4::Orientable;
}
impl TimeDisplay {
pub fn new() -> Self {
glib::Object::builder().build()
}
/// Set the time limit in seconds
pub fn set_time_limit(&self, total_secs: Option) {
let imp = self.imp();
*imp.total_secs.borrow_mut() = total_secs;
self.update_display();
}
/// Set the remaining time in seconds
pub fn set_remaining(&self, remaining_secs: Option) {
let imp = self.imp();
*imp.remaining_secs.borrow_mut() = remaining_secs;
self.update_display();
}
/// Update the display based on current state
fn update_display(&self) {
let imp = self.imp();
if let Some(label) = imp.label.borrow().as_ref() {
let remaining = *imp.remaining_secs.borrow();
let text = if let Some(secs) = remaining {
format_duration(secs)
} else {
"--:--".to_string()
};
label.set_text(&text);
// Update styling based on remaining time
label.remove_css_class("time-warning");
label.remove_css_class("time-critical");
if let Some(secs) = remaining {
if secs <= 60 {
label.add_css_class("time-critical");
} else if secs <= 300 {
label.add_css_class("time-warning");
}
}
}
}
}
impl Default for TimeDisplay {
fn default() -> Self {
Self::new()
}
}
/// Format a duration in seconds as HH:MM:SS or MM:SS
fn format_duration(secs: u64) -> String {
let hours = secs / 3600;
let minutes = (secs % 3600) / 60;
let seconds = secs % 60;
if hours > 0 {
format!("{:02}:{:02}:{:02}", hours, minutes, seconds)
} else {
format!("{:02}:{:02}", minutes, seconds)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_duration() {
assert_eq!(format_duration(0), "00:00");
assert_eq!(format_duration(59), "00:59");
assert_eq!(format_duration(60), "01:00");
assert_eq!(format_duration(3599), "59:59");
assert_eq!(format_duration(3600), "01:00:00");
assert_eq!(format_duration(3661), "01:01:01");
}
}