1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-08-18 07:11:04 +02:00
ironbar/src/modules/clock.rs
Jake Stanger c7743b28c6
docs: add rustdoc comments to all module options
This part of an upcoming effort to generate documentation from code.

Pushing this out before that stage so that the JSON schema is fully documented.
2024-05-23 21:46:33 +01:00

193 lines
5.6 KiB
Rust

use std::env;
use chrono::{DateTime, Local, Locale};
use color_eyre::Result;
use gtk::prelude::*;
use gtk::{Align, Button, Calendar, Label, Orientation};
use serde::Deserialize;
use tokio::sync::{broadcast, mpsc};
use tokio::time::sleep;
use crate::config::{CommonConfig, ModuleOrientation};
use crate::gtk_helpers::IronbarGtkExt;
use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
};
use crate::{glib_recv, module_impl, send_async, spawn, try_send};
#[derive(Debug, Deserialize, Clone)]
pub struct ClockModule {
/// The format string to use for the date/time shown on the bar.
/// Pango markup is supported.
///
/// Detail on available tokens can be found here:
/// <https://docs.rs/chrono/latest/chrono/format/strftime/index.html>
///
/// **Default**: `%d/%m/%Y %H:%M`
#[serde(default = "default_format")]
format: String,
/// The format string to use for the date/time shown in the popup header.
/// Pango markup is supported.
///
/// Detail on available tokens can be found here:
/// <https://docs.rs/chrono/latest/chrono/format/strftime/index.html>
///
/// **Default**: `%H:%M:%S`
#[serde(default = "default_popup_format")]
format_popup: String,
/// The locale to use when formatting dates.
///
/// Note this will not control the calendar -
/// for that you must set `LC_TIME`.
///
/// **Valid options**: See [here](https://docs.rs/pure-rust-locales/0.8.1/pure_rust_locales/enum.Locale.html#variants)
/// <br>
/// **Default**: `$LC_TIME` or `$LANG` or `'POSIX'`
#[serde(default = "default_locale")]
locale: String,
/// The orientation to display the widget contents.
/// Setting to vertical will rotate text 90 degrees.
///
/// **Valid options**: `horizontal`, `vertical`
/// <br>
/// **Default**: `horizontal`
#[serde(default)]
orientation: ModuleOrientation,
/// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option<CommonConfig>,
}
impl Default for ClockModule {
fn default() -> Self {
ClockModule {
format: default_format(),
format_popup: default_popup_format(),
locale: default_locale(),
orientation: ModuleOrientation::Horizontal,
common: Some(CommonConfig::default()),
}
}
}
fn default_format() -> String {
String::from("%d/%m/%Y %H:%M")
}
fn default_popup_format() -> String {
String::from("%H:%M:%S")
}
fn default_locale() -> String {
env::var("LC_TIME")
.or_else(|_| env::var("LANG"))
.map_or_else(|_| "POSIX".to_string(), strip_tail)
}
fn strip_tail(string: String) -> String {
string
.split_once('.')
.map(|(head, _)| head.to_string())
.unwrap_or(string)
}
impl Module<Button> for ClockModule {
type SendMessage = DateTime<Local>;
type ReceiveMessage = ();
module_impl!("clock");
fn spawn_controller(
&self,
_info: &ModuleInfo,
context: &WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
_rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> Result<()> {
let tx = context.tx.clone();
spawn(async move {
loop {
let date = Local::now();
send_async!(tx, ModuleUpdateEvent::Update(date));
sleep(tokio::time::Duration::from_millis(500)).await;
}
});
Ok(())
}
fn into_widget(
self,
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
info: &ModuleInfo,
) -> Result<ModuleParts<Button>> {
let button = Button::new();
let label = Label::builder()
.angle(self.orientation.to_angle())
.use_markup(true)
.build();
button.add(&label);
let tx = context.tx.clone();
button.connect_clicked(move |button| {
try_send!(tx, ModuleUpdateEvent::TogglePopup(button.popup_id()));
});
let format = self.format.clone();
let locale = Locale::try_from(self.locale.as_str()).unwrap_or(Locale::POSIX);
let rx = context.subscribe();
glib_recv!(rx, date => {
let date_string = format!("{}", date.format_localized(&format, locale));
label.set_label(&date_string);
});
let popup = self
.into_popup(
context.controller_tx.clone(),
context.subscribe(),
context,
info,
)
.into_popup_parts(vec![&button]);
Ok(ModuleParts::new(button, popup))
}
fn into_popup(
self,
_tx: mpsc::Sender<Self::ReceiveMessage>,
rx: broadcast::Receiver<Self::SendMessage>,
_context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
_info: &ModuleInfo,
) -> Option<gtk::Box> {
let container = gtk::Box::new(Orientation::Vertical, 0);
let clock = Label::builder()
.halign(Align::Center)
.use_markup(true)
.build();
clock.add_class("calendar-clock");
container.add(&clock);
let calendar = Calendar::new();
calendar.add_class("calendar");
container.add(&calendar);
let format = self.format_popup;
let locale = Locale::try_from(self.locale.as_str()).unwrap_or(Locale::POSIX);
glib_recv!(rx, date => {
let date_string = format!("{}", date.format_localized(&format, locale));
clock.set_label(&date_string);
});
container.show_all();
Some(container)
}
}