msansm1/rss-podcast-dlpublic Fork
⑂ 97adfbe
Commits
⬇ Clone ▾
git clone https://git.rickub.com/msansm1/rss-podcast-dl.git
git clone ssh://git@rickub.com/msansm1/rss-podcast-dl.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

add light theme + toggle between themes

msansm1 committed 2026-07-09T17:51:30+02:00 Browse files
97adfbe parent: c97181b
modified src/app.rs +77 -31
@@ -4,6 +4,7 @@
44
55 use crate::config::AppState;
66 use crate::config::storage::{load_feeds_default, save_feeds_default};
7+use crate::config::Theme;
78 use eframe::egui;
89 use log::{error, info};
910
@@ -107,12 +108,18 @@ impl eframe::App for RssPodcastApp {
107108 ctx.send_viewport_cmd(egui::ViewportCommand::Title(self.title.clone()));
108109
109110 // Appliquer un thème personnalisé moderne
110- self.apply_custom_theme(ctx);
111+ self.apply_custom_theme(ctx, self.state.get_theme());
112+
113+ // Get theme-specific colors
114+ let notification_bg = match self.state.get_theme() {
115+ Theme::Dark => egui::Color32::from_rgb(40, 42, 54),
116+ Theme::Light => egui::Color32::from_rgb(220, 230, 245),
117+ };
111118
112119 // Draw notifications panel at the top
113120 egui::TopBottomPanel::top("notifications_panel")
114121 .frame(egui::Frame::default()
115- .fill(egui::Color32::from_rgb(40, 42, 54))
122+ .fill(notification_bg)
116123 .rounding(egui::Rounding::same(6.0)))
117124 .show(ctx, |ui| {
118125 ui.vertical_centered(|ui| {
@@ -136,35 +143,77 @@ impl eframe::App for RssPodcastApp {
136143
137144 impl RssPodcastApp {
138145 /// Applique un thème personnalisé moderne et agréable
139- fn apply_custom_theme(&self, ctx: &egui::Context) {
146+ fn apply_custom_theme(&mut self, ctx: &egui::Context, theme: Theme) {
140147 let mut style = (*ctx.style()).clone();
141148
142- // Couleurs de base (thème sombre moderne inspiré de Nord/ Dracula)
143- style.visuals.dark_mode = true;
144-
145- // Couleurs principales
146- let bg_primary = egui::Color32::from_rgb(36, 39, 58);
147- let bg_secondary = egui::Color32::from_rgb(46, 52, 72);
148- let bg_tertiary = egui::Color32::from_rgb(56, 62, 80);
149- let accent = egui::Color32::from_rgb(130, 170, 250);
150- let border = egui::Color32::from_rgb(80, 88, 106);
151-
152- // Arrière-plan
153- style.visuals.widgets.noninteractive.bg_fill = bg_primary;
154- style.visuals.widgets.noninteractive.bg_stroke = egui::Stroke::new(1.0, border);
155-
156- // Boutons normaux
157- style.visuals.widgets.inactive.bg_fill = bg_secondary;
158- style.visuals.widgets.inactive.bg_stroke = egui::Stroke::new(1.0, border);
159-
160- // Boutons hover
161- style.visuals.widgets.hovered.bg_fill = bg_tertiary;
162-
163- // Boutons actifs
164- style.visuals.widgets.active.bg_fill = accent;
165- style.visuals.widgets.active.bg_stroke = egui::Stroke::new(1.0, accent);
149+ match theme {
150+ Theme::Dark => {
151+ // Thème sombre moderne inspiré de Nord/Dracula
152+ style.visuals.dark_mode = true;
153+
154+ // Couleurs principales pour le thème sombre
155+ let bg_primary = egui::Color32::from_rgb(36, 39, 58);
156+ let bg_secondary = egui::Color32::from_rgb(46, 52, 72);
157+ let bg_tertiary = egui::Color32::from_rgb(56, 62, 80);
158+ let accent = egui::Color32::from_rgb(130, 170, 250);
159+ let border = egui::Color32::from_rgb(80, 88, 106);
160+
161+ // Arrière-plan
162+ style.visuals.widgets.noninteractive.bg_fill = bg_primary;
163+ style.visuals.widgets.noninteractive.bg_stroke = egui::Stroke::new(1.0, border);
164+
165+ // Boutons normaux
166+ style.visuals.widgets.inactive.bg_fill = bg_secondary;
167+ style.visuals.widgets.inactive.bg_stroke = egui::Stroke::new(1.0, border);
168+
169+ // Boutons hover
170+ style.visuals.widgets.hovered.bg_fill = bg_tertiary;
171+
172+ // Boutons actifs
173+ style.visuals.widgets.active.bg_fill = accent;
174+ style.visuals.widgets.active.bg_stroke = egui::Stroke::new(1.0, accent);
175+
176+ // Couleur des liens
177+ style.visuals.hyperlink_color = accent;
178+
179+ // Couleur de la fenêtre
180+ self.clear_color = egui::Color32::from_rgb(30, 30, 30);
181+ }
182+ Theme::Light => {
183+ // Thème clair moderne
184+ style.visuals.dark_mode = false;
185+
186+ // Couleurs principales pour le thème clair
187+ let bg_primary = egui::Color32::from_rgb(240, 245, 252);
188+ let bg_secondary = egui::Color32::from_rgb(220, 230, 245);
189+ let bg_tertiary = egui::Color32::from_rgb(200, 220, 240);
190+ let accent = egui::Color32::from_rgb(100, 140, 220);
191+ let border = egui::Color32::from_rgb(180, 195, 210);
192+
193+ // Arrière-plan
194+ style.visuals.widgets.noninteractive.bg_fill = bg_primary;
195+ style.visuals.widgets.noninteractive.bg_stroke = egui::Stroke::new(1.0, border);
196+
197+ // Boutons normaux
198+ style.visuals.widgets.inactive.bg_fill = bg_secondary;
199+ style.visuals.widgets.inactive.bg_stroke = egui::Stroke::new(1.0, border);
200+
201+ // Boutons hover
202+ style.visuals.widgets.hovered.bg_fill = bg_tertiary;
203+
204+ // Boutons actifs
205+ style.visuals.widgets.active.bg_fill = accent;
206+ style.visuals.widgets.active.bg_stroke = egui::Stroke::new(1.0, accent);
207+
208+ // Couleur des liens
209+ style.visuals.hyperlink_color = accent;
210+
211+ // Couleur de la fenêtre
212+ self.clear_color = egui::Color32::from_rgb(248, 250, 255);
213+ }
214+ }
166215
167- // Espacement amélioré
216+ // Espacement amélioré (commun aux deux thèmes)
168217 style.spacing.item_spacing = egui::Vec2::new(12.0, 12.0);
169218 style.spacing.button_padding = egui::Vec2::new(12.0, 6.0);
170219
@@ -172,9 +221,6 @@ impl RssPodcastApp {
172221 style.spacing.icon_width = 20.0;
173222 style.spacing.icon_spacing = 8.0;
174223
175- // Couleur des liens
176- style.visuals.hyperlink_color = accent;
177-
178224 ctx.set_style(style);
179225 }
180226 }
@@ -4,6 +4,7 @@
4 4
5 use crate::config::AppState;5 use crate::config::AppState;
6 use crate::config::storage::{load_feeds_default, save_feeds_default};6 use crate::config::storage::{load_feeds_default, save_feeds_default};
7+use crate::config::Theme;
7 use eframe::egui;8 use eframe::egui;
8 use log::{error, info};9 use log::{error, info};
9 10
@@ -107,12 +108,18 @@ impl eframe::App for RssPodcastApp {
107 ctx.send_viewport_cmd(egui::ViewportCommand::Title(self.title.clone()));108 ctx.send_viewport_cmd(egui::ViewportCommand::Title(self.title.clone()));
108 109
109 // Appliquer un thème personnalisé moderne110 // Appliquer un thème personnalisé moderne
110- self.apply_custom_theme(ctx);111+ self.apply_custom_theme(ctx, self.state.get_theme());
112+
113+ // Get theme-specific colors
114+ let notification_bg = match self.state.get_theme() {
115+ Theme::Dark => egui::Color32::from_rgb(40, 42, 54),
116+ Theme::Light => egui::Color32::from_rgb(220, 230, 245),
117+ };
111 118
112 // Draw notifications panel at the top119 // Draw notifications panel at the top
113 egui::TopBottomPanel::top("notifications_panel")120 egui::TopBottomPanel::top("notifications_panel")
114 .frame(egui::Frame::default()121 .frame(egui::Frame::default()
115- .fill(egui::Color32::from_rgb(40, 42, 54))122+ .fill(notification_bg)
116 .rounding(egui::Rounding::same(6.0)))123 .rounding(egui::Rounding::same(6.0)))
117 .show(ctx, |ui| {124 .show(ctx, |ui| {
118 ui.vertical_centered(|ui| {125 ui.vertical_centered(|ui| {
@@ -136,35 +143,77 @@ impl eframe::App for RssPodcastApp {
136 143
137 impl RssPodcastApp {144 impl RssPodcastApp {
138 /// Applique un thème personnalisé moderne et agréable145 /// Applique un thème personnalisé moderne et agréable
139- fn apply_custom_theme(&self, ctx: &egui::Context) {146+ fn apply_custom_theme(&mut self, ctx: &egui::Context, theme: Theme) {
140 let mut style = (*ctx.style()).clone();147 let mut style = (*ctx.style()).clone();
141 148
142- // Couleurs de base (thème sombre moderne inspiré de Nord/ Dracula)149+ match theme {
143- style.visuals.dark_mode = true;150+ Theme::Dark => {
144- 151+ // Thème sombre moderne inspiré de Nord/Dracula
145- // Couleurs principales152+ style.visuals.dark_mode = true;
146- let bg_primary = egui::Color32::from_rgb(36, 39, 58);153+
147- let bg_secondary = egui::Color32::from_rgb(46, 52, 72);154+ // Couleurs principales pour le thème sombre
148- let bg_tertiary = egui::Color32::from_rgb(56, 62, 80);155+ let bg_primary = egui::Color32::from_rgb(36, 39, 58);
149- let accent = egui::Color32::from_rgb(130, 170, 250);156+ let bg_secondary = egui::Color32::from_rgb(46, 52, 72);
150- let border = egui::Color32::from_rgb(80, 88, 106);157+ let bg_tertiary = egui::Color32::from_rgb(56, 62, 80);
151- 158+ let accent = egui::Color32::from_rgb(130, 170, 250);
152- // Arrière-plan159+ let border = egui::Color32::from_rgb(80, 88, 106);
153- style.visuals.widgets.noninteractive.bg_fill = bg_primary;160+
154- style.visuals.widgets.noninteractive.bg_stroke = egui::Stroke::new(1.0, border);161+ // Arrière-plan
155- 162+ style.visuals.widgets.noninteractive.bg_fill = bg_primary;
156- // Boutons normaux163+ style.visuals.widgets.noninteractive.bg_stroke = egui::Stroke::new(1.0, border);
157- style.visuals.widgets.inactive.bg_fill = bg_secondary;164+
158- style.visuals.widgets.inactive.bg_stroke = egui::Stroke::new(1.0, border);165+ // Boutons normaux
159- 166+ style.visuals.widgets.inactive.bg_fill = bg_secondary;
160- // Boutons hover167+ style.visuals.widgets.inactive.bg_stroke = egui::Stroke::new(1.0, border);
161- style.visuals.widgets.hovered.bg_fill = bg_tertiary;168+
162- 169+ // Boutons hover
163- // Boutons actifs170+ style.visuals.widgets.hovered.bg_fill = bg_tertiary;
164- style.visuals.widgets.active.bg_fill = accent;171+
165- style.visuals.widgets.active.bg_stroke = egui::Stroke::new(1.0, accent);172+ // Boutons actifs
173+ style.visuals.widgets.active.bg_fill = accent;
174+ style.visuals.widgets.active.bg_stroke = egui::Stroke::new(1.0, accent);
175+
176+ // Couleur des liens
177+ style.visuals.hyperlink_color = accent;
178+
179+ // Couleur de la fenêtre
180+ self.clear_color = egui::Color32::from_rgb(30, 30, 30);
181+ }
182+ Theme::Light => {
183+ // Thème clair moderne
184+ style.visuals.dark_mode = false;
185+
186+ // Couleurs principales pour le thème clair
187+ let bg_primary = egui::Color32::from_rgb(240, 245, 252);
188+ let bg_secondary = egui::Color32::from_rgb(220, 230, 245);
189+ let bg_tertiary = egui::Color32::from_rgb(200, 220, 240);
190+ let accent = egui::Color32::from_rgb(100, 140, 220);
191+ let border = egui::Color32::from_rgb(180, 195, 210);
192+
193+ // Arrière-plan
194+ style.visuals.widgets.noninteractive.bg_fill = bg_primary;
195+ style.visuals.widgets.noninteractive.bg_stroke = egui::Stroke::new(1.0, border);
196+
197+ // Boutons normaux
198+ style.visuals.widgets.inactive.bg_fill = bg_secondary;
199+ style.visuals.widgets.inactive.bg_stroke = egui::Stroke::new(1.0, border);
200+
201+ // Boutons hover
202+ style.visuals.widgets.hovered.bg_fill = bg_tertiary;
203+
204+ // Boutons actifs
205+ style.visuals.widgets.active.bg_fill = accent;
206+ style.visuals.widgets.active.bg_stroke = egui::Stroke::new(1.0, accent);
207+
208+ // Couleur des liens
209+ style.visuals.hyperlink_color = accent;
210+
211+ // Couleur de la fenêtre
212+ self.clear_color = egui::Color32::from_rgb(248, 250, 255);
213+ }
214+ }
166 215
167- // Espacement amélioré216+ // Espacement amélioré (commun aux deux thèmes)
168 style.spacing.item_spacing = egui::Vec2::new(12.0, 12.0);217 style.spacing.item_spacing = egui::Vec2::new(12.0, 12.0);
169 style.spacing.button_padding = egui::Vec2::new(12.0, 6.0);218 style.spacing.button_padding = egui::Vec2::new(12.0, 6.0);
170 219
@@ -172,9 +221,6 @@ impl RssPodcastApp {
172 style.spacing.icon_width = 20.0;221 style.spacing.icon_width = 20.0;
173 style.spacing.icon_spacing = 8.0;222 style.spacing.icon_spacing = 8.0;
174 223
175- // Couleur des liens
176- style.visuals.hyperlink_color = accent;
177-
178 ctx.set_style(style);224 ctx.set_style(style);
179 }225 }
180 }226 }
modified src/config/app_state.rs +59 -0
@@ -9,9 +9,44 @@ use crate::config::search::SearchQuery;
99 use crate::config::statistics::AppStatistics;
1010 use crate::ui::notifications::NotificationManager;
1111 use chrono::{DateTime, Utc};
12+use serde::{Deserialize, Serialize};
1213 use std::collections::HashMap;
1314 use std::path::PathBuf;
1415
16+/// Theme options for the application
17+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18+pub enum Theme {
19+ /// Dark theme (default)
20+ Dark,
21+ /// Light theme
22+ Light,
23+}
24+
25+impl Default for Theme {
26+ fn default() -> Self {
27+ Theme::Dark
28+ }
29+}
30+
31+impl Theme {
32+ /// Toggles between light and dark theme
33+ pub fn toggle(&mut self) {
34+ *self = match self {
35+ Theme::Dark => Theme::Light,
36+ Theme::Light => Theme::Dark,
37+ };
38+ }
39+
40+ /// Returns the opposite theme
41+ #[allow(dead_code)]
42+ pub fn opposite(&self) -> Self {
43+ match self {
44+ Theme::Dark => Theme::Light,
45+ Theme::Light => Theme::Dark,
46+ }
47+ }
48+}
49+
1550 /// Main application state
1651 ///
1752 /// This struct holds all the state needed for the RSS Podcast Downloader application.
@@ -83,6 +118,8 @@ pub struct AppState {
83118 pub notification_manager: NotificationManager,
84119 /// List of playlists for grouping feeds
85120 pub playlists: Vec<Playlist>,
121+ /// Current theme (dark or light)
122+ pub theme: Theme,
86123 // Filter state
87124 /// Filter: date from (inclusive)
88125 #[allow(dead_code)]
@@ -179,6 +216,7 @@ impl AppState {
179216 sort_direction: "asc".to_string(),
180217 statistics: AppStatistics::new(),
181218 history_manager: HistoryManager::new(),
219+ theme: Theme::default(),
182220 }
183221 }
184222
@@ -479,6 +517,27 @@ impl AppState {
479517 pub fn clear_export_import_error(&mut self) {
480518 self.export_import_error = None;
481519 }
520+
521+ /// Toggles the theme between light and dark
522+ pub fn toggle_theme(&mut self) {
523+ self.theme.toggle();
524+ let theme_name = match self.theme {
525+ Theme::Dark => "sombre",
526+ Theme::Light => "clair",
527+ };
528+ self.notification_manager.info(format!("Thème basculé vers: {}", theme_name));
529+ }
530+
531+ /// Sets the theme to a specific value
532+ #[allow(dead_code)]
533+ pub fn set_theme(&mut self, theme: Theme) {
534+ self.theme = theme;
535+ }
536+
537+ /// Gets the current theme
538+ pub fn get_theme(&self) -> Theme {
539+ self.theme
540+ }
482541 }
483542
484543 #[cfg(test)]
@@ -9,9 +9,44 @@ use crate::config::search::SearchQuery;
9 use crate::config::statistics::AppStatistics;9 use crate::config::statistics::AppStatistics;
10 use crate::ui::notifications::NotificationManager;10 use crate::ui::notifications::NotificationManager;
11 use chrono::{DateTime, Utc};11 use chrono::{DateTime, Utc};
12+use serde::{Deserialize, Serialize};
12 use std::collections::HashMap;13 use std::collections::HashMap;
13 use std::path::PathBuf;14 use std::path::PathBuf;
14 15
16+/// Theme options for the application
17+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18+pub enum Theme {
19+ /// Dark theme (default)
20+ Dark,
21+ /// Light theme
22+ Light,
23+}
24+
25+impl Default for Theme {
26+ fn default() -> Self {
27+ Theme::Dark
28+ }
29+}
30+
31+impl Theme {
32+ /// Toggles between light and dark theme
33+ pub fn toggle(&mut self) {
34+ *self = match self {
35+ Theme::Dark => Theme::Light,
36+ Theme::Light => Theme::Dark,
37+ };
38+ }
39+
40+ /// Returns the opposite theme
41+ #[allow(dead_code)]
42+ pub fn opposite(&self) -> Self {
43+ match self {
44+ Theme::Dark => Theme::Light,
45+ Theme::Light => Theme::Dark,
46+ }
47+ }
48+}
49+
15 /// Main application state50 /// Main application state
16 ///51 ///
17 /// This struct holds all the state needed for the RSS Podcast Downloader application.52 /// This struct holds all the state needed for the RSS Podcast Downloader application.
@@ -83,6 +118,8 @@ pub struct AppState {
83 pub notification_manager: NotificationManager,118 pub notification_manager: NotificationManager,
84 /// List of playlists for grouping feeds119 /// List of playlists for grouping feeds
85 pub playlists: Vec<Playlist>,120 pub playlists: Vec<Playlist>,
121+ /// Current theme (dark or light)
122+ pub theme: Theme,
86 // Filter state123 // Filter state
87 /// Filter: date from (inclusive)124 /// Filter: date from (inclusive)
88 #[allow(dead_code)]125 #[allow(dead_code)]
@@ -179,6 +216,7 @@ impl AppState {
179 sort_direction: "asc".to_string(),216 sort_direction: "asc".to_string(),
180 statistics: AppStatistics::new(),217 statistics: AppStatistics::new(),
181 history_manager: HistoryManager::new(),218 history_manager: HistoryManager::new(),
219+ theme: Theme::default(),
182 }220 }
183 }221 }
184 222
@@ -479,6 +517,27 @@ impl AppState {
479 pub fn clear_export_import_error(&mut self) {517 pub fn clear_export_import_error(&mut self) {
480 self.export_import_error = None;518 self.export_import_error = None;
481 }519 }
520+
521+ /// Toggles the theme between light and dark
522+ pub fn toggle_theme(&mut self) {
523+ self.theme.toggle();
524+ let theme_name = match self.theme {
525+ Theme::Dark => "sombre",
526+ Theme::Light => "clair",
527+ };
528+ self.notification_manager.info(format!("Thème basculé vers: {}", theme_name));
529+ }
530+
531+ /// Sets the theme to a specific value
532+ #[allow(dead_code)]
533+ pub fn set_theme(&mut self, theme: Theme) {
534+ self.theme = theme;
535+ }
536+
537+ /// Gets the current theme
538+ pub fn get_theme(&self) -> Theme {
539+ self.theme
540+ }
482 }541 }
483 542
484 #[cfg(test)]543 #[cfg(test)]
modified src/config/mod.rs +1 -1
@@ -17,7 +17,7 @@ pub mod storage;
1717 // kept for future extensibility and API completeness
1818 #[allow(dead_code)]
1919 #[allow(unused_imports)]
20-pub use app_state::AppState;
20+pub use app_state::{AppState, Theme};
2121 #[allow(dead_code)]
2222 #[allow(unused_imports)]
2323 pub use backup::{BackupInfo, BackupManager};
@@ -17,7 +17,7 @@ pub mod storage;
17 // kept for future extensibility and API completeness17 // kept for future extensibility and API completeness
18 #[allow(dead_code)]18 #[allow(dead_code)]
19 #[allow(unused_imports)]19 #[allow(unused_imports)]
20-pub use app_state::AppState;20+pub use app_state::{AppState, Theme};
21 #[allow(dead_code)]21 #[allow(dead_code)]
22 #[allow(unused_imports)]22 #[allow(unused_imports)]
23 pub use backup::{BackupInfo, BackupManager};23 pub use backup::{BackupInfo, BackupManager};
modified src/ui/status_bar.rs +32 -2
@@ -3,14 +3,27 @@
33 //! This module contains the status bar display logic.
44
55 use crate::config::AppState;
6+use crate::config::Theme;
67 use egui::{Align, Layout, Ui};
78
89 /// Draws the status bar at the bottom of the application
910 pub fn draw_status_bar(state: &mut AppState, ui: &mut Ui) {
11+ // Determine colors based on theme
12+ let (bg_color, stroke_color) = match state.get_theme() {
13+ Theme::Dark => (
14+ egui::Color32::from_rgb(36, 39, 58),
15+ egui::Color32::from_rgb(70, 78, 95)
16+ ),
17+ Theme::Light => (
18+ egui::Color32::from_rgb(230, 235, 245),
19+ egui::Color32::from_rgb(180, 195, 210)
20+ ),
21+ };
22+
1023 // Frame pour la barre de statut
1124 egui::Frame::default()
12- .fill(egui::Color32::from_rgb(36, 39, 58))
13- .stroke(egui::Stroke::new(1.0, egui::Color32::from_rgb(70, 78, 95)))
25+ .fill(bg_color)
26+ .stroke(egui::Stroke::new(1.0, stroke_color))
1427 .rounding(egui::Rounding::same(4.0))
1528 .inner_margin(egui::Margin::symmetric(12.0, 8.0))
1629 .show(ui, |ui| {
@@ -56,6 +69,23 @@ pub fn draw_status_bar(state: &mut AppState, ui: &mut Ui) {
5669 ui.colored_label(egui::Color32::from_rgb(150, 200, 150), "Prêt");
5770 }
5871
72+ // Theme toggle button
73+ let theme_icon = match state.get_theme() {
74+ Theme::Dark => "🌙",
75+ Theme::Light => "☀️",
76+ };
77+ let theme_tooltip = match state.get_theme() {
78+ Theme::Dark => "Basculer vers le thème clair",
79+ Theme::Light => "Basculer vers le thème sombre",
80+ };
81+
82+ if ui.link(egui::RichText::new(theme_icon).size(16.0))
83+ .on_hover_text(theme_tooltip)
84+ .clicked()
85+ {
86+ state.toggle_theme();
87+ }
88+
5989 // Feed count on the right avec icône
6090 ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
6191 ui.label(egui::RichText::new(format!("📊 {} flux ", feed_count))
@@ -3,14 +3,27 @@
3 //! This module contains the status bar display logic.3 //! This module contains the status bar display logic.
4 4
5 use crate::config::AppState;5 use crate::config::AppState;
6+use crate::config::Theme;
6 use egui::{Align, Layout, Ui};7 use egui::{Align, Layout, Ui};
7 8
8 /// Draws the status bar at the bottom of the application9 /// Draws the status bar at the bottom of the application
9 pub fn draw_status_bar(state: &mut AppState, ui: &mut Ui) {10 pub fn draw_status_bar(state: &mut AppState, ui: &mut Ui) {
11+ // Determine colors based on theme
12+ let (bg_color, stroke_color) = match state.get_theme() {
13+ Theme::Dark => (
14+ egui::Color32::from_rgb(36, 39, 58),
15+ egui::Color32::from_rgb(70, 78, 95)
16+ ),
17+ Theme::Light => (
18+ egui::Color32::from_rgb(230, 235, 245),
19+ egui::Color32::from_rgb(180, 195, 210)
20+ ),
21+ };
22+
10 // Frame pour la barre de statut23 // Frame pour la barre de statut
11 egui::Frame::default()24 egui::Frame::default()
12- .fill(egui::Color32::from_rgb(36, 39, 58))25+ .fill(bg_color)
13- .stroke(egui::Stroke::new(1.0, egui::Color32::from_rgb(70, 78, 95)))26+ .stroke(egui::Stroke::new(1.0, stroke_color))
14 .rounding(egui::Rounding::same(4.0))27 .rounding(egui::Rounding::same(4.0))
15 .inner_margin(egui::Margin::symmetric(12.0, 8.0))28 .inner_margin(egui::Margin::symmetric(12.0, 8.0))
16 .show(ui, |ui| {29 .show(ui, |ui| {
@@ -56,6 +69,23 @@ pub fn draw_status_bar(state: &mut AppState, ui: &mut Ui) {
56 ui.colored_label(egui::Color32::from_rgb(150, 200, 150), "Prêt");69 ui.colored_label(egui::Color32::from_rgb(150, 200, 150), "Prêt");
57 }70 }
58 71
72+ // Theme toggle button
73+ let theme_icon = match state.get_theme() {
74+ Theme::Dark => "🌙",
75+ Theme::Light => "☀️",
76+ };
77+ let theme_tooltip = match state.get_theme() {
78+ Theme::Dark => "Basculer vers le thème clair",
79+ Theme::Light => "Basculer vers le thème sombre",
80+ };
81+
82+ if ui.link(egui::RichText::new(theme_icon).size(16.0))
83+ .on_hover_text(theme_tooltip)
84+ .clicked()
85+ {
86+ state.toggle_theme();
87+ }
88+
59 // Feed count on the right avec icône89 // Feed count on the right avec icône
60 ui.with_layout(Layout::right_to_left(Align::Center), |ui| {90 ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
61 ui.label(egui::RichText::new(format!("📊 {} flux ", feed_count))91 ui.label(egui::RichText::new(format!("📊 {} flux ", feed_count))