commit b4acad021c0651512479ca419719e80a1b863a7c
parent 8b2f9707c57443169146c1b5a8a4ede10a812f7b
Author: Sylvia Ivory <git@sivory.net>
Date: Wed, 18 Jun 2025 16:09:19 -0700
Add setting time format
Diffstat:
4 files changed, 191 insertions(+), 31 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1137,6 +1137,7 @@ dependencies = [
"chrono",
"fjordgard-weather",
"iced",
+ "strum",
"tokio",
]
diff --git a/Cargo.toml b/Cargo.toml
@@ -13,4 +13,5 @@ edition = "2024"
chrono = "0.4.41"
fjordgard-weather = { version = "0.1.0", path = "crates/weather" }
iced = { version = "0.13.1", features = ["tokio", "canvas", "image", "svg"] }
+strum = { version = "0.27.1", features = ["derive"] }
tokio = { version = "1.45.1", features = ["full"] }
diff --git a/src/config.rs b/src/config.rs
@@ -1,3 +1,31 @@
+use std::fmt;
+
+#[derive(Debug, Clone, PartialEq, strum::Display, strum::VariantArray)]
+pub enum BackgroundMode {
+ Unsplash,
+ Solid,
+ Local,
+}
+
+impl BackgroundMode {
+ pub fn default_background(&self) -> &'static str {
+ match self {
+ // https://unsplash.com/collections/1053828/tabliss-official
+ Self::Unsplash => "1053828",
+ Self::Solid => "#ffffff",
+ Self::Local => "",
+ }
+ }
+
+ pub fn edit_text(&self) -> &'static str {
+ match self {
+ Self::Unsplash => "Unsplash collection",
+ Self::Solid => "Color (#rrggbb)",
+ Self::Local => "File path",
+ }
+ }
+}
+
pub struct Location {
pub longitude: f64,
pub latitude: f64,
@@ -7,7 +35,8 @@ pub struct Location {
pub struct Config {
pub timezone: String,
pub time_format: String,
- pub collection: String,
+ pub background_mode: BackgroundMode,
+ pub background: String,
pub location: Option<Location>,
}
@@ -16,8 +45,8 @@ impl Default for Config {
Self {
timezone: String::from("Etc/UTC"),
time_format: String::from("%-I:%M:%S"),
- // https://unsplash.com/collections/1053828/tabliss-official
- collection: String::from("1053828"),
+ background_mode: BackgroundMode::Unsplash,
+ background: BackgroundMode::Unsplash.default_background().to_string(),
location: None,
}
}
diff --git a/src/main.rs b/src/main.rs
@@ -1,16 +1,21 @@
use chrono::{DateTime, Local};
use iced::{
- Color, Element, Font, Length, Subscription,
+ Color, Element, Font, Length, Size, Subscription, Task,
font::Weight,
time,
- widget::{center, column, container, horizontal_space, row, stack, text},
+ widget::{
+ button, center, column, combo_box, container, horizontal_space, row, stack, text,
+ text_input,
+ },
+ window,
};
-use config::Config;
-
use background::{BackgroundKind, background};
+use config::Config;
+use icon::{icon, icon_button};
+use strum::VariantArray;
-use crate::icon::{icon, icon_button};
+use crate::config::BackgroundMode;
mod background;
mod config;
@@ -20,6 +25,10 @@ struct Fjordgard {
config: Config,
time: DateTime<Local>,
background: BackgroundKind,
+ backgrounds: combo_box::State<BackgroundMode>,
+
+ settings_window: Option<window::Id>,
+ main_window: window::Id,
}
#[derive(Debug, Clone, Copy)]
@@ -29,27 +38,102 @@ enum MediaControl {
Next,
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
enum Message {
Tick(DateTime<Local>),
Media(MediaControl),
- Settings,
+ OpenSettings,
+
+ SettingsOpened(window::Id),
+ MainWindowOpened,
+ WindowClosed(window::Id),
+
+ ConfigBackgroundMode(BackgroundMode),
+ ConfigTimeFormat(String),
}
impl Fjordgard {
- fn title(&self) -> String {
- String::from("Fjordgard")
+ fn new() -> (Self, Task<Message>) {
+ let (id, open) = window::open(window::Settings::default());
+
+ (
+ Self {
+ config: Config::default(),
+ time: Local::now(),
+ background: BackgroundKind::Color(Color::from_rgb8(255, 255, 255)),
+ backgrounds: combo_box::State::new(BackgroundMode::VARIANTS.to_vec()),
+
+ settings_window: None,
+ main_window: id,
+ },
+ open.map(|_| Message::MainWindowOpened),
+ )
+ }
+ fn title(&self, window_id: window::Id) -> String {
+ if window_id == self.main_window {
+ String::from("Fjordgard")
+ } else {
+ String::from("Settings - Fjordgard")
+ }
}
- fn update(&mut self, msg: Message) {
+ fn update(&mut self, msg: Message) -> Task<Message> {
match msg {
- Message::Tick(time) => self.time = time,
- _ => {}
+ Message::Tick(time) => {
+ self.time = time;
+ Task::none()
+ }
+ Message::OpenSettings => {
+ if self.settings_window.is_none() {
+ let (id, open) = window::open(window::Settings {
+ level: window::Level::AlwaysOnTop,
+ size: Size::new(350.0, 450.0),
+ ..Default::default()
+ });
+
+ self.settings_window = Some(id);
+
+ open.map(Message::SettingsOpened)
+ } else {
+ Task::none()
+ }
+ }
+ Message::WindowClosed(id) => {
+ if self.main_window == id {
+ iced::exit()
+ } else {
+ self.settings_window = None;
+ Task::none()
+ }
+ }
+ Message::ConfigBackgroundMode(mode) => {
+ self.config.background = mode.default_background().to_string();
+ self.config.background_mode = mode;
+ Task::none()
+ }
+ Message::ConfigTimeFormat(format) => {
+ self.config.time_format = format;
+ Task::none()
+ }
+ _ => Task::none(),
}
}
- fn view(&self) -> Element<Message> {
- let time_text = self.time.format(&self.config.time_format).to_string();
+ fn view(&self, window_id: window::Id) -> Element<Message> {
+ if self.main_window == window_id {
+ self.view_main()
+ } else {
+ self.view_settings()
+ }
+ }
+
+ fn view_main(&self) -> Element<Message> {
+ let dt = self.time.format(&self.config.time_format);
+ let mut time_text = String::new();
+
+ if let Err(_) = dt.write_to(&mut time_text) {
+ time_text = String::from("Invalid time format")
+ }
let mut bold = Font::DEFAULT;
bold.weight = Weight::Bold;
@@ -77,7 +161,7 @@ impl Fjordgard {
)
.center_x(Length::Fill);
- let settings = icon_button("icons/settings.svg", Message::Settings);
+ let settings = icon_button("icons/settings.svg", Message::OpenSettings);
stack![
background(&self.background),
@@ -93,23 +177,68 @@ impl Fjordgard {
.into()
}
- fn tick(&self) -> Subscription<Message> {
- time::every(time::Duration::from_secs(1)).map(|_| Message::Tick(Local::now()))
+ fn view_settings(&self) -> Element<Message> {
+ let placeholder = Config::default();
+ let config = &self.config;
+
+ let mut background_mode_row =
+ row![text(config.background_mode.edit_text()).width(Length::FillPortion(1)),];
+
+ if config.background_mode == BackgroundMode::Local {
+ let text = if config.background == "" {
+ "Select file..."
+ } else {
+ &config.background
+ };
+
+ background_mode_row =
+ background_mode_row.push(button(text).width(Length::FillPortion(2)));
+ } else {
+ background_mode_row = background_mode_row.push(
+ text_input(
+ config.background_mode.default_background(),
+ &config.background,
+ )
+ .width(Length::FillPortion(2)),
+ );
+ }
+
+ container(
+ column![
+ row![
+ text("Time format").width(Length::FillPortion(1)),
+ text_input(&placeholder.time_format, &config.time_format)
+ .width(Length::FillPortion(2))
+ .on_input(Message::ConfigTimeFormat)
+ ],
+ row![
+ text("Background mode").width(Length::FillPortion(1)),
+ combo_box(
+ &self.backgrounds,
+ "",
+ Some(&placeholder.background_mode),
+ Message::ConfigBackgroundMode
+ )
+ .width(Length::FillPortion(2))
+ ],
+ background_mode_row
+ ]
+ .spacing(10),
+ )
+ .padding(15)
+ .into()
}
-}
-impl Default for Fjordgard {
- fn default() -> Self {
- Self {
- config: Config::default(),
- time: Local::now(),
- background: BackgroundKind::Color(Color::from_rgb8(255, 255, 255)),
- }
+ fn subscription(&self) -> Subscription<Message> {
+ Subscription::batch(vec![
+ time::every(time::Duration::from_secs(1)).map(|_| Message::Tick(Local::now())),
+ window::close_events().map(Message::WindowClosed),
+ ])
}
}
fn main() -> iced::Result {
- iced::application(Fjordgard::title, Fjordgard::update, Fjordgard::view)
- .subscription(Fjordgard::tick)
- .run()
+ iced::daemon(Fjordgard::title, Fjordgard::update, Fjordgard::view)
+ .subscription(Fjordgard::subscription)
+ .run_with(Fjordgard::new)
}