use super::*; use eframe::egui; use egui::{ Align2, CentralPanel, Color32, ProgressBar, ScrollArea, SidePanel, Spinner, Window, }; use std::path::Path; use std::time::Duration; impl eframe::App for RcloneUI { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { self.poll_rclone_init(); self.poll_background_operation(); egui::TopBottomPanel::top("top").show(ctx, |ui| { egui::menu::bar(ui, |ui| { ui.label("🚀 Rclone GUI"); ui.separator(); ui.add_enabled_ui(!self.history_back.is_empty(), |ui| { if ui.button("⬅").clicked() { log::debug!("Кнопка 'Назад' нажата"); self.go_back(); } }); ui.add_enabled_ui(!self.history_forward.is_empty(), |ui| { if ui.button("➡").clicked() { log::debug!("Кнопка 'Вперед' нажата"); self.go_forward(); } }); if self.active_task_count > 0 { ui.add(Spinner::new().size(16.0)); } }); }); SidePanel::left("left") .default_width(180.0) .show(ctx, |ui| { ui.add_space(10.0); ui.heading("Хранилища"); ui.separator(); ScrollArea::vertical().show(ui, |ui| { for remote in self.remote_list.clone() { if ui .selectable_label( self.current_path.starts_with(&remote.name), format!("📡 {}", remote.name), ) .clicked() { log::debug!("Выбрано удаленное хранилище: {}", remote.name); self.navigate_to(format!("{}:", remote.name)); } } }); }); SidePanel::right("right") .default_width(200.0) .show(ctx, |ui| { ui.heading("Задачи"); ui.separator(); ScrollArea::vertical().show(ui, |ui| { for op in &self.active_operations { ui.group(|ui| { ui.label(&op.description); ui.add( ProgressBar::new(op.progress) .text(format!("{:.0}%", op.progress * 100.0)), ); ui.small(&op.status); }); } if self.active_operations.is_empty() { ui.weak("Нет активных задач"); } }); }); CentralPanel::default().show(ctx, |ui| { ui.horizontal(|ui| { ui.label(format!("📍 {}", self.current_path)); ui.add_enabled_ui(self.active_task_count == 0, |ui| { if ui.button("🔄").clicked() { log::debug!("Обновление текущего пути: {}", self.current_path); self.pending_load_path = Some(self.current_path.clone()); } }); }); ui.separator(); ScrollArea::vertical().id_source("files").show(ui, |ui| { if !self.current_path.is_empty() { if self.current_path.contains('/') && ui.button("📁 .. (Вверх)").clicked() { log::debug!("Переход вверх по директории"); if let Some((p, _)) = self.current_path.rsplit_once('/') { self.navigate_to(p.into()); } else if let Some((p, _)) = self.current_path.rsplit_once(':') { self.navigate_to(format!("{}:", p)); } } for file in self.current_files.clone() { let full = if self.current_path.ends_with(':') { format!("{}{}", self.current_path, file.name) } else { format!("{}/{}", self.current_path, file.name) }; let sel = self.selected_paths.contains(&full); ui.horizontal(|ui| { if ui.selectable_label(sel, file.icon()).clicked() { if sel { log::debug!("Снят выбор с {}", full); self.selected_paths.retain(|p| p != &full); } else { log::debug!("Выбран {}", full); self.selected_paths.push(full.clone()); } } if file.is_dir { if ui.link(&file.name).clicked() { log::debug!("Открыта директория: {}", full); self.navigate_to(full); } } else { ui.label(&file.name); } ui.with_layout( egui::Layout::right_to_left(egui::Align::Center), |ui| { ui.small(file.format_size()); }, ); }); } } else { ui.centered_and_justified(|ui| { ui.label("Выберите хранилище слева"); }); } }); if !self.selected_paths.is_empty() { ui.separator(); ui.horizontal(|ui| { ui.label(format!("Выбрано: {}", self.selected_paths.len())); ui.add_enabled_ui(self.active_task_count == 0, |ui| { if ui.button("📋 Копировать").clicked() { log::info!("Начало операции копирования выбранных элементов"); self.transfer_source_info = self.get_selected_info(); self.is_move_mode = false; self.show_transfer_dialog = true; } if ui.button("✂ Переместить").clicked() { log::info!("Начало операции перемещения выбранных элементов"); self.transfer_source_info = self.get_selected_info(); self.is_move_mode = true; self.show_transfer_dialog = true; } if ui.button("🗑 Удалить").clicked() { log::info!("Начало операции удаления выбранных элементов"); self.delete_selected(); self.selected_paths.clear(); } }); }); } }); // Модальное окно трансфера if self.show_transfer_dialog { Window::new("Трансфер") .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) .collapsible(false) .show(ctx, |ui| { ui.horizontal(|ui| { ui.selectable_value( &mut self.active_transfer_tab, TransferTab::Remote, "☁ Облако", ); ui.selectable_value( &mut self.active_transfer_tab, TransferTab::Local, "💻 ПК", ); }); ui.separator(); ui.vertical(|ui| { ui.horizontal(|ui| { ui.add( egui::TextEdit::singleline(&mut self.transfer_dest) .hint_text("Путь назначения..."), ); if self.active_transfer_tab == TransferTab::Local && ui.button("📂 Обзор").clicked() { log::debug!("Открыт локальный браузер файлов"); self.refresh_local_list(); self.show_local_browser = true; } }); if !self.transfer_dest.is_empty() && !self.is_path_valid() { ui.colored_label( Color32::KHAKI, if self.active_transfer_tab == TransferTab::Remote { "⚠ Путь облака должен содержать ':'" } else { "⚠ Укажите локальный путь без ':'" }, ); } }); ui.add_space(10.0); ui.horizontal(|ui| { let can_start = self.is_path_valid() && self.active_task_count == 0; if ui .add_enabled(can_start, egui::Button::new("🚀 Начать")) .clicked() { log::info!("Запуск трансфера в {}", self.transfer_dest); self.perform_transfer(self.is_move_mode); self.show_transfer_dialog = false; } if ui.button("Отмена").clicked() { log::debug!("Отмена трансфера"); self.show_transfer_dialog = false; } }); }); } // Внутренний браузер папок if self.show_local_browser { Window::new("Выбор локальной папки") .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) .fixed_size([400.0, 300.0]) .show(ctx, |ui| { ui.label(format!("📍 {}", self.local_browser_path)); ui.horizontal(|ui| { if ui.button("⬅ Наверх").clicked() { if let Some(parent) = Path::new(&self.local_browser_path).parent() { self.local_browser_path = parent.to_string_lossy().into(); self.refresh_local_list(); } } if ui.button("🔄").clicked() { self.refresh_local_list(); } }); ui.separator(); // Проверяем, пуста ли папка let is_empty = self.local_browser_files.is_empty(); // Создаем рамку для контента egui::Frame::group(ui.style()) .inner_margin(egui::Margin::same(8.0)) .show(ui, |ui| { if is_empty { // Если папка пуста, показываем сообщение по центру с иконкой let available_height = ui.available_height(); ui.centered_and_justified(|ui| { ui.add_space(available_height / 3.0); ui.vertical(|ui| { ui.label("📂"); ui.add_space(8.0); ui.colored_label( egui::Color32::GRAY, "Эта папка пуста" ); }); }); } else { // Если есть файлы, показываем список ScrollArea::vertical() .max_height(200.0) .auto_shrink([false; 2]) .show(ui, |ui| { let files = self.local_browser_files.clone(); for file in files { if file.is_dir { ui.horizontal(|ui| { let response = ui.button("📁"); ui.add_space(4.0); let link_response = ui.link(&file.name); if response.clicked() || link_response.clicked() { self.local_browser_path = file.path; self.refresh_local_list(); } }); } else { ui.horizontal(|ui| { ui.label("📄"); ui.add_space(4.0); ui.label(&file.name); ui.with_layout( egui::Layout::right_to_left(egui::Align::Center), |ui| { ui.small(file.format_size()); }, ); }); } } }); } }); ui.add_space(8.0); ui.separator(); ui.horizontal(|ui| { if ui.button("✅ Выбрать").clicked() { log::debug!("Выбрана локальная папка: {}", self.local_browser_path); self.transfer_dest = self.local_browser_path.clone(); self.show_local_browser = false; } if ui.button("Отмена").clicked() { self.show_local_browser = false; } }); }); } if let Some(msg) = &self.error_message { let mut close = false; Window::new("Внимание") .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) .show(ctx, |ui| { ui.label(msg); if ui.button("ОК").clicked() { close = true; } }); if close { log::debug!("Ошибка закрыта: {}", msg); self.error_message = None; } } if let Some(path) = self.pending_load_path.take() { self.current_path = path.clone(); self.selected_paths.clear(); self.load_files(&path); } ctx.request_repaint_after(Duration::from_millis(100)); } }