2026-08-30 11:05:00 +02:00
|
|
|
mod exchange;
|
|
|
|
|
mod clock;
|
|
|
|
|
|
|
|
|
|
use exchange::{get_exchanges, Exchange};
|
|
|
|
|
use iced::widget::{column, container, row, text};
|
|
|
|
|
use iced::{Element, Length, Color, Theme, Subscription};
|
|
|
|
|
use chrono::{DateTime, Utc};
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
|
|
pub fn main() -> iced::Result {
|
|
|
|
|
iced::application(
|
|
|
|
|
"Horloges Boursières Mondiales",
|
|
|
|
|
WorldClocksApp::update,
|
|
|
|
|
WorldClocksApp::view,
|
|
|
|
|
)
|
|
|
|
|
.subscription(WorldClocksApp::subscription)
|
|
|
|
|
.window(iced::window::Settings {
|
2026-08-30 11:35:07 +02:00
|
|
|
size: iced::Size { width: 1600.0, height: 320.0 },
|
2026-08-30 11:05:00 +02:00
|
|
|
..Default::default()
|
|
|
|
|
})
|
|
|
|
|
.run_with(WorldClocksApp::new)
|
2026-08-30 10:13:31 +02:00
|
|
|
}
|
2026-08-30 11:05:00 +02:00
|
|
|
|
|
|
|
|
struct WorldClocksApp {
|
|
|
|
|
exchanges: Vec<Exchange>,
|
|
|
|
|
now: DateTime<Utc>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum Message {
|
|
|
|
|
Tick(DateTime<Utc>),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl WorldClocksApp {
|
|
|
|
|
fn new() -> (Self, iced::Task<Message>) {
|
|
|
|
|
(
|
|
|
|
|
Self {
|
|
|
|
|
exchanges: get_exchanges(),
|
|
|
|
|
now: Utc::now(),
|
|
|
|
|
},
|
|
|
|
|
iced::Task::none(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn update(&mut self, message: Message) {
|
|
|
|
|
match message {
|
|
|
|
|
Message::Tick(time) => {
|
|
|
|
|
self.now = time;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn subscription(&self) -> Subscription<Message> {
|
|
|
|
|
iced::time::every(Duration::from_secs(1)).map(|_| Message::Tick(Utc::now()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn view(&self) -> Element<'_, Message> {
|
|
|
|
|
let mut row_layout = row![].spacing(10).padding(15);
|
|
|
|
|
|
|
|
|
|
for (i, exchange) in self.exchanges.iter().enumerate() {
|
|
|
|
|
let is_utc = i == 0;
|
|
|
|
|
let local_time = exchange.local_time(&self.now);
|
|
|
|
|
let time_str = local_time.format("%H:%M:%S").to_string();
|
|
|
|
|
|
|
|
|
|
let mut col = column![].spacing(5).align_x(iced::Alignment::Center);
|
|
|
|
|
|
|
|
|
|
if is_utc {
|
|
|
|
|
col = col.push(
|
|
|
|
|
text("UTC")
|
|
|
|
|
.size(14)
|
|
|
|
|
.style(|_theme: &Theme| text::Style { color: Some(Color::from_rgb(0.0, 0.8, 0.8)) })
|
|
|
|
|
);
|
|
|
|
|
col = col.push(text("").size(12));
|
|
|
|
|
} else {
|
|
|
|
|
col = col.push(text(exchange.city.clone()).size(13));
|
|
|
|
|
let is_open = exchange.is_open(&self.now);
|
|
|
|
|
let (status_text, status_color) = if is_open {
|
|
|
|
|
("ouvert", Color::from_rgb(0.0, 0.8, 0.2))
|
|
|
|
|
} else {
|
|
|
|
|
("fermé", Color::from_rgb(0.9, 0.2, 0.2))
|
|
|
|
|
};
|
|
|
|
|
col = col.push(
|
|
|
|
|
text(status_text)
|
|
|
|
|
.size(12)
|
|
|
|
|
.style(move |_theme: &Theme| text::Style { color: Some(status_color) })
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-30 11:35:07 +02:00
|
|
|
// Horloge analogique graphique (1.5x)
|
2026-08-30 11:05:00 +02:00
|
|
|
col = col.push(clock::view_clock(local_time));
|
|
|
|
|
|
|
|
|
|
// Horloge numérique
|
|
|
|
|
let digital_color = if is_utc { Color::from_rgb(0.0, 0.8, 0.8) } else { Color::WHITE };
|
|
|
|
|
col = col.push(
|
|
|
|
|
text(time_str)
|
|
|
|
|
.size(13)
|
|
|
|
|
.style(move |_theme: &Theme| text::Style { color: Some(digital_color) })
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let boxed_clock = container(col)
|
2026-08-30 11:35:07 +02:00
|
|
|
.width(Length::Fixed(160.0))
|
2026-08-30 11:05:00 +02:00
|
|
|
.padding(8)
|
|
|
|
|
.style(|_theme: &Theme| container::Style {
|
|
|
|
|
background: Some(iced::Background::Color(Color::from_rgb(0.12, 0.12, 0.15))),
|
|
|
|
|
border: iced::Border {
|
|
|
|
|
color: Color::from_rgb(0.25, 0.25, 0.3),
|
|
|
|
|
width: 1.0,
|
|
|
|
|
radius: 4.0.into(),
|
|
|
|
|
},
|
|
|
|
|
..Default::default()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
row_layout = row_layout.push(boxed_clock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
container(row_layout)
|
|
|
|
|
.width(Length::Fill)
|
|
|
|
|
.height(Length::Fill)
|
|
|
|
|
.center_x(Length::Fill)
|
|
|
|
|
.center_y(Length::Fill)
|
|
|
|
|
.style(|_theme: &Theme| container::Style {
|
|
|
|
|
background: Some(iced::Background::Color(Color::from_rgb(0.08, 0.08, 0.1))),
|
|
|
|
|
..Default::default()
|
|
|
|
|
})
|
|
|
|
|
.into()
|
|
|
|
|
}
|
|
|
|
|
}
|