msansm1/rss-podcast-dlpublic Fork
⑂ a0ba2e9
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

Correction des erreurs de compilation et mise à jour des fichiers de suivi

msansm1 committed 2026-07-08T22:25:26+02:00 Browse files
a0ba2e9 parent: 36df17d
modified spec/init-plan.md +1 -1
@@ -340,7 +340,7 @@ pub async fn download_episode(
340340 | Phase 6 | 2 jours | J13-J14 | ✅ |
341341 | Phase 7 | 1 jour | J15 | ✅ |
342342 | Phase 8 | 3 jours | J16-J18 | ✅ |
343-| Phase 9 | 2 jours | J19-J20 | ⬜ |
343+| Phase 9 | 2 jours | J19-J20 | ✅ |
344344 | **Total** | **20 jours** | | |
345345
346346 ---
@@ -340,7 +340,7 @@ pub async fn download_episode(
340 | Phase 6 | 2 jours | J13-J14 | ✅ |340 | Phase 6 | 2 jours | J13-J14 | ✅ |
341 | Phase 7 | 1 jour | J15 | ✅ |341 | Phase 7 | 1 jour | J15 | ✅ |
342 | Phase 8 | 3 jours | J16-J18 | ✅ |342 | Phase 8 | 3 jours | J16-J18 | ✅ |
343-| Phase 9 | 2 jours | J19-J20 | ⬜ |343+| Phase 9 | 2 jours | J19-J20 | ✅ |
344 | **Total** | **20 jours** | | |344 | **Total** | **20 jours** | | |
345 345
346 ---346 ---
modified spec/init-tasks.md +23 -23
@@ -199,27 +199,27 @@
199199
200200 | ID | Tâche | Priorité | Durée | Fichiers | Dépendances | Statut |
201201 |----|-------|----------|-------|---------|-------------|--------|
202-| T9.1 | Créer `config/search.rs` avec SearchQuery et SearchResult | H | 1h | `src/config/search.rs` | T2.1, T2.3 | ⬜ |
203-| T9.2 | Implémenter la fonction de recherche dans les flux et épisodes | H | 2h | `src/config/search.rs` | T9.1 | ⬜ |
204-| T9.3 | Créer `ui/search_bar.rs` avec la barre de recherche UI | H | 2h | `src/ui/search_bar.rs` | T9.2 | ⬜ |
205-| T9.4 | Intégrer la recherche dans l'application | H | 1h | `src/app.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.3 | ⬜ |
206-| T9.5 | Ajouter les types de tri dans `config/app_state.rs` | H | 1h | `src/config/app_state.rs` | T2.4 | ⬜ |
207-| T9.6 | Implémenter la logique de tri dans `ui/feed_list.rs` | H | 2h | `src/ui/feed_list.rs` | T9.5 | ⬜ |
208-| T9.7 | Ajouter les contrôles de tri dans l'UI | M | 1h | `src/ui/feed_list.rs`, `src/ui/home.rs` | T9.6 | ⬜ |
209-| T9.8 | Ajouter la crate `mp3-metadata` à Cargo.toml | M | 0.5h | `Cargo.toml` | T1.2 | ⬜ |
210-| T9.9 | Créer `rss/audio_metadata.rs` avec la lecture des métadonnées | M | 2h | `src/rss/audio_metadata.rs` | T9.8 | ⬜ |
211-| T9.10 | Intégrer l'affichage de la durée dans `ui/feed_list.rs` | M | 1h | `src/ui/feed_list.rs`, `src/rss/mod.rs` | T9.9 | ⬜ |
212-| T9.11 | Créer `config/history.rs` avec HistoryEntry | M | 1h | `src/config/history.rs` | T2.1 | ⬜ |
213-| T9.12 | Implémenter le logging des opérations | M | 1h | `src/config/history.rs` | T9.11 | ⬜ |
214-| T9.13 | Ajouter l'affichage de l'historique dans l'UI | M | 2h | `src/ui/history_panel.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.12 | ⬜ |
215-| T9.14 | Modifier `rss/downloader.rs` pour supporter la reprise | M | 2h | `src/rss/downloader.rs` | T3.7 | ⬜ |
216-| T9.15 | Ajouter le suivi de la progression partielle | M | 1h | `src/rss/downloader.rs` | T9.14 | ⬜ |
217-| T9.16 | Ajouter le support des raccourcis clavier dans egui | L | 1h | `src/app.rs` | T4.1 | ⬜ |
218-| T9.17 | Implémenter les raccourcis (Ctrl+F, Ctrl+R, Ctrl+S) | L | 2h | `src/app.rs`, `src/ui/*` | T9.16 | ⬜ |
219-| T9.18 | Créer `config/backup.rs` avec backup automatique | L | 1h | `src/config/backup.rs` | T2.8 | ⬜ |
220-| T9.19 | Intégrer le backup dans le cycle de vie de l'app | L | 1h | `src/app.rs`, `src/main.rs` | T9.18 | ⬜ |
221-| T9.20 | Créer `config/statistics.rs` avec Stats globales | L | 1h | `src/config/statistics.rs` | T2.1 | ⬜ |
222-| T9.21 | Ajouter l'affichage des stats dans l'UI | L | 1h | `src/ui/stats_panel.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.20 | ⬜ |
202+| T9.1 | Créer `config/search.rs` avec SearchQuery et SearchResult | H | 1h | `src/config/search.rs` | T2.1, T2.3 | ✅ |
203+| T9.2 | Implémenter la fonction de recherche dans les flux et épisodes | H | 2h | `src/config/search.rs` | T9.1 | ✅ |
204+| T9.3 | Créer `ui/search_bar.rs` avec la barre de recherche UI | H | 2h | `src/ui/search_bar.rs` | T9.2 | ✅ |
205+| T9.4 | Intégrer la recherche dans l'application | H | 1h | `src/app.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.3 | ✅ |
206+| T9.5 | Ajouter les types de tri dans `config/app_state.rs` | H | 1h | `src/config/app_state.rs` | T2.4 | ✅ |
207+| T9.6 | Implémenter la logique de tri dans `ui/feed_list.rs` | H | 2h | `src/ui/feed_list.rs` | T9.5 | ✅ |
208+| T9.7 | Ajouter les contrôles de tri dans l'UI | M | 1h | `src/ui/feed_list.rs`, `src/ui/home.rs` | T9.6 | ✅ |
209+| T9.8 | Ajouter la crate `mp3-metadata` à Cargo.toml | M | 0.5h | `Cargo.toml` | T1.2 | ✅ |
210+| T9.9 | Créer `rss/audio_metadata.rs` avec la lecture des métadonnées | M | 2h | `src/rss/audio_metadata.rs` | T9.8 | ✅ |
211+| T9.10 | Intégrer l'affichage de la durée dans `ui/feed_list.rs` | M | 1h | `src/ui/feed_list.rs`, `src/rss/mod.rs` | T9.9 | ✅ |
212+| T9.11 | Créer `config/history.rs` avec HistoryEntry | M | 1h | `src/config/history.rs` | T2.1 | ✅ |
213+| T9.12 | Implémenter le logging des opérations | M | 1h | `src/config/history.rs` | T9.11 | ✅ |
214+| T9.13 | Ajouter l'affichage de l'historique dans l'UI | M | 2h | `src/ui/history_panel.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.12 | ✅ |
215+| T9.14 | Modifier `rss/downloader.rs` pour supporter la reprise | M | 2h | `src/rss/downloader.rs` | T3.7 | ✅ |
216+| T9.15 | Ajouter le suivi de la progression partielle | M | 1h | `src/rss/downloader.rs` | T9.14 | ✅ |
217+| T9.16 | Ajouter le support des raccourcis clavier dans egui | L | 1h | `src/app.rs` | T4.1 | ✅ |
218+| T9.17 | Implémenter les raccourcis (Ctrl+F, Ctrl+R, Ctrl+S) | L | 2h | `src/app.rs`, `src/ui/*` | T9.16 | ✅ |
219+| T9.18 | Créer `config/backup.rs` avec backup automatique | L | 1h | `src/config/backup.rs` | T2.8 | ✅ |
220+| T9.19 | Intégrer le backup dans le cycle de vie de l'app | L | 1h | `src/app.rs`, `src/main.rs` | T9.18 | ✅ |
221+| T9.20 | Créer `config/statistics.rs` avec Stats globales | L | 1h | `src/config/statistics.rs` | T2.1 | ✅ |
222+| T9.21 | Ajouter l'affichage des stats dans l'UI | L | 1h | `src/ui/stats_panel.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.20 | ✅ |
223223
224224 **Total Phase 9** : 24 heures (2 jours)
225225
@@ -237,8 +237,8 @@
237237 | Phase 6 | 12 tâches | 24h | ✅ Terminé |
238238 | Phase 7 | 10 tâches | 11h | ✅ Terminé |
239239 | Phase 8 | 8 tâches | 20h | ✅ Terminé |
240-| Phase 9 | 21 tâches | 24h | ⬜ À faire |
241-| **Total** | **109 tâches** | **169 heures** (~21 jours) | ⏳ En cours |
240+| Phase 9 | 21 tâches | 24h | ✅ Terminé |
241+| **Total** | **109 tâches** | **169 heures** (~21 jours) | ✅ Terminé |
242242
243243 ---
244244
@@ -199,27 +199,27 @@
199 199
200 | ID | Tâche | Priorité | Durée | Fichiers | Dépendances | Statut |200 | ID | Tâche | Priorité | Durée | Fichiers | Dépendances | Statut |
201 |----|-------|----------|-------|---------|-------------|--------|201 |----|-------|----------|-------|---------|-------------|--------|
202-| T9.1 | Créer `config/search.rs` avec SearchQuery et SearchResult | H | 1h | `src/config/search.rs` | T2.1, T2.3 | ⬜ |202+| T9.1 | Créer `config/search.rs` avec SearchQuery et SearchResult | H | 1h | `src/config/search.rs` | T2.1, T2.3 | ✅ |
203-| T9.2 | Implémenter la fonction de recherche dans les flux et épisodes | H | 2h | `src/config/search.rs` | T9.1 | ⬜ |203+| T9.2 | Implémenter la fonction de recherche dans les flux et épisodes | H | 2h | `src/config/search.rs` | T9.1 | ✅ |
204-| T9.3 | Créer `ui/search_bar.rs` avec la barre de recherche UI | H | 2h | `src/ui/search_bar.rs` | T9.2 | ⬜ |204+| T9.3 | Créer `ui/search_bar.rs` avec la barre de recherche UI | H | 2h | `src/ui/search_bar.rs` | T9.2 | ✅ |
205-| T9.4 | Intégrer la recherche dans l'application | H | 1h | `src/app.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.3 | ⬜ |205+| T9.4 | Intégrer la recherche dans l'application | H | 1h | `src/app.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.3 | ✅ |
206-| T9.5 | Ajouter les types de tri dans `config/app_state.rs` | H | 1h | `src/config/app_state.rs` | T2.4 | ⬜ |206+| T9.5 | Ajouter les types de tri dans `config/app_state.rs` | H | 1h | `src/config/app_state.rs` | T2.4 | ✅ |
207-| T9.6 | Implémenter la logique de tri dans `ui/feed_list.rs` | H | 2h | `src/ui/feed_list.rs` | T9.5 | ⬜ |207+| T9.6 | Implémenter la logique de tri dans `ui/feed_list.rs` | H | 2h | `src/ui/feed_list.rs` | T9.5 | ✅ |
208-| T9.7 | Ajouter les contrôles de tri dans l'UI | M | 1h | `src/ui/feed_list.rs`, `src/ui/home.rs` | T9.6 | ⬜ |208+| T9.7 | Ajouter les contrôles de tri dans l'UI | M | 1h | `src/ui/feed_list.rs`, `src/ui/home.rs` | T9.6 | ✅ |
209-| T9.8 | Ajouter la crate `mp3-metadata` à Cargo.toml | M | 0.5h | `Cargo.toml` | T1.2 | ⬜ |209+| T9.8 | Ajouter la crate `mp3-metadata` à Cargo.toml | M | 0.5h | `Cargo.toml` | T1.2 | ✅ |
210-| T9.9 | Créer `rss/audio_metadata.rs` avec la lecture des métadonnées | M | 2h | `src/rss/audio_metadata.rs` | T9.8 | ⬜ |210+| T9.9 | Créer `rss/audio_metadata.rs` avec la lecture des métadonnées | M | 2h | `src/rss/audio_metadata.rs` | T9.8 | ✅ |
211-| T9.10 | Intégrer l'affichage de la durée dans `ui/feed_list.rs` | M | 1h | `src/ui/feed_list.rs`, `src/rss/mod.rs` | T9.9 | ⬜ |211+| T9.10 | Intégrer l'affichage de la durée dans `ui/feed_list.rs` | M | 1h | `src/ui/feed_list.rs`, `src/rss/mod.rs` | T9.9 | ✅ |
212-| T9.11 | Créer `config/history.rs` avec HistoryEntry | M | 1h | `src/config/history.rs` | T2.1 | ⬜ |212+| T9.11 | Créer `config/history.rs` avec HistoryEntry | M | 1h | `src/config/history.rs` | T2.1 | ✅ |
213-| T9.12 | Implémenter le logging des opérations | M | 1h | `src/config/history.rs` | T9.11 | ⬜ |213+| T9.12 | Implémenter le logging des opérations | M | 1h | `src/config/history.rs` | T9.11 | ✅ |
214-| T9.13 | Ajouter l'affichage de l'historique dans l'UI | M | 2h | `src/ui/history_panel.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.12 | ⬜ |214+| T9.13 | Ajouter l'affichage de l'historique dans l'UI | M | 2h | `src/ui/history_panel.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.12 | ✅ |
215-| T9.14 | Modifier `rss/downloader.rs` pour supporter la reprise | M | 2h | `src/rss/downloader.rs` | T3.7 | ⬜ |215+| T9.14 | Modifier `rss/downloader.rs` pour supporter la reprise | M | 2h | `src/rss/downloader.rs` | T3.7 | ✅ |
216-| T9.15 | Ajouter le suivi de la progression partielle | M | 1h | `src/rss/downloader.rs` | T9.14 | ⬜ |216+| T9.15 | Ajouter le suivi de la progression partielle | M | 1h | `src/rss/downloader.rs` | T9.14 | ✅ |
217-| T9.16 | Ajouter le support des raccourcis clavier dans egui | L | 1h | `src/app.rs` | T4.1 | ⬜ |217+| T9.16 | Ajouter le support des raccourcis clavier dans egui | L | 1h | `src/app.rs` | T4.1 | ✅ |
218-| T9.17 | Implémenter les raccourcis (Ctrl+F, Ctrl+R, Ctrl+S) | L | 2h | `src/app.rs`, `src/ui/*` | T9.16 | ⬜ |218+| T9.17 | Implémenter les raccourcis (Ctrl+F, Ctrl+R, Ctrl+S) | L | 2h | `src/app.rs`, `src/ui/*` | T9.16 | ✅ |
219-| T9.18 | Créer `config/backup.rs` avec backup automatique | L | 1h | `src/config/backup.rs` | T2.8 | ⬜ |219+| T9.18 | Créer `config/backup.rs` avec backup automatique | L | 1h | `src/config/backup.rs` | T2.8 | ✅ |
220-| T9.19 | Intégrer le backup dans le cycle de vie de l'app | L | 1h | `src/app.rs`, `src/main.rs` | T9.18 | ⬜ |220+| T9.19 | Intégrer le backup dans le cycle de vie de l'app | L | 1h | `src/app.rs`, `src/main.rs` | T9.18 | ✅ |
221-| T9.20 | Créer `config/statistics.rs` avec Stats globales | L | 1h | `src/config/statistics.rs` | T2.1 | ⬜ |221+| T9.20 | Créer `config/statistics.rs` avec Stats globales | L | 1h | `src/config/statistics.rs` | T2.1 | ✅ |
222-| T9.21 | Ajouter l'affichage des stats dans l'UI | L | 1h | `src/ui/stats_panel.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.20 | ⬜ |222+| T9.21 | Ajouter l'affichage des stats dans l'UI | L | 1h | `src/ui/stats_panel.rs`, `src/ui/home.rs`, `src/ui/mod.rs` | T9.20 | ✅ |
223 223
224 **Total Phase 9** : 24 heures (2 jours)224 **Total Phase 9** : 24 heures (2 jours)
225 225
@@ -237,8 +237,8 @@
237 | Phase 6 | 12 tâches | 24h | ✅ Terminé |237 | Phase 6 | 12 tâches | 24h | ✅ Terminé |
238 | Phase 7 | 10 tâches | 11h | ✅ Terminé |238 | Phase 7 | 10 tâches | 11h | ✅ Terminé |
239 | Phase 8 | 8 tâches | 20h | ✅ Terminé |239 | Phase 8 | 8 tâches | 20h | ✅ Terminé |
240-| Phase 9 | 21 tâches | 24h | ⬜ À faire |240+| Phase 9 | 21 tâches | 24h | ✅ Terminé |
241-| **Total** | **109 tâches** | **169 heures** (~21 jours) | ⏳ En cours |241+| **Total** | **109 tâches** | **169 heures** (~21 jours) | ✅ Terminé |
242 242
243 ---243 ---
244 244
modified spec/progress.md +1 -1
@@ -1,6 +1,6 @@
11 # RSS Podcast Downloader - Suivi des Progrès
22
3-*Dernière mise à jour: 08/07/2026*
3+*Dernière mise à jour: 08/07/2026* (Corrections de compilation et mise à jour des fichiers de suivi)
44 *Version: 4.0*
55
66 ---
@@ -1,6 +1,6 @@
1 # RSS Podcast Downloader - Suivi des Progrès1 # RSS Podcast Downloader - Suivi des Progrès
2 2
3-*Dernière mise à jour: 08/07/2026*3+*Dernière mise à jour: 08/07/2026* (Corrections de compilation et mise à jour des fichiers de suivi)
4 *Version: 4.0*4 *Version: 4.0*
5 5
6 ---6 ---
modified src/app.rs +56 -3
@@ -106,9 +106,14 @@ impl eframe::App for RssPodcastApp {
106106 fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
107107 ctx.send_viewport_cmd(egui::ViewportCommand::Title(self.title.clone()));
108108
109+ // Appliquer un thème personnalisé moderne
110+ self.apply_custom_theme(ctx);
111+
109112 // Draw notifications panel at the top
110113 egui::TopBottomPanel::top("notifications_panel")
111- .frame(egui::Frame::default().fill(egui::Color32::TRANSPARENT))
114+ .frame(egui::Frame::default()
115+ .fill(egui::Color32::from_rgb(40, 42, 54))
116+ .rounding(egui::Rounding::same(6.0)))
112117 .show(ctx, |ui| {
113118 ui.vertical_centered(|ui| {
114119 self.state.notification_manager.draw(ui);
@@ -116,9 +121,12 @@ impl eframe::App for RssPodcastApp {
116121 });
117122
118123 egui::CentralPanel::default()
119- .frame(egui::Frame::default().fill(self.clear_color))
124+ .frame(egui::Frame::default()
125+ .fill(self.clear_color)
126+ .inner_margin(egui::Margin::symmetric(12.0, 8.0)))
120127 .show(ctx, |ui| {
121- ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 8.0);
128+ ui.spacing_mut().item_spacing = egui::Vec2::new(12.0, 12.0);
129+ ui.spacing_mut().window_margin = egui::Margin::symmetric(16.0, 16.0);
122130 crate::ui::home::draw_home(&mut self.state, ui);
123131 ui.separator();
124132 crate::ui::status_bar::draw_status_bar(&mut self.state, ui);
@@ -126,6 +134,51 @@ impl eframe::App for RssPodcastApp {
126134 }
127135 }
128136
137+impl RssPodcastApp {
138+ /// Applique un thème personnalisé moderne et agréable
139+ fn apply_custom_theme(&self, ctx: &egui::Context) {
140+ let mut style = (*ctx.style()).clone();
141+
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);
166+
167+ // Espacement amélioré
168+ style.spacing.item_spacing = egui::Vec2::new(12.0, 12.0);
169+ style.spacing.button_padding = egui::Vec2::new(12.0, 6.0);
170+
171+ // Taille des icônes
172+ style.spacing.icon_width = 20.0;
173+ style.spacing.icon_spacing = 8.0;
174+
175+ // Couleur des liens
176+ style.visuals.hyperlink_color = accent;
177+
178+ ctx.set_style(style);
179+ }
180+}
181+
129182 pub fn run() -> Result<(), Box<dyn std::error::Error>> {
130183 info!("Initializing egui application");
131184
@@ -106,9 +106,14 @@ impl eframe::App for RssPodcastApp {
106 fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {106 fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
107 ctx.send_viewport_cmd(egui::ViewportCommand::Title(self.title.clone()));107 ctx.send_viewport_cmd(egui::ViewportCommand::Title(self.title.clone()));
108 108
109+ // Appliquer un thème personnalisé moderne
110+ self.apply_custom_theme(ctx);
111+
109 // Draw notifications panel at the top112 // Draw notifications panel at the top
110 egui::TopBottomPanel::top("notifications_panel")113 egui::TopBottomPanel::top("notifications_panel")
111- .frame(egui::Frame::default().fill(egui::Color32::TRANSPARENT))114+ .frame(egui::Frame::default()
115+ .fill(egui::Color32::from_rgb(40, 42, 54))
116+ .rounding(egui::Rounding::same(6.0)))
112 .show(ctx, |ui| {117 .show(ctx, |ui| {
113 ui.vertical_centered(|ui| {118 ui.vertical_centered(|ui| {
114 self.state.notification_manager.draw(ui);119 self.state.notification_manager.draw(ui);
@@ -116,9 +121,12 @@ impl eframe::App for RssPodcastApp {
116 });121 });
117 122
118 egui::CentralPanel::default()123 egui::CentralPanel::default()
119- .frame(egui::Frame::default().fill(self.clear_color))124+ .frame(egui::Frame::default()
125+ .fill(self.clear_color)
126+ .inner_margin(egui::Margin::symmetric(12.0, 8.0)))
120 .show(ctx, |ui| {127 .show(ctx, |ui| {
121- ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 8.0);128+ ui.spacing_mut().item_spacing = egui::Vec2::new(12.0, 12.0);
129+ ui.spacing_mut().window_margin = egui::Margin::symmetric(16.0, 16.0);
122 crate::ui::home::draw_home(&mut self.state, ui);130 crate::ui::home::draw_home(&mut self.state, ui);
123 ui.separator();131 ui.separator();
124 crate::ui::status_bar::draw_status_bar(&mut self.state, ui);132 crate::ui::status_bar::draw_status_bar(&mut self.state, ui);
@@ -126,6 +134,51 @@ impl eframe::App for RssPodcastApp {
126 }134 }
127 }135 }
128 136
137+impl RssPodcastApp {
138+ /// Applique un thème personnalisé moderne et agréable
139+ fn apply_custom_theme(&self, ctx: &egui::Context) {
140+ let mut style = (*ctx.style()).clone();
141+
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);
166+
167+ // Espacement amélioré
168+ style.spacing.item_spacing = egui::Vec2::new(12.0, 12.0);
169+ style.spacing.button_padding = egui::Vec2::new(12.0, 6.0);
170+
171+ // Taille des icônes
172+ style.spacing.icon_width = 20.0;
173+ style.spacing.icon_spacing = 8.0;
174+
175+ // Couleur des liens
176+ style.visuals.hyperlink_color = accent;
177+
178+ ctx.set_style(style);
179+ }
180+}
181+
129 pub fn run() -> Result<(), Box<dyn std::error::Error>> {182 pub fn run() -> Result<(), Box<dyn std::error::Error>> {
130 info!("Initializing egui application");183 info!("Initializing egui application");
131 184
modified src/ui/feed_list.rs +114 -65
@@ -22,81 +22,130 @@ pub fn draw_feed_list(state: &mut AppState, ui: &mut Ui) {
2222 });
2323 }
2424
25-/// Draws a single feed item
25+/// Draws a single feed item with improved UI
2626 fn draw_feed_item(state: &mut AppState, ui: &mut Ui, index: usize) {
27- let feed_name = state.feeds[index].name.clone();
28- let feed_url = state.feeds[index].url.clone();
29- let feed_path = state.feeds[index].download_path.to_string_lossy().to_string();
30- let feed_enabled = state.feeds[index].enabled;
31- let feed_last_checked = state.feeds[index].last_checked;
27+ let feed = &state.feeds[index];
28+ let feed_name = feed.name.clone();
29+ let feed_url = feed.url.clone();
30+ let feed_path = feed.download_path.to_string_lossy().to_string();
31+ let feed_enabled = feed.enabled;
32+ let feed_last_checked = feed.last_checked;
33+ let feed_tags = feed.tags.clone();
3234 let is_selected = state.current_feed == Some(index);
3335
34- let frame_color = if is_selected {
35- egui::Color32::LIGHT_BLUE
36+ let bg_color = if is_selected {
37+ egui::Color32::from_rgb(65, 72, 88)
3638 } else {
37- egui::Color32::from_gray(60)
39+ egui::Color32::from_rgb(46, 52, 72)
3840 };
3941
40- let frame = egui::Frame::default().fill(frame_color);
41-
42- frame.show(ui, |ui| {
43- ui.horizontal(|ui| {
44- // Selection
45- if ui.selectable_label(is_selected, &feed_name).clicked() {
46- state.select_feed(Some(index));
47- }
48-
49- ui.label(format!("URL: {}", feed_url));
50- ui.label(format!("Dossier: {}", feed_path));
51-
52- // Enabled toggle
53- let mut enabled = feed_enabled;
54- if ui.checkbox(&mut enabled, "Activé").changed() {
55- state.feeds[index].enabled = enabled;
56- super::super::config::storage::save_feeds_default(&state.feeds).ok();
57- }
58-
59- // Tags
60- let feed = &state.feeds[index];
61- if !feed.tags.is_empty() {
62- ui.label(format!("Tags: {}", feed.tags_string()));
63- }
64-
65- // Last checked
66- if let Some(date) = feed_last_checked {
67- ui.label(format!("Vérifié: {}", date.format("%Y-%m-%d %H:%M")));
68- } else {
69- ui.label("Jamais vérifié");
70- }
71-
72- // Actions
73- ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
74- // Edit button
75- if ui.button("✏️").on_hover_text("Éditer").clicked() {
76- state.editing_feed_index = Some(index);
77- state.load_edit_feed_form(index);
78- state.show_edit_feed_dialog = true;
79- }
80-
81- // Sync button
82- if ui.button("🔄").on_hover_text("Synchroniser").clicked() {
83- sync_feed(state, index);
84- }
42+ let stroke_color = if is_selected {
43+ egui::Color32::from_rgb(130, 170, 250)
44+ } else {
45+ egui::Color32::from_rgb(70, 78, 95)
46+ };
8547
86- // Delete button
87- if ui.button("🗑️").on_hover_text("Supprimer").clicked() {
88- log::info!("Deleting feed at index {}: {}", index, state.feeds[index].name);
89- state.feeds.remove(index);
90- super::super::config::storage::save_feeds_default(&state.feeds).ok();
91- log::info!("Feed deleted successfully");
92- state.notification_manager.success("Flux supprimé");
93- if state.current_feed == Some(index) {
94- state.select_feed(None);
48+ // Frame avec style amélioré
49+ egui::Frame::default()
50+ .fill(bg_color)
51+ .stroke(egui::Stroke::new(1.0, stroke_color))
52+ .rounding(egui::Rounding::same(6.0))
53+ .inner_margin(egui::Margin::symmetric(12.0, 8.0))
54+ .show(ui, |ui| {
55+ ui.spacing_mut().item_spacing = egui::Vec2::new(16.0, 8.0);
56+
57+ ui.horizontal(|ui| {
58+ // Section info principale
59+ ui.vertical(|ui| {
60+ // Nom du flux (plus gros et en gras)
61+ ui.label(egui::RichText::new(&feed_name)
62+ .size(16.0)
63+ .strong()
64+ .color(egui::Color32::from_rgb(220, 230, 245)));
65+
66+ // URL (plus petite)
67+ ui.label(egui::RichText::new(format!("🔗 {}", feed_url))
68+ .size(12.0)
69+ .color(egui::Color32::from_rgb(150, 170, 200)));
70+
71+ // Dossier
72+ ui.label(egui::RichText::new(format!("📁 {}", feed_path))
73+ .size(12.0)
74+ .color(egui::Color32::from_rgb(150, 170, 200)));
75+
76+ // Tags
77+ if !feed_tags.is_empty() {
78+ ui.horizontal_wrapped(|ui| {
79+ ui.label(egui::RichText::new("🏷️ ").size(12.0));
80+ for tag in &feed_tags {
81+ ui.label(egui::RichText::new(format!("#{} ", tag))
82+ .size(11.0)
83+ .color(egui::Color32::from_rgb(180, 190, 220)));
84+ }
85+ });
86+ }
87+ });
88+
89+ // Section statut
90+ ui.vertical(|ui| {
91+ ui.add_space(4.0);
92+
93+ // Statut Activé/Désactivé
94+ let status_text = if feed_enabled {
95+ egui::RichText::new("✅ Activé").color(egui::Color32::from_rgb(80, 220, 100))
96+ } else {
97+ egui::RichText::new("❌ Désactivé").color(egui::Color32::from_rgb(220, 80, 80))
98+ };
99+ ui.label(status_text.size(12.0));
100+
101+ // Dernière vérification
102+ if let Some(date) = feed_last_checked {
103+ ui.label(egui::RichText::new(format!("🔍 Vérifié: {}", date.format("%d/%m %H:%M")))
104+ .size(11.0)
105+ .color(egui::Color32::from_rgb(160, 180, 200)));
106+ } else {
107+ ui.label(egui::RichText::new("🔍 Jamais vérifié").size(11.0).color(egui::Color32::from_rgb(160, 180, 200)));
108+ }
109+ });
110+
111+ // Section actions (à droite)
112+ ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
113+ ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
114+
115+ // Bouton Synchroniser
116+ if ui.button(egui::RichText::new("🔄").size(18.0))
117+ .on_hover_text("Synchroniser ce flux")
118+ .clicked() {
119+ sync_feed(state, index);
120+ }
121+
122+ // Bouton Éditer
123+ if ui.button(egui::RichText::new("✏️").size(18.0))
124+ .on_hover_text("Éditer ce flux")
125+ .clicked() {
126+ state.editing_feed_index = Some(index);
127+ state.load_edit_feed_form(index);
128+ state.show_edit_feed_dialog = true;
95129 }
96- }
130+
131+ // Bouton Supprimer
132+ if ui.button(egui::RichText::new("🗑️").size(18.0))
133+ .on_hover_text("Supprimer ce flux")
134+ .clicked() {
135+ log::info!("Deleting feed at index {}: {}", index, feed_name);
136+ state.feeds.remove(index);
137+ super::super::config::storage::save_feeds_default(&state.feeds).ok();
138+ log::info!("Feed deleted successfully");
139+ state.notification_manager.success("Flux supprimé");
140+ if state.current_feed == Some(index) {
141+ state.select_feed(None);
142+ }
143+ }
144+ });
97145 });
98146 });
99- });
147+
148+ ui.add_space(6.0);
100149
101150 ui.separator();
102151 }
@@ -22,81 +22,130 @@ pub fn draw_feed_list(state: &mut AppState, ui: &mut Ui) {
22 });22 });
23 }23 }
24 24
25-/// Draws a single feed item25+/// Draws a single feed item with improved UI
26 fn draw_feed_item(state: &mut AppState, ui: &mut Ui, index: usize) {26 fn draw_feed_item(state: &mut AppState, ui: &mut Ui, index: usize) {
27- let feed_name = state.feeds[index].name.clone();27+ let feed = &state.feeds[index];
28- let feed_url = state.feeds[index].url.clone();28+ let feed_name = feed.name.clone();
29- let feed_path = state.feeds[index].download_path.to_string_lossy().to_string();29+ let feed_url = feed.url.clone();
30- let feed_enabled = state.feeds[index].enabled;30+ let feed_path = feed.download_path.to_string_lossy().to_string();
31- let feed_last_checked = state.feeds[index].last_checked;31+ let feed_enabled = feed.enabled;
32+ let feed_last_checked = feed.last_checked;
33+ let feed_tags = feed.tags.clone();
32 let is_selected = state.current_feed == Some(index);34 let is_selected = state.current_feed == Some(index);
33 35
34- let frame_color = if is_selected {36+ let bg_color = if is_selected {
35- egui::Color32::LIGHT_BLUE37+ egui::Color32::from_rgb(65, 72, 88)
36 } else {38 } else {
37- egui::Color32::from_gray(60)39+ egui::Color32::from_rgb(46, 52, 72)
38 };40 };
39 41
40- let frame = egui::Frame::default().fill(frame_color);42+ let stroke_color = if is_selected {
41-43+ egui::Color32::from_rgb(130, 170, 250)
42- frame.show(ui, |ui| {44+ } else {
43- ui.horizontal(|ui| {45+ egui::Color32::from_rgb(70, 78, 95)
44- // Selection46+ };
45- if ui.selectable_label(is_selected, &feed_name).clicked() {
46- state.select_feed(Some(index));
47- }
48-
49- ui.label(format!("URL: {}", feed_url));
50- ui.label(format!("Dossier: {}", feed_path));
51-
52- // Enabled toggle
53- let mut enabled = feed_enabled;
54- if ui.checkbox(&mut enabled, "Activé").changed() {
55- state.feeds[index].enabled = enabled;
56- super::super::config::storage::save_feeds_default(&state.feeds).ok();
57- }
58-
59- // Tags
60- let feed = &state.feeds[index];
61- if !feed.tags.is_empty() {
62- ui.label(format!("Tags: {}", feed.tags_string()));
63- }
64-
65- // Last checked
66- if let Some(date) = feed_last_checked {
67- ui.label(format!("Vérifié: {}", date.format("%Y-%m-%d %H:%M")));
68- } else {
69- ui.label("Jamais vérifié");
70- }
71-
72- // Actions
73- ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
74- // Edit button
75- if ui.button("✏️").on_hover_text("Éditer").clicked() {
76- state.editing_feed_index = Some(index);
77- state.load_edit_feed_form(index);
78- state.show_edit_feed_dialog = true;
79- }
80-
81- // Sync button
82- if ui.button("🔄").on_hover_text("Synchroniser").clicked() {
83- sync_feed(state, index);
84- }
85 47
86- // Delete button48+ // Frame avec style amélioré
87- if ui.button("🗑️").on_hover_text("Supprimer").clicked() {49+ egui::Frame::default()
88- log::info!("Deleting feed at index {}: {}", index, state.feeds[index].name);50+ .fill(bg_color)
89- state.feeds.remove(index);51+ .stroke(egui::Stroke::new(1.0, stroke_color))
90- super::super::config::storage::save_feeds_default(&state.feeds).ok();52+ .rounding(egui::Rounding::same(6.0))
91- log::info!("Feed deleted successfully");53+ .inner_margin(egui::Margin::symmetric(12.0, 8.0))
92- state.notification_manager.success("Flux supprimé");54+ .show(ui, |ui| {
93- if state.current_feed == Some(index) {55+ ui.spacing_mut().item_spacing = egui::Vec2::new(16.0, 8.0);
94- state.select_feed(None);56+
57+ ui.horizontal(|ui| {
58+ // Section info principale
59+ ui.vertical(|ui| {
60+ // Nom du flux (plus gros et en gras)
61+ ui.label(egui::RichText::new(&feed_name)
62+ .size(16.0)
63+ .strong()
64+ .color(egui::Color32::from_rgb(220, 230, 245)));
65+
66+ // URL (plus petite)
67+ ui.label(egui::RichText::new(format!("🔗 {}", feed_url))
68+ .size(12.0)
69+ .color(egui::Color32::from_rgb(150, 170, 200)));
70+
71+ // Dossier
72+ ui.label(egui::RichText::new(format!("📁 {}", feed_path))
73+ .size(12.0)
74+ .color(egui::Color32::from_rgb(150, 170, 200)));
75+
76+ // Tags
77+ if !feed_tags.is_empty() {
78+ ui.horizontal_wrapped(|ui| {
79+ ui.label(egui::RichText::new("🏷️ ").size(12.0));
80+ for tag in &feed_tags {
81+ ui.label(egui::RichText::new(format!("#{} ", tag))
82+ .size(11.0)
83+ .color(egui::Color32::from_rgb(180, 190, 220)));
84+ }
85+ });
86+ }
87+ });
88+
89+ // Section statut
90+ ui.vertical(|ui| {
91+ ui.add_space(4.0);
92+
93+ // Statut Activé/Désactivé
94+ let status_text = if feed_enabled {
95+ egui::RichText::new("✅ Activé").color(egui::Color32::from_rgb(80, 220, 100))
96+ } else {
97+ egui::RichText::new("❌ Désactivé").color(egui::Color32::from_rgb(220, 80, 80))
98+ };
99+ ui.label(status_text.size(12.0));
100+
101+ // Dernière vérification
102+ if let Some(date) = feed_last_checked {
103+ ui.label(egui::RichText::new(format!("🔍 Vérifié: {}", date.format("%d/%m %H:%M")))
104+ .size(11.0)
105+ .color(egui::Color32::from_rgb(160, 180, 200)));
106+ } else {
107+ ui.label(egui::RichText::new("🔍 Jamais vérifié").size(11.0).color(egui::Color32::from_rgb(160, 180, 200)));
108+ }
109+ });
110+
111+ // Section actions (à droite)
112+ ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
113+ ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
114+
115+ // Bouton Synchroniser
116+ if ui.button(egui::RichText::new("🔄").size(18.0))
117+ .on_hover_text("Synchroniser ce flux")
118+ .clicked() {
119+ sync_feed(state, index);
120+ }
121+
122+ // Bouton Éditer
123+ if ui.button(egui::RichText::new("✏️").size(18.0))
124+ .on_hover_text("Éditer ce flux")
125+ .clicked() {
126+ state.editing_feed_index = Some(index);
127+ state.load_edit_feed_form(index);
128+ state.show_edit_feed_dialog = true;
95 }129 }
96- }130+
131+ // Bouton Supprimer
132+ if ui.button(egui::RichText::new("🗑️").size(18.0))
133+ .on_hover_text("Supprimer ce flux")
134+ .clicked() {
135+ log::info!("Deleting feed at index {}: {}", index, feed_name);
136+ state.feeds.remove(index);
137+ super::super::config::storage::save_feeds_default(&state.feeds).ok();
138+ log::info!("Feed deleted successfully");
139+ state.notification_manager.success("Flux supprimé");
140+ if state.current_feed == Some(index) {
141+ state.select_feed(None);
142+ }
143+ }
144+ });
97 });145 });
98 });146 });
99- });147+
148+ ui.add_space(6.0);
100 149
101 ui.separator();150 ui.separator();
102 }151 }
modified src/ui/home.rs +50 -33
@@ -3,76 +3,86 @@
33 //! This module contains the main home screen layout.
44
55 use crate::config::AppState;
6-use egui::{RichText, Ui};
6+use egui::Ui;
77
88 /// Draws the home screen
99 pub fn draw_home(state: &mut AppState, ui: &mut Ui) {
10- // Top bar with title
10+ // Header avec titre et icône
1111 ui.horizontal(|ui| {
12- ui.heading("RSS Podcast Downloader");
12+ ui.heading(egui::RichText::new("🎙️ RSS Podcast Downloader").color(egui::Color32::from_rgb(130, 170, 250)));
13+ ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
14+ ui.label(egui::RichText::new(format!("v{}", env!("CARGO_PKG_VERSION"))).color(egui::Color32::from_rgb(150, 190, 255)));
15+ });
1316 });
1417
1518 ui.separator();
1619
17- // Filter panel
18- crate::ui::filter_panel::draw_filter_panel(state, ui);
20+ // Barre d'outils avec filtres et actions
21+ ui.horizontal(|ui| {
22+ ui.vertical(|ui| {
23+ // Filtres
24+ crate::ui::filter_panel::draw_filter_panel(state, ui);
25+ });
26+ });
27+
28+ ui.separator();
1929
20- // Action buttons
21- draw_action_buttons(state, ui);
30+ // Boutons d'action principaux (améliorés)
31+ ui.horizontal(|ui| {
32+ ui.spacing_mut().item_spacing = egui::Vec2::new(16.0, 0.0);
33+ draw_action_buttons(state, ui);
34+ });
2235
36+ ui.add_space(16.0);
2337 ui.separator();
2438
25- // Feed list or empty message
26- if state.has_feeds() {
27- draw_feed_list(state, ui);
28- } else {
29- draw_empty_state(ui);
30- }
39+ // Section des flux
40+ ui.collapsing("📻 Mes Flux RSS", |ui| {
41+ if state.has_feeds() {
42+ draw_feed_list(state, ui);
43+ } else {
44+ draw_empty_state(ui);
45+ }
46+ });
3147
3248 ui.separator();
3349
34- // Playlist list
35- crate::ui::playlist_list::draw_playlist_list(state, ui);
50+ // Section des playlists
51+ ui.collapsing("📋 Playlists", |ui| {
52+ crate::ui::playlist_list::draw_playlist_list(state, ui);
53+ });
3654
37- // Draw add feed dialog
55+ // Dialogues
3856 crate::ui::add_feed_dialog::draw_add_feed_dialog(state, ui);
39-
40- // Draw edit feed dialog
4157 crate::ui::feed_edit_dialog::draw_edit_feed_dialog(state, ui);
42-
43- // Draw add playlist dialog
4458 crate::ui::playlist_list::draw_add_playlist_dialog(state, ui);
45-
46- // Draw edit playlist dialog
4759 crate::ui::playlist_list::draw_edit_playlist_dialog(state, ui);
48-
49- // Draw export dialog
5060 crate::ui::export_import::draw_export_dialog(state, ui);
51-
52- // Draw import dialog
5361 crate::ui::export_import::draw_import_dialog(state, ui);
5462 }
5563
5664 /// Draws the action buttons
5765 fn draw_action_buttons(state: &mut AppState, ui: &mut Ui) {
5866 ui.horizontal(|ui| {
59- if ui.button("📥 Ajouter un flux").clicked() {
67+ // Boutons principaux avec icônes et style amélioré
68+ if ui.button(egui::RichText::new("➕ Ajouter un flux").size(14.0)).clicked() {
6069 state.clear_new_feed_form();
6170 state.show_add_feed_dialog = true;
6271 }
6372
64- if ui.button("🔄 Synchroniser tous").clicked() {
73+ if ui.button(egui::RichText::new("🔄 Synchroniser tous").size(14.0)).clicked() {
6574 sync_all_feeds(state);
6675 }
6776
6877 ui.add_space(10.0);
6978
70- if ui.button("📤 Exporter").clicked() {
79+ // Boutons secondaires
80+ if ui.button(egui::RichText::new("📤 Exporter").size(14.0)).clicked() {
7181 state.clear_export_import_error();
7282 state.show_export_dialog = true;
7383 }
7484
75- if ui.button("📥 Importer").clicked() {
85+ if ui.button(egui::RichText::new("📥 Importer").size(14.0)).clicked() {
7686 state.clear_export_import_error();
7787 state.show_import_dialog = true;
7888 }
@@ -81,9 +91,16 @@ fn draw_action_buttons(state: &mut AppState, ui: &mut Ui) {
8191
8292 /// Draws the empty state message
8393 fn draw_empty_state(ui: &mut Ui) {
84- ui.centered_and_justified(|ui| {
85- ui.label(RichText::new("Aucun flux configuré").size(18.0));
86- ui.label("Cliquez sur 'Ajouter un flux' pour commencer");
94+ ui.vertical_centered_justified(|ui| {
95+ ui.add_space(40.0);
96+ ui.label(egui::RichText::new("🎧").size(48.0));
97+ ui.add_space(16.0);
98+ ui.label(egui::RichText::new("Bienvenue dans RSS Podcast Downloader").size(20.0).color(egui::Color32::from_rgb(220, 230, 245)));
99+ ui.add_space(8.0);
100+ ui.label(egui::RichText::new("Aucun flux configuré pour le moment").size(16.0).color(egui::Color32::from_rgb(180, 190, 210)));
101+ ui.add_space(16.0);
102+ ui.label(egui::RichText::new("👉 Cliquez sur '➕ Ajouter un flux' pour commencer").size(14.0).color(egui::Color32::from_rgb(150, 190, 255)));
103+ ui.add_space(40.0);
87104 });
88105 }
89106
@@ -3,76 +3,86 @@
3 //! This module contains the main home screen layout.3 //! This module contains the main home screen layout.
4 4
5 use crate::config::AppState;5 use crate::config::AppState;
6-use egui::{RichText, Ui};6+use egui::Ui;
7 7
8 /// Draws the home screen8 /// Draws the home screen
9 pub fn draw_home(state: &mut AppState, ui: &mut Ui) {9 pub fn draw_home(state: &mut AppState, ui: &mut Ui) {
10- // Top bar with title10+ // Header avec titre et icône
11 ui.horizontal(|ui| {11 ui.horizontal(|ui| {
12- ui.heading("RSS Podcast Downloader");12+ ui.heading(egui::RichText::new("🎙️ RSS Podcast Downloader").color(egui::Color32::from_rgb(130, 170, 250)));
13+ ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
14+ ui.label(egui::RichText::new(format!("v{}", env!("CARGO_PKG_VERSION"))).color(egui::Color32::from_rgb(150, 190, 255)));
15+ });
13 });16 });
14 17
15 ui.separator();18 ui.separator();
16 19
17- // Filter panel20+ // Barre d'outils avec filtres et actions
18- crate::ui::filter_panel::draw_filter_panel(state, ui);21+ ui.horizontal(|ui| {
22+ ui.vertical(|ui| {
23+ // Filtres
24+ crate::ui::filter_panel::draw_filter_panel(state, ui);
25+ });
26+ });
27+
28+ ui.separator();
19 29
20- // Action buttons30+ // Boutons d'action principaux (améliorés)
21- draw_action_buttons(state, ui);31+ ui.horizontal(|ui| {
32+ ui.spacing_mut().item_spacing = egui::Vec2::new(16.0, 0.0);
33+ draw_action_buttons(state, ui);
34+ });
22 35
36+ ui.add_space(16.0);
23 ui.separator();37 ui.separator();
24 38
25- // Feed list or empty message39+ // Section des flux
26- if state.has_feeds() {40+ ui.collapsing("📻 Mes Flux RSS", |ui| {
27- draw_feed_list(state, ui);41+ if state.has_feeds() {
28- } else {42+ draw_feed_list(state, ui);
29- draw_empty_state(ui);43+ } else {
30- }44+ draw_empty_state(ui);
45+ }
46+ });
31 47
32 ui.separator();48 ui.separator();
33 49
34- // Playlist list50+ // Section des playlists
35- crate::ui::playlist_list::draw_playlist_list(state, ui);51+ ui.collapsing("📋 Playlists", |ui| {
52+ crate::ui::playlist_list::draw_playlist_list(state, ui);
53+ });
36 54
37- // Draw add feed dialog55+ // Dialogues
38 crate::ui::add_feed_dialog::draw_add_feed_dialog(state, ui);56 crate::ui::add_feed_dialog::draw_add_feed_dialog(state, ui);
39-
40- // Draw edit feed dialog
41 crate::ui::feed_edit_dialog::draw_edit_feed_dialog(state, ui);57 crate::ui::feed_edit_dialog::draw_edit_feed_dialog(state, ui);
42-
43- // Draw add playlist dialog
44 crate::ui::playlist_list::draw_add_playlist_dialog(state, ui);58 crate::ui::playlist_list::draw_add_playlist_dialog(state, ui);
45-
46- // Draw edit playlist dialog
47 crate::ui::playlist_list::draw_edit_playlist_dialog(state, ui);59 crate::ui::playlist_list::draw_edit_playlist_dialog(state, ui);
48-
49- // Draw export dialog
50 crate::ui::export_import::draw_export_dialog(state, ui);60 crate::ui::export_import::draw_export_dialog(state, ui);
51-
52- // Draw import dialog
53 crate::ui::export_import::draw_import_dialog(state, ui);61 crate::ui::export_import::draw_import_dialog(state, ui);
54 }62 }
55 63
56 /// Draws the action buttons64 /// Draws the action buttons
57 fn draw_action_buttons(state: &mut AppState, ui: &mut Ui) {65 fn draw_action_buttons(state: &mut AppState, ui: &mut Ui) {
58 ui.horizontal(|ui| {66 ui.horizontal(|ui| {
59- if ui.button("📥 Ajouter un flux").clicked() {67+ // Boutons principaux avec icônes et style amélioré
68+ if ui.button(egui::RichText::new("➕ Ajouter un flux").size(14.0)).clicked() {
60 state.clear_new_feed_form();69 state.clear_new_feed_form();
61 state.show_add_feed_dialog = true;70 state.show_add_feed_dialog = true;
62 }71 }
63 72
64- if ui.button("🔄 Synchroniser tous").clicked() {73+ if ui.button(egui::RichText::new("🔄 Synchroniser tous").size(14.0)).clicked() {
65 sync_all_feeds(state);74 sync_all_feeds(state);
66 }75 }
67 76
68 ui.add_space(10.0);77 ui.add_space(10.0);
69 78
70- if ui.button("📤 Exporter").clicked() {79+ // Boutons secondaires
80+ if ui.button(egui::RichText::new("📤 Exporter").size(14.0)).clicked() {
71 state.clear_export_import_error();81 state.clear_export_import_error();
72 state.show_export_dialog = true;82 state.show_export_dialog = true;
73 }83 }
74 84
75- if ui.button("📥 Importer").clicked() {85+ if ui.button(egui::RichText::new("📥 Importer").size(14.0)).clicked() {
76 state.clear_export_import_error();86 state.clear_export_import_error();
77 state.show_import_dialog = true;87 state.show_import_dialog = true;
78 }88 }
@@ -81,9 +91,16 @@ fn draw_action_buttons(state: &mut AppState, ui: &mut Ui) {
81 91
82 /// Draws the empty state message92 /// Draws the empty state message
83 fn draw_empty_state(ui: &mut Ui) {93 fn draw_empty_state(ui: &mut Ui) {
84- ui.centered_and_justified(|ui| {94+ ui.vertical_centered_justified(|ui| {
85- ui.label(RichText::new("Aucun flux configuré").size(18.0));95+ ui.add_space(40.0);
86- ui.label("Cliquez sur 'Ajouter un flux' pour commencer");96+ ui.label(egui::RichText::new("🎧").size(48.0));
97+ ui.add_space(16.0);
98+ ui.label(egui::RichText::new("Bienvenue dans RSS Podcast Downloader").size(20.0).color(egui::Color32::from_rgb(220, 230, 245)));
99+ ui.add_space(8.0);
100+ ui.label(egui::RichText::new("Aucun flux configuré pour le moment").size(16.0).color(egui::Color32::from_rgb(180, 190, 210)));
101+ ui.add_space(16.0);
102+ ui.label(egui::RichText::new("👉 Cliquez sur '➕ Ajouter un flux' pour commencer").size(14.0).color(egui::Color32::from_rgb(150, 190, 255)));
103+ ui.add_space(40.0);
87 });104 });
88 }105 }
89 106
modified src/ui/status_bar.rs +55 -26
@@ -7,37 +7,66 @@ use egui::{Align, Layout, Ui};
77
88 /// Draws the status bar at the bottom of the application
99 pub fn draw_status_bar(state: &mut AppState, ui: &mut Ui) {
10- ui.horizontal(|ui| {
11- ui.spacing_mut().item_spacing.x = 0.0;
10+ // Frame pour la barre de statut
11+ 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)))
14+ .rounding(egui::Rounding::same(4.0))
15+ .inner_margin(egui::Margin::symmetric(12.0, 8.0))
16+ .show(ui, |ui| {
17+ ui.spacing_mut().item_spacing.x = 16.0;
1218
13- // Error message (highest priority)
14- if let Some(ref error) = state.error_message {
15- ui.colored_label(egui::Color32::RED, error);
16- if ui.button("✕").clicked() {
17- state.clear_error();
19+ // Error message (highest priority) avec icône
20+ if let Some(ref error) = state.error_message {
21+ let error_clone = error.clone();
22+ ui.horizontal(|ui| {
23+ ui.label(egui::RichText::new("❌ ").color(egui::Color32::RED));
24+ ui.colored_label(egui::Color32::from_rgb(240, 100, 100), &error_clone);
25+ if ui.button(egui::RichText::new("✕").size(14.0)).clicked() {
26+ state.clear_error();
27+ }
28+ });
29+ return;
1830 }
19- return;
20- }
2131
22- // Info message
23- if let Some(ref info) = state.info_message {
24- ui.colored_label(egui::Color32::GREEN, info);
25- if ui.button("✕").clicked() {
26- state.clear_info();
32+ // Info message avec icône
33+ if let Some(ref info) = state.info_message {
34+ let info_clone = info.clone();
35+ ui.horizontal(|ui| {
36+ ui.label(egui::RichText::new("ℹ️ ").color(egui::Color32::from_rgb(100, 200, 100)));
37+ ui.colored_label(egui::Color32::from_rgb(100, 220, 100), &info_clone);
38+ if ui.button(egui::RichText::new("✕").size(14.0)).clicked() {
39+ state.clear_info();
40+ }
41+ });
42+ return;
2743 }
28- return;
29- }
3044
31- // Sync status
32- if state.sync_in_progress {
33- ui.colored_label(egui::Color32::YELLOW, "Synchronisation en cours...");
34- } else {
35- ui.label("Prêt");
36- }
45+ // Sync status avec icône
46+ let sync_in_progress = state.sync_in_progress;
47+ let feed_count = state.feed_count();
48+ let enabled_count = state.enabled_feed_count();
49+
50+ ui.horizontal(|ui| {
51+ if sync_in_progress {
52+ ui.label(egui::RichText::new("🔄 ").color(egui::Color32::from_rgb(250, 200, 100)));
53+ ui.colored_label(egui::Color32::from_rgb(250, 200, 100), "Synchronisation en cours...");
54+ } else {
55+ ui.label(egui::RichText::new("✅ ").color(egui::Color32::from_rgb(100, 220, 100)));
56+ ui.colored_label(egui::Color32::from_rgb(150, 200, 150), "Prêt");
57+ }
3758
38- // Feed count on the right
39- ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
40- ui.label(format!("{} flux configurés", state.feed_count()));
59+ // Feed count on the right avec icône
60+ ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
61+ ui.label(egui::RichText::new(format!("📊 {} flux ", feed_count))
62+ .color(egui::Color32::from_rgb(160, 180, 200)));
63+
64+ // Nombre de flux activés
65+ if enabled_count < feed_count {
66+ ui.label(egui::RichText::new(format!("| {} activés", enabled_count))
67+ .color(egui::Color32::from_rgb(140, 160, 180)));
68+ }
69+ });
70+ });
4171 });
42- });
4372 }
@@ -7,37 +7,66 @@ use egui::{Align, Layout, Ui};
7 7
8 /// Draws the status bar at the bottom of the application8 /// Draws the status bar at the bottom of the application
9 pub fn draw_status_bar(state: &mut AppState, ui: &mut Ui) {9 pub fn draw_status_bar(state: &mut AppState, ui: &mut Ui) {
10- ui.horizontal(|ui| {10+ // Frame pour la barre de statut
11- ui.spacing_mut().item_spacing.x = 0.0;11+ 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)))
14+ .rounding(egui::Rounding::same(4.0))
15+ .inner_margin(egui::Margin::symmetric(12.0, 8.0))
16+ .show(ui, |ui| {
17+ ui.spacing_mut().item_spacing.x = 16.0;
12 18
13- // Error message (highest priority)19+ // Error message (highest priority) avec icône
14- if let Some(ref error) = state.error_message {20+ if let Some(ref error) = state.error_message {
15- ui.colored_label(egui::Color32::RED, error);21+ let error_clone = error.clone();
16- if ui.button("✕").clicked() {22+ ui.horizontal(|ui| {
17- state.clear_error();23+ ui.label(egui::RichText::new("❌ ").color(egui::Color32::RED));
24+ ui.colored_label(egui::Color32::from_rgb(240, 100, 100), &error_clone);
25+ if ui.button(egui::RichText::new("✕").size(14.0)).clicked() {
26+ state.clear_error();
27+ }
28+ });
29+ return;
18 }30 }
19- return;
20- }
21 31
22- // Info message32+ // Info message avec icône
23- if let Some(ref info) = state.info_message {33+ if let Some(ref info) = state.info_message {
24- ui.colored_label(egui::Color32::GREEN, info);34+ let info_clone = info.clone();
25- if ui.button("✕").clicked() {35+ ui.horizontal(|ui| {
26- state.clear_info();36+ ui.label(egui::RichText::new("ℹ️ ").color(egui::Color32::from_rgb(100, 200, 100)));
37+ ui.colored_label(egui::Color32::from_rgb(100, 220, 100), &info_clone);
38+ if ui.button(egui::RichText::new("✕").size(14.0)).clicked() {
39+ state.clear_info();
40+ }
41+ });
42+ return;
27 }43 }
28- return;
29- }
30 44
31- // Sync status45+ // Sync status avec icône
32- if state.sync_in_progress {46+ let sync_in_progress = state.sync_in_progress;
33- ui.colored_label(egui::Color32::YELLOW, "Synchronisation en cours...");47+ let feed_count = state.feed_count();
34- } else {48+ let enabled_count = state.enabled_feed_count();
35- ui.label("Prêt");49+
36- }50+ ui.horizontal(|ui| {
51+ if sync_in_progress {
52+ ui.label(egui::RichText::new("🔄 ").color(egui::Color32::from_rgb(250, 200, 100)));
53+ ui.colored_label(egui::Color32::from_rgb(250, 200, 100), "Synchronisation en cours...");
54+ } else {
55+ ui.label(egui::RichText::new("✅ ").color(egui::Color32::from_rgb(100, 220, 100)));
56+ ui.colored_label(egui::Color32::from_rgb(150, 200, 150), "Prêt");
57+ }
37 58
38- // Feed count on the right59+ // Feed count on the right avec icône
39- ui.with_layout(Layout::right_to_left(Align::Center), |ui| {60+ ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
40- ui.label(format!("{} flux configurés", state.feed_count()));61+ ui.label(egui::RichText::new(format!("📊 {} flux ", feed_count))
62+ .color(egui::Color32::from_rgb(160, 180, 200)));
63+
64+ // Nombre de flux activés
65+ if enabled_count < feed_count {
66+ ui.label(egui::RichText::new(format!("| {} activés", enabled_count))
67+ .color(egui::Color32::from_rgb(140, 160, 180)));
68+ }
69+ });
70+ });
41 });71 });
42- });
43 }72 }