fix(Ui): fix err with deleting copy and replace more than 1 file
This commit is contained in:
1 parent
43a5995036
commit
04a0dc21b9
1 file changed
+191
-189
@@ -1,14 +1,12 @@
|
||||
//! Графический интерфейс для операций rclone.
|
||||
|
||||
use crate::operations::{
|
||||
self, CopyOptions, DeleteOptions, FileInfo, FindOptions, MoveOptions, Remote,
|
||||
};
|
||||
use crate::rclone_install::RcloneApp;
|
||||
use eframe::egui;
|
||||
use egui::{Align2, CentralPanel, Color32, ScrollArea, Spinner, Window};
|
||||
use egui::{CentralPanel, ScrollArea, Window, Align2, Color32, Spinner};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use tokio::runtime::Runtime;
|
||||
use crate::rclone_install::RcloneApp;
|
||||
use crate::operations::{self, FileInfo, Remote, CopyOptions, MoveOptions, DeleteOptions, FindOptions};
|
||||
|
||||
/// Состояние приложения.
|
||||
#[derive(Clone)]
|
||||
@@ -50,7 +48,7 @@ pub struct RcloneUI {
|
||||
current_files: Vec<FileInfo>,
|
||||
selected_paths: Vec<String>,
|
||||
|
||||
transfer_source: String,
|
||||
transfer_source_list: Vec<String>, // список выбранных источников для копирования/перемещения
|
||||
transfer_dest: String,
|
||||
transfer_progress: Option<TransferProgress>,
|
||||
|
||||
@@ -108,7 +106,7 @@ impl RcloneUI {
|
||||
remote_list: Vec::new(),
|
||||
current_files: Vec::new(),
|
||||
selected_paths: Vec::new(),
|
||||
transfer_source: String::new(),
|
||||
transfer_source_list: Vec::new(),
|
||||
transfer_dest: String::new(),
|
||||
transfer_progress: None,
|
||||
search_pattern: String::new(),
|
||||
@@ -155,12 +153,8 @@ impl RcloneUI {
|
||||
Ok(types) => self.available_remote_types = types,
|
||||
Err(_) => {
|
||||
self.available_remote_types = vec![
|
||||
"drive".to_string(),
|
||||
"s3".to_string(),
|
||||
"dropbox".to_string(),
|
||||
"local".to_string(),
|
||||
"onedrive".to_string(),
|
||||
"webdav".to_string(),
|
||||
"drive".to_string(), "s3".to_string(), "dropbox".to_string(),
|
||||
"local".to_string(), "onedrive".to_string(), "webdav".to_string(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -259,7 +253,8 @@ impl RcloneUI {
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_move(&mut self, source: &str, dest: &str) {
|
||||
/// Массовое перемещение выбранных файлов/папок
|
||||
fn perform_move_multiple(&mut self, sources: Vec<String>, dest: &str) {
|
||||
if let Some(rclone) = self.rclone.clone() {
|
||||
self.state = AppState::Moving;
|
||||
let options = MoveOptions {
|
||||
@@ -267,25 +262,36 @@ impl RcloneUI {
|
||||
dry_run: false,
|
||||
delete_empty_src_dirs: true,
|
||||
};
|
||||
let source = source.to_string();
|
||||
let dest = dest.to_string();
|
||||
self.start_background_operation(move || {
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
operations::sync::move_files(&rclone, &source, &dest, &options)
|
||||
})
|
||||
.map(|stats| {
|
||||
format!(
|
||||
"Перемещено {} файлов, {} байт",
|
||||
stats.files, stats.transferred
|
||||
)
|
||||
})
|
||||
.map_err(|e| format!("Ошибка перемещения: {}", e))
|
||||
let _rt = Runtime::new().unwrap();
|
||||
let mut errors = Vec::new();
|
||||
let mut moved_count = 0;
|
||||
let mut total_bytes = 0;
|
||||
|
||||
for source in sources {
|
||||
match operations::sync::move_files(&rclone, &source, &dest, &options) {
|
||||
Ok(stats) => {
|
||||
moved_count += 1;
|
||||
total_bytes += stats.transferred;
|
||||
}
|
||||
Err(e) => {
|
||||
errors.push(format!("{}: {}", source, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(format!("Перемещено {} элементов, {} байт", moved_count, total_bytes))
|
||||
} else {
|
||||
Err(format!("Перемещено {}, ошибки:\n{}", moved_count, errors.join("\n")))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_copy(&mut self, source: &str, dest: &str) {
|
||||
/// Массовое копирование выбранных файлов/папок
|
||||
fn perform_copy_multiple(&mut self, sources: Vec<String>, dest: &str) {
|
||||
if let Some(rclone) = self.rclone.clone() {
|
||||
self.state = AppState::Copying;
|
||||
let options = CopyOptions {
|
||||
@@ -294,13 +300,73 @@ impl RcloneUI {
|
||||
bandwidth_limit: self.settings.bandwidth_limit.clone(),
|
||||
no_traverse: false,
|
||||
};
|
||||
let source = source.to_string();
|
||||
let dest = dest.to_string();
|
||||
self.start_background_operation(move || {
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async { operations::sync::copy(&rclone, &source, &dest, &options) })
|
||||
.map(|stats| format!("Скопировано {} файлов", stats.files))
|
||||
.map_err(|e| format!("Ошибка копирования: {}", e))
|
||||
let _rt = Runtime::new().unwrap();
|
||||
let mut errors = Vec::new();
|
||||
let mut copied_count = 0;
|
||||
|
||||
for source in sources {
|
||||
match operations::sync::copy(&rclone, &source, &dest, &options) {
|
||||
Ok(_stats) => {
|
||||
copied_count += 1;
|
||||
}
|
||||
Err(e) => {
|
||||
errors.push(format!("{}: {}", source, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(format!("Скопировано {} элементов", copied_count))
|
||||
} else {
|
||||
Err(format!("Скопировано {}, ошибки:\n{}", copied_count, errors.join("\n")))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Удаление нескольких выбранных путей
|
||||
fn delete_selected_paths(&mut self, paths: Vec<String>) {
|
||||
if let Some(rclone) = self.rclone.clone() {
|
||||
// Создаём карту: путь -> является ли директорией
|
||||
let mut is_dir_map = std::collections::HashMap::new();
|
||||
for file in &self.current_files {
|
||||
let full_path = if self.current_path.ends_with(':') {
|
||||
format!("{}{}", self.current_path, file.name)
|
||||
} else {
|
||||
format!("{}/{}", self.current_path, file.name)
|
||||
};
|
||||
is_dir_map.insert(full_path, file.is_dir);
|
||||
}
|
||||
|
||||
self.start_background_operation(move || {
|
||||
let mut errors = Vec::new();
|
||||
let mut deleted_count = 0;
|
||||
|
||||
for path in paths {
|
||||
let is_dir = is_dir_map.get(&path).cloned().unwrap_or(false);
|
||||
let options = DeleteOptions {
|
||||
recursive: is_dir,
|
||||
verbose: false,
|
||||
dry_run: false,
|
||||
};
|
||||
match operations::sync::delete(&rclone, &path, &options) {
|
||||
Ok(stats) => {
|
||||
deleted_count += 1;
|
||||
println!("Удалено: {} ({} элементов)", path, stats.files);
|
||||
}
|
||||
Err(e) => {
|
||||
errors.push(format!("{}: {}", path, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(format!("Успешно удалено {} элементов", deleted_count))
|
||||
} else {
|
||||
Err(format!("Удалено {} элементов, но возникли ошибки:\n{}", deleted_count, errors.join("\n")))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -337,46 +403,27 @@ impl RcloneUI {
|
||||
}
|
||||
|
||||
fn validate_remote_name(name: &str) -> bool {
|
||||
if name.is_empty() || name.starts_with(' ') || name.starts_with('-') || name.ends_with(' ')
|
||||
{
|
||||
if name.is_empty() || name.starts_with(' ') || name.starts_with('-') || name.ends_with(' ') {
|
||||
return false;
|
||||
}
|
||||
name.chars().all(|ch| {
|
||||
ch.is_ascii_alphanumeric()
|
||||
|| ch == '_'
|
||||
|| ch == '-'
|
||||
|| ch == '.'
|
||||
|| ch == '+'
|
||||
|| ch == '@'
|
||||
|| ch == ' '
|
||||
})
|
||||
name.chars().all(|ch| ch.is_ascii_alphanumeric() || ch == '_' || ch == '-' || ch == '.' || ch == '+' || ch == '@' || ch == ' ')
|
||||
}
|
||||
|
||||
fn format_size(bytes: u64) -> String {
|
||||
const KB: u64 = 1024;
|
||||
const MB: u64 = KB * 1024;
|
||||
const GB: u64 = MB * 1024;
|
||||
if bytes >= GB {
|
||||
format!("{:.2} ГБ", bytes as f64 / GB as f64)
|
||||
} else if bytes >= MB {
|
||||
format!("{:.2} МБ", bytes as f64 / MB as f64)
|
||||
} else if bytes >= KB {
|
||||
format!("{:.2} КБ", bytes as f64 / KB as f64)
|
||||
} else {
|
||||
format!("{} Б", bytes)
|
||||
}
|
||||
if bytes >= GB { format!("{:.2} ГБ", bytes as f64 / GB as f64) }
|
||||
else if bytes >= MB { format!("{:.2} МБ", bytes as f64 / MB as f64) }
|
||||
else if bytes >= KB { format!("{:.2} КБ", bytes as f64 / KB as f64) }
|
||||
else { format!("{} Б", bytes) }
|
||||
}
|
||||
|
||||
fn format_speed(speed: f64) -> String {
|
||||
if speed < 1024.0 {
|
||||
format!("{:.0} Б/с", speed)
|
||||
} else if speed < 1024.0 * 1024.0 {
|
||||
format!("{:.1} КБ/с", speed / 1024.0)
|
||||
} else if speed < 1024.0 * 1024.0 * 1024.0 {
|
||||
format!("{:.1} МБ/с", speed / (1024.0 * 1024.0))
|
||||
} else {
|
||||
format!("{:.2} ГБ/с", speed / (1024.0 * 1024.0 * 1024.0))
|
||||
}
|
||||
if speed < 1024.0 { format!("{:.0} Б/с", speed) }
|
||||
else if speed < 1024.0 * 1024.0 { format!("{:.1} КБ/с", speed / 1024.0) }
|
||||
else if speed < 1024.0 * 1024.0 * 1024.0 { format!("{:.1} МБ/с", speed / (1024.0 * 1024.0)) }
|
||||
else { format!("{:.2} ГБ/с", speed / (1024.0 * 1024.0 * 1024.0)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,9 +439,7 @@ impl eframe::App for RcloneUI {
|
||||
egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
|
||||
egui::menu::bar(ui, |ui| {
|
||||
ui.menu_button("Файл", |ui| {
|
||||
if ui.button("Выход").clicked() {
|
||||
std::process::exit(0);
|
||||
}
|
||||
if ui.button("Выход").clicked() { std::process::exit(0); }
|
||||
});
|
||||
ui.menu_button("Хранилища", |ui| {
|
||||
if ui.button("Добавить новое хранилище").clicked() {
|
||||
@@ -406,24 +451,13 @@ impl eframe::App for RcloneUI {
|
||||
});
|
||||
ui.menu_button("Настройки", |ui| {
|
||||
ui.checkbox(&mut self.settings.show_hidden, "Показывать скрытые файлы");
|
||||
ui.checkbox(
|
||||
&mut self.settings.confirm_before_transfer,
|
||||
"Подтверждать перед передачей",
|
||||
);
|
||||
ui.add(
|
||||
egui::Slider::new(&mut self.settings.max_concurrent_transfers, 1..=10)
|
||||
.text("Макс. одновременных передач"),
|
||||
);
|
||||
ui.checkbox(&mut self.settings.confirm_before_transfer, "Подтверждать перед передачей");
|
||||
ui.add(egui::Slider::new(&mut self.settings.max_concurrent_transfers, 1..=10).text("Макс. одновременных передач"));
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Ограничение скорости:");
|
||||
let mut limit_str =
|
||||
self.settings.bandwidth_limit.clone().unwrap_or_default();
|
||||
let mut limit_str = self.settings.bandwidth_limit.clone().unwrap_or_default();
|
||||
if ui.text_edit_singleline(&mut limit_str).changed() {
|
||||
self.settings.bandwidth_limit = if limit_str.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(limit_str)
|
||||
};
|
||||
self.settings.bandwidth_limit = if limit_str.is_empty() { None } else { Some(limit_str) };
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -436,18 +470,10 @@ impl eframe::App for RcloneUI {
|
||||
});
|
||||
} else {
|
||||
match &self.state {
|
||||
AppState::Ready => {
|
||||
ui.colored_label(Color32::GREEN, "● Готов");
|
||||
}
|
||||
AppState::Initializing => {
|
||||
ui.colored_label(Color32::YELLOW, "● Инициализация...");
|
||||
}
|
||||
AppState::Error(_) => {
|
||||
ui.colored_label(Color32::RED, "● Ошибка");
|
||||
}
|
||||
_ => {
|
||||
ui.colored_label(Color32::BLUE, "● Занят");
|
||||
}
|
||||
AppState::Ready => { ui.colored_label(Color32::GREEN, "● Готов"); }
|
||||
AppState::Initializing => { ui.colored_label(Color32::YELLOW, "● Инициализация..."); }
|
||||
AppState::Error(_) => { ui.colored_label(Color32::RED, "● Ошибка"); }
|
||||
_ => { ui.colored_label(Color32::BLUE, "● Занят"); }
|
||||
}
|
||||
};
|
||||
if let Some(rclone) = &self.rclone {
|
||||
@@ -476,8 +502,7 @@ impl eframe::App for RcloneUI {
|
||||
if ui.button(format!("📡 {}", remote.name)).clicked() {
|
||||
self.pending_load_path = Some(format!("{}:", remote.name));
|
||||
}
|
||||
if ui.button("🗑️").on_hover_text("Удалить хранилище").clicked()
|
||||
{
|
||||
if ui.button("🗑️").on_hover_text("Удалить хранилище").clicked() {
|
||||
self.remote_to_delete = Some(remote.name.clone());
|
||||
self.show_delete_remote_dialog = true;
|
||||
}
|
||||
@@ -495,11 +520,7 @@ impl eframe::App for RcloneUI {
|
||||
let mut new_search = search_pattern.clone();
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("📍 Путь:");
|
||||
ui.label(if current_path.is_empty() {
|
||||
"Выберите хранилище"
|
||||
} else {
|
||||
¤t_path
|
||||
});
|
||||
ui.label(if current_path.is_empty() { "Выберите хранилище" } else { ¤t_path });
|
||||
ui.separator();
|
||||
ui.label("🔍 Поиск:");
|
||||
if ui.text_edit_singleline(&mut new_search).changed() {
|
||||
@@ -523,7 +544,7 @@ impl eframe::App for RcloneUI {
|
||||
ui.add_space(10.0);
|
||||
ui.colored_label(
|
||||
egui::Color32::LIGHT_BLUE,
|
||||
"⟳ Выполняется операция... Пожалуйста, подождите.",
|
||||
"⟳ Выполняется операция... Пожалуйста, подождите."
|
||||
);
|
||||
});
|
||||
ctx.request_repaint_after(Duration::from_millis(16));
|
||||
@@ -561,9 +582,7 @@ impl eframe::App for RcloneUI {
|
||||
let current_files = self.current_files.clone();
|
||||
let current_path = self.current_path.clone();
|
||||
ScrollArea::vertical().show(ui, |ui| {
|
||||
if current_path.contains('/')
|
||||
&& ui.button("📁 .. (Родительская папка)").clicked()
|
||||
{
|
||||
if current_path.contains('/') && ui.button("📁 .. (Родительская папка)").clicked() {
|
||||
let parent = current_path.rsplit_once('/').map(|(p, _)| p).unwrap_or("");
|
||||
self.pending_load_path = Some(parent.to_string());
|
||||
}
|
||||
@@ -615,37 +634,17 @@ impl eframe::App for RcloneUI {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!("Выбрано: {}", self.selected_paths.len()));
|
||||
if ui.button("📋 Копировать").clicked() {
|
||||
self.transfer_source = self.selected_paths[0].clone();
|
||||
self.transfer_source_list = self.selected_paths.clone();
|
||||
self.show_transfer_dialog = true;
|
||||
}
|
||||
if ui.button("✂️ Переместить").clicked() {
|
||||
self.transfer_source = self.selected_paths[0].clone();
|
||||
self.transfer_source_list = self.selected_paths.clone();
|
||||
self.show_transfer_dialog = true;
|
||||
}
|
||||
if ui.button("🗑️ Удалить").clicked() {
|
||||
if let Some(rclone) = self.rclone.clone() {
|
||||
let path = self.selected_paths[0].clone();
|
||||
// Определяем, является ли выбранный путь директорией
|
||||
let is_dir = self.current_files.iter().any(|f| {
|
||||
let full_path = if self.current_path.ends_with(':') {
|
||||
format!("{}{}", self.current_path, f.name)
|
||||
} else {
|
||||
format!("{}/{}", self.current_path, f.name)
|
||||
};
|
||||
full_path == path && f.is_dir
|
||||
});
|
||||
let options = DeleteOptions {
|
||||
recursive: is_dir,
|
||||
verbose: true,
|
||||
dry_run: false,
|
||||
};
|
||||
self.start_background_operation(move || {
|
||||
operations::sync::delete(&rclone, &path, &options)
|
||||
.map(|_| format!("Удалено: {}", path))
|
||||
.map_err(|e| format!("Ошибка удаления: {}", e))
|
||||
});
|
||||
self.selected_paths.clear();
|
||||
}
|
||||
let paths_to_delete = self.selected_paths.clone();
|
||||
self.delete_selected_paths(paths_to_delete);
|
||||
self.selected_paths.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -659,36 +658,51 @@ impl eframe::App for RcloneUI {
|
||||
self.search_files(&pattern);
|
||||
}
|
||||
|
||||
// Диалог передачи файлов
|
||||
// Диалог передачи файлов (поддерживает несколько источников)
|
||||
if self.show_transfer_dialog {
|
||||
let source = self.transfer_source.clone();
|
||||
let source_count = self.transfer_source_list.len();
|
||||
let dest = self.transfer_dest.clone();
|
||||
Window::new("Передача файлов")
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.show(ctx, |ui| {
|
||||
ui.label(format!("Источник: {}", source));
|
||||
ui.label("Назначение:");
|
||||
ui.label(format!("Выбрано элементов для передачи: {}", source_count));
|
||||
if source_count <= 3 {
|
||||
for src in &self.transfer_source_list {
|
||||
ui.label(format!("📄 {}", src));
|
||||
}
|
||||
} else {
|
||||
ui.label(format!("и ещё {} элементов", source_count - 3));
|
||||
}
|
||||
ui.separator();
|
||||
ui.label("Назначение (директория):");
|
||||
let mut dest_edit = dest.clone();
|
||||
if ui.text_edit_singleline(&mut dest_edit).changed() {
|
||||
self.transfer_dest = dest_edit;
|
||||
}
|
||||
ui.separator();
|
||||
let current_dest = self.transfer_dest.clone();
|
||||
let sources = self.transfer_source_list.clone();
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("✅ Копировать").clicked() {
|
||||
self.perform_copy(&source, ¤t_dest);
|
||||
self.perform_copy_multiple(sources.clone(), ¤t_dest);
|
||||
self.show_transfer_dialog = false;
|
||||
self.selected_paths.clear();
|
||||
self.transfer_source_list.clear();
|
||||
self.transfer_dest.clear();
|
||||
}
|
||||
if ui.button("✂️ Переместить").clicked() {
|
||||
self.perform_move(&source, ¤t_dest);
|
||||
self.perform_move_multiple(sources.clone(), ¤t_dest);
|
||||
self.show_transfer_dialog = false;
|
||||
self.selected_paths.clear();
|
||||
self.transfer_source_list.clear();
|
||||
self.transfer_dest.clear();
|
||||
}
|
||||
if ui.button("❌ Отмена").clicked() {
|
||||
self.show_transfer_dialog = false;
|
||||
self.transfer_source_list.clear();
|
||||
self.transfer_dest.clear();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -737,74 +751,62 @@ impl eframe::App for RcloneUI {
|
||||
|
||||
// Диалог предупреждения о браузере
|
||||
if self.show_browser_warning {
|
||||
Window::new("Внимание")
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.show(ctx, |ui| {
|
||||
ui.colored_label(Color32::YELLOW, "🌐 Будет открыт браузер для авторизации");
|
||||
ui.label("После завершения авторизации в браузере вернитесь в приложение.");
|
||||
ui.label("Операция выполняется в фоне, интерфейс не зависнет.");
|
||||
ui.separator();
|
||||
if ui.button("Продолжить").clicked() {
|
||||
if let Some((name, typ)) = self.pending_remote_creation.take() {
|
||||
self.execute_remote_creation(&name, &typ);
|
||||
}
|
||||
self.show_browser_warning = false;
|
||||
Window::new("Внимание").collapsible(false).resizable(false).anchor(Align2::CENTER_CENTER, [0.0, 0.0]).show(ctx, |ui| {
|
||||
ui.colored_label(Color32::YELLOW, "🌐 Будет открыт браузер для авторизации");
|
||||
ui.label("После завершения авторизации в браузере вернитесь в приложение.");
|
||||
ui.label("Операция выполняется в фоне, интерфейс не зависнет.");
|
||||
ui.separator();
|
||||
if ui.button("Продолжить").clicked() {
|
||||
if let Some((name, typ)) = self.pending_remote_creation.take() {
|
||||
self.execute_remote_creation(&name, &typ);
|
||||
}
|
||||
if ui.button("Отмена").clicked() {
|
||||
self.pending_remote_creation = None;
|
||||
self.show_browser_warning = false;
|
||||
}
|
||||
});
|
||||
self.show_browser_warning = false;
|
||||
}
|
||||
if ui.button("Отмена").clicked() {
|
||||
self.pending_remote_creation = None;
|
||||
self.show_browser_warning = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Диалог подтверждения удаления хранилища
|
||||
if self.show_delete_remote_dialog {
|
||||
let remote_name = self.remote_to_delete.clone();
|
||||
Window::new("Удаление хранилища")
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.show(ctx, |ui| {
|
||||
if let Some(ref name) = remote_name {
|
||||
ui.label(format!("Удалить хранилище '{}'?", name));
|
||||
ui.label("Это действие не удаляет данные, только конфигурацию.");
|
||||
let name_clone = name.clone();
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("✅ Удалить").clicked() {
|
||||
self.delete_remote(&name_clone);
|
||||
self.show_delete_remote_dialog = false;
|
||||
self.remote_to_delete = None;
|
||||
}
|
||||
if ui.button("❌ Отмена").clicked() {
|
||||
self.show_delete_remote_dialog = false;
|
||||
self.remote_to_delete = None;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
self.show_delete_remote_dialog = false;
|
||||
}
|
||||
});
|
||||
Window::new("Удаление хранилища").collapsible(false).resizable(false).anchor(Align2::CENTER_CENTER, [0.0, 0.0]).show(ctx, |ui| {
|
||||
if let Some(ref name) = remote_name {
|
||||
ui.label(format!("Удалить хранилище '{}'?", name));
|
||||
ui.label("Это действие не удаляет данные, только конфигурацию.");
|
||||
let name_clone = name.clone();
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("✅ Удалить").clicked() {
|
||||
self.delete_remote(&name_clone);
|
||||
self.show_delete_remote_dialog = false;
|
||||
self.remote_to_delete = None;
|
||||
}
|
||||
if ui.button("❌ Отмена").clicked() {
|
||||
self.show_delete_remote_dialog = false;
|
||||
self.remote_to_delete = None;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
self.show_delete_remote_dialog = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Диалог ошибок
|
||||
if let Some(error) = &self.error_message {
|
||||
let error_clone = error.clone();
|
||||
Window::new("Ошибка")
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.show(ctx, |ui| {
|
||||
ui.colored_label(Color32::RED, "⚠️ Произошла ошибка:");
|
||||
ui.label(&error_clone);
|
||||
if ui.button("OK").clicked() {
|
||||
self.error_message = None;
|
||||
if let AppState::Error(_) = &self.state {
|
||||
self.state = AppState::Ready;
|
||||
}
|
||||
Window::new("Ошибка").collapsible(false).resizable(false).anchor(Align2::CENTER_CENTER, [0.0, 0.0]).show(ctx, |ui| {
|
||||
ui.colored_label(Color32::RED, "⚠️ Произошла ошибка:");
|
||||
ui.label(&error_clone);
|
||||
if ui.button("OK").clicked() {
|
||||
self.error_message = None;
|
||||
if let AppState::Error(_) = &self.state {
|
||||
self.state = AppState::Ready;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ctx.request_repaint();
|
||||
|
||||
Reference in new issue
Block a user