diff --git a/czkawka_core/src/similar_images.rs b/czkawka_core/src/similar_images.rs index dc07911..4400654 100644 --- a/czkawka_core/src/similar_images.rs +++ b/czkawka_core/src/similar_images.rs @@ -40,7 +40,8 @@ pub const SIMILAR_VALUES: [[u32; 6]; 4] = [ pub struct ImagesEntry { pub path: PathBuf, pub size: u64, - pub dimensions: String, + pub width: u32, + pub height: u32, pub modified_date: u64, pub hash: ImHash, pub similarity: u32, @@ -65,7 +66,8 @@ impl FileEntry { path: self.path, modified_date: self.modified_date, - dimensions: String::new(), + width: 0, + height: 0, hash: Vec::new(), similarity: 0, image_type: ImageType::Unknown, @@ -393,7 +395,8 @@ impl SimilarImages { let dimensions = img.dimensions(); - file_entry.dimensions = format!("{}x{}", dimensions.0, dimensions.1); + file_entry.width = dimensions.0; + file_entry.height = dimensions.1; let hasher_config = HasherConfig::new() .hash_size(self.hash_size as u32, self.hash_size as u32) @@ -782,9 +785,10 @@ impl PrintResults for SimilarImages { for file_entry in struct_similar { writeln!( writer, - "{:?} - {} - {} - {}", + "{:?} - {}x{} - {} - {}", file_entry.path, - file_entry.dimensions, + file_entry.width, + file_entry.height, format_size(file_entry.size, BINARY), get_string_from_similarity(&file_entry.similarity, self.hash_size) )?; @@ -799,18 +803,20 @@ impl PrintResults for SimilarImages { writeln!(writer)?; writeln!( writer, - "{:?} - {} - {} - {}", + "{:?} - {}x{} - {} - {}", file_entry.path, - file_entry.dimensions, + file_entry.width, + file_entry.height, format_size(file_entry.size, BINARY), get_string_from_similarity(&file_entry.similarity, self.hash_size) )?; for file_entry in vec_file_entry { writeln!( writer, - "{:?} - {} - {} - {}", + "{:?} - {}x{} - {} - {}", file_entry.path, - file_entry.dimensions, + file_entry.width, + file_entry.height, format_size(file_entry.size, BINARY), get_string_from_similarity(&file_entry.similarity, self.hash_size) )?; @@ -1505,7 +1511,8 @@ mod tests { ImagesEntry { path: PathBuf::from(name.to_string()), size: 0, - dimensions: String::new(), + width: 100, + height: 100, modified_date: 0, hash, similarity: 0, diff --git a/czkawka_gui/src/compute_results.rs b/czkawka_gui/src/compute_results.rs index 23d713c..273eff7 100644 --- a/czkawka_gui/src/compute_results.rs +++ b/czkawka_gui/src/compute_results.rs @@ -755,7 +755,7 @@ fn compute_similar_images( &directory, base_file_entry.size, base_file_entry.modified_date, - &base_file_entry.dimensions, + &format!("{}x{}", base_file_entry.width, base_file_entry.height), 0, hash_size, true, @@ -769,7 +769,7 @@ fn compute_similar_images( &directory, file_entry.size, file_entry.modified_date, - &file_entry.dimensions, + &format!("{}x{}", file_entry.width, file_entry.height), file_entry.similarity, hash_size, false, @@ -799,7 +799,7 @@ fn compute_similar_images( &directory, file_entry.size, file_entry.modified_date, - &file_entry.dimensions, + &format!("{}x{}", file_entry.width, file_entry.height), file_entry.similarity, hash_size, false, diff --git a/krokiet/src/connect_delete.rs b/krokiet/src/connect_delete.rs index 5230a22..e40d79d 100644 --- a/krokiet/src/connect_delete.rs +++ b/krokiet/src/connect_delete.rs @@ -49,8 +49,8 @@ fn remove_selected_items(items: Vec, active_tab: CurrentTab) { let items_to_remove = items .iter() .map(|item| { - let path = item.val.iter().nth(path_idx).unwrap(); - let name = item.val.iter().nth(name_idx).unwrap(); + let path = item.val_str.iter().nth(path_idx).unwrap(); + let name = item.val_str.iter().nth(name_idx).unwrap(); format!("{}{}{}", path, MAIN_SEPARATOR, name) }) .collect::>(); @@ -196,8 +196,8 @@ mod tests { (false, false, false, vec!["5"]), ]); let (to_delete, left) = filter_out_checked_items(&items, false); - let to_delete_data = get_single_data_from_model(&to_delete); - let left_data = get_single_data_from_model(&left); + let to_delete_data = get_single_data_str_from_model(&to_delete); + let left_data = get_single_data_str_from_model(&left); assert_eq!(to_delete_data, vec!["3", "4"]); assert_eq!(left_data, vec!["1", "2", "5"]); @@ -216,8 +216,8 @@ mod tests { (false, false, false, vec!["8"]), ]); let (to_delete, left) = filter_out_checked_items(&items, true); - let to_delete_data = get_single_data_from_model(&to_delete); - let left_data = get_single_data_from_model(&left); + let to_delete_data = get_single_data_str_from_model(&to_delete); + let left_data = get_single_data_str_from_model(&left); assert_eq!(to_delete_data, vec!["3"]); assert_eq!(left_data, vec!["6", "7", "8"]); @@ -236,15 +236,15 @@ mod tests { (false, false, false, vec!["8"]), ]); let (to_delete, left) = filter_out_checked_items(&items, true); - let to_delete_data = get_single_data_from_model(&to_delete); - let left_data = get_single_data_from_model(&left); + let to_delete_data = get_single_data_str_from_model(&to_delete); + let left_data = get_single_data_str_from_model(&left); assert_eq!(to_delete_data, vec!["3"]); assert_eq!(left_data, vec!["1", "2", "4", "5", "6"]); } - fn get_single_data_from_model(model: &[MainListModel]) -> Vec { - let mut d = model.iter().map(|item| item.val.iter().next().unwrap().to_string()).collect::>(); + fn get_single_data_str_from_model(model: &[MainListModel]) -> Vec { + let mut d = model.iter().map(|item| item.val_str.iter().next().unwrap().to_string()).collect::>(); d.sort(); d } @@ -258,7 +258,8 @@ mod tests { checked: item.0, header_row: item.1, selected_row: item.2, - val: ModelRc::new(all_items), + val_str: ModelRc::new(all_items), + val_int: ModelRc::new(VecModel::default()), }); } ModelRc::new(model) diff --git a/krokiet/src/connect_scan.rs b/krokiet/src/connect_scan.rs index 0a32ef3..5865ee6 100644 --- a/krokiet/src/connect_scan.rs +++ b/krokiet/src/connect_scan.rs @@ -108,29 +108,12 @@ fn write_similar_images_results_referenced(app: &MainWindow, vector: Vec<(Images let items_found = vector.len(); let items = Rc::new(VecModel::default()); for (ref_fe, vec_fe) in vector { - let (ref_directory, ref_file) = split_path(ref_fe.get_path()); - let ref_data_model = VecModel::from_slice(&[ - similar_images::get_string_from_similarity(&ref_fe.similarity, hash_size).into(), - format_size(ref_fe.size, BINARY).into(), - ref_fe.dimensions.clone().into(), - ref_file.into(), - ref_directory.into(), - NaiveDateTime::from_timestamp_opt(ref_fe.get_modified_date() as i64, 0).unwrap().to_string().into(), - ]); - insert_data_to_model(&items, ref_data_model, true); + let (data_model_str, data_model_int) = prepare_data_model_similar_images(&ref_fe, hash_size); + insert_data_to_model(&items, data_model_str, data_model_int, true); for fe in vec_fe { - let (directory, file) = split_path(fe.get_path()); - let data_model = VecModel::from_slice(&[ - similar_images::get_string_from_similarity(&fe.similarity, hash_size).into(), - format_size(fe.size, BINARY).into(), - fe.dimensions.clone().into(), - file.into(), - directory.into(), - NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), - ]); - - insert_data_to_model(&items, data_model, false); + let (data_model_str, data_model_int) = prepare_data_model_similar_images(&fe, hash_size); + insert_data_to_model(&items, data_model_str, data_model_int, false); } } app.set_similar_images_model(items.into()); @@ -141,26 +124,31 @@ fn write_similar_images_results(app: &MainWindow, vector: Vec>, let items_found = vector.len(); let items = Rc::new(VecModel::default()); for vec_fe in vector { - insert_data_to_model(&items, ModelRc::new(VecModel::default()), true); + insert_data_to_model(&items, ModelRc::new(VecModel::default()), ModelRc::new(VecModel::default()), true); for fe in vec_fe { - let (directory, file) = split_path(fe.get_path()); - let data_model = VecModel::from_slice(&[ - similar_images::get_string_from_similarity(&fe.similarity, hash_size).into(), - format_size(fe.size, BINARY).into(), - fe.dimensions.clone().into(), - file.into(), - directory.into(), - NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), - ]); - - insert_data_to_model(&items, data_model, false); + let (data_model_str, data_model_int) = prepare_data_model_similar_images(&fe, hash_size); + insert_data_to_model(&items, data_model_str, data_model_int, false); } } app.set_similar_images_model(items.into()); app.invoke_scan_ended(format!("Found {items_found} similar images files").into()); app.global::().set_info_text(messages.into()); } +fn prepare_data_model_similar_images(fe: &ImagesEntry, hash_size: u8) -> (ModelRc, ModelRc) { + let (directory, file) = split_path(fe.get_path()); + let data_model_str = VecModel::from_slice(&[ + similar_images::get_string_from_similarity(&fe.similarity, hash_size).into(), + format_size(fe.size, BINARY).into(), + format!("{}x{}", fe.width, fe.height).into(), + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), + ]); + let data_model_int = VecModel::from_slice(&[fe.width as i32, fe.height as i32]); + (data_model_str, data_model_int) +} +///////////////////////////////// Empty Files fn scan_empty_files(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) @@ -184,20 +172,26 @@ fn write_empty_files_results(app: &MainWindow, vector: Vec, messages: let items_found = vector.len(); let items = Rc::new(VecModel::default()); for fe in vector { - let (directory, file) = split_path(fe.get_path()); - let data_model = VecModel::from_slice(&[ - file.into(), - directory.into(), - NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), - ]); - - insert_data_to_model(&items, data_model, false); + let (data_model_str, data_model_int) = prepare_data_model_empty_files(&fe); + insert_data_to_model(&items, data_model_str, data_model_int, false); } app.set_empty_files_model(items.into()); app.invoke_scan_ended(format!("Found {items_found} empty files").into()); app.global::().set_info_text(messages.into()); } +fn prepare_data_model_empty_files(fe: &FileEntry) -> (ModelRc, ModelRc) { + let (directory, file) = split_path(fe.get_path()); + let data_model_str = VecModel::from_slice(&[ + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), + ]); + let data_model_int = VecModel::from_slice(&[]); + (data_model_str, data_model_int) +} + +////////////////////////////////////////// Empty Folders fn scan_empty_folders(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) @@ -221,26 +215,33 @@ fn write_empty_folders_results(app: &MainWindow, vector: Vec, messa let items_found = vector.len(); let items = Rc::new(VecModel::default()); for fe in vector { - let (directory, file) = split_path(&fe.path); - let data_model = VecModel::from_slice(&[ - file.into(), - directory.into(), - NaiveDateTime::from_timestamp_opt(fe.modified_date as i64, 0).unwrap().to_string().into(), - ]); - - insert_data_to_model(&items, data_model, false); + let (data_model_str, data_model_int) = prepare_data_model_empty_folders(&fe); + insert_data_to_model(&items, data_model_str, data_model_int, false); } app.set_empty_folder_model(items.into()); app.invoke_scan_ended(format!("Found {items_found} empty folders").into()); app.global::().set_info_text(messages.into()); } -fn insert_data_to_model(items: &Rc>, data_model: ModelRc, header_row: bool) { +fn prepare_data_model_empty_folders(fe: &FolderEntry) -> (ModelRc, ModelRc) { + let (directory, file) = split_path(&fe.path); + let data_model_str = VecModel::from_slice(&[ + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.modified_date as i64, 0).unwrap().to_string().into(), + ]); + let data_model_int = VecModel::from_slice(&[]); + (data_model_str, data_model_int) +} + +////////////////////////////////////////// Common +fn insert_data_to_model(items: &Rc>, data_model_str: ModelRc, data_model_int: ModelRc, header_row: bool) { let main = MainListModel { checked: false, header_row, selected_row: false, - val: ModelRc::new(data_model), + val_str: ModelRc::new(data_model_str), + val_int: ModelRc::new(data_model_int), // TODO fill }; items.push(main); } diff --git a/krokiet/src/connect_select.rs b/krokiet/src/connect_select.rs index 7009f13..b2a6e74 100644 --- a/krokiet/src/connect_select.rs +++ b/krokiet/src/connect_select.rs @@ -1,6 +1,6 @@ use crate::common::{get_tool_model, set_tool_model}; +use crate::SelectModel; use crate::{Callabler, GuiState, MainListModel, MainWindow, SelectMode}; -use crate::{CurrentTab, SelectModel}; use slint::{ComponentHandle, Model, ModelRc, VecModel}; // TODO optimize this, not sure if it is possible to not copy entire model to just select item @@ -22,7 +22,7 @@ pub fn connect_select(app: &MainWindow) { } pub fn connect_showing_proper_select_buttons(app: &MainWindow) { - set_select_buttons(&app); + set_select_buttons(app); let a = app.as_weak(); app.global::().on_tab_changed(move || { let app = a.upgrade().unwrap(); diff --git a/krokiet/ui/common.slint b/krokiet/ui/common.slint index 95f888a..e5b276a 100644 --- a/krokiet/ui/common.slint +++ b/krokiet/ui/common.slint @@ -20,7 +20,8 @@ export struct MainListModel { checked: bool, header_row: bool, selected_row: bool, - val: [string] + val_str: [string], + val_int: [int] } export enum BottomPanelVisibility { diff --git a/krokiet/ui/main_lists.slint b/krokiet/ui/main_lists.slint index fd68a2f..6ce0d48 100644 --- a/krokiet/ui/main_lists.slint +++ b/krokiet/ui/main_lists.slint @@ -8,10 +8,10 @@ import {GuiState} from "gui_state.slint"; export component MainList { in-out property <[MainListModel]> empty_folder_model: [ - {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + {checked: false, selected_row: false, header_row: true, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: true, selected_row: false, header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[MainListModel]> empty_files_model; in-out property <[MainListModel]> similar_images_model; diff --git a/krokiet/ui/main_window.slint b/krokiet/ui/main_window.slint index 9b500ee..ad81e75 100644 --- a/krokiet/ui/main_window.slint +++ b/krokiet/ui/main_window.slint @@ -38,16 +38,16 @@ export component MainWindow inherits Window { step_name: "Cache", }; in-out property <[MainListModel]> empty_folder_model: [ - {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + {checked: false, selected_row: false, header_row: true, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: true, selected_row: false, header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[MainListModel]> empty_files_model: [ - {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + {checked: false, selected_row: false, header_row: true, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: true, selected_row: false, header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[MainListModel]> similar_images_model: []; diff --git a/krokiet/ui/selectable_tree_view.slint b/krokiet/ui/selectable_tree_view.slint index 08c1169..8a0c730 100644 --- a/krokiet/ui/selectable_tree_view.slint +++ b/krokiet/ui/selectable_tree_view.slint @@ -9,10 +9,10 @@ export component SelectableTableView inherits Rectangle { callback item_opened(string); in property <[string]> columns; in-out property <[MainListModel]> values: [ - {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + {checked: false, selected_row: false, header_row: true, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: true, selected_row: false, header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[length]> column_sizes: [30px, 80px, 150px, 160px]; private property column_number: column-sizes.length + 1; @@ -89,24 +89,24 @@ export component SelectableTableView inherits Rectangle { } if (root.selected_item != -1) { - Callabler.load_image_preview(r.val[root.parentPathIdx - 1] + "/" + r.val[root.fileNameIdx - 1]); + Callabler.load_image_preview(r.val_str[root.parentPathIdx - 1] + "/" + r.val_str[root.fileNameIdx - 1]); } else { GuiState.preview_visible = false; } } } double-clicked => { - Callabler.item_opened(r.val[root.parentPathIdx - 1] + "/" + r.val[root.fileNameIdx - 1]) + Callabler.item_opened(r.val_str[root.parentPathIdx - 1] + "/" + r.val_str[root.fileNameIdx - 1]) } pointer-event(event) => { // TODO this should be clicked by double-click - https://github.com/slint-ui/slint/issues/4235 if (event.button == PointerEventButton.right && event.kind == PointerEventKind.up) { - Callabler.item_opened(r.val[root.parentPathIdx - 1]) + Callabler.item_opened(r.val_str[root.parentPathIdx - 1]) } else if (event.button == PointerEventButton.left && event.kind == PointerEventKind.up) { clicked_manual(); } //else if (event.button == PointerEventButton.middle && event.kind == PointerEventKind.up) { - // Callabler.item_opened(r.val[root.parentPathIdx - 1] + "/" + r.val[root.fileNameIdx - 1]) + // Callabler.item_opened(r.val_str[root.parentPathIdx - 1] + "/" + r.val_str[root.fileNameIdx - 1]) //} } } @@ -123,7 +123,7 @@ export component SelectableTableView inherits Rectangle { HorizontalLayout { spacing: 5px; - for f [idx] in r.val: Text { + for f [idx] in r.val_str: Text { width: root.column-sizes[idx + 1]; text: f; font-size: 12px;