diff --git a/krokiet/src/connect_scan.rs b/krokiet/src/connect_scan.rs index 5da7888..797416e 100644 --- a/krokiet/src/connect_scan.rs +++ b/krokiet/src/connect_scan.rs @@ -793,11 +793,11 @@ fn prepare_data_model_bad_extensions(fe: &BadFileEntry) -> (ModelRc>, data_model_str: ModelRc, data_model_int: ModelRc, full_header_row: Option) { +fn insert_data_to_model(items: &Rc>, data_model_str: ModelRc, data_model_int: ModelRc, filled_header_row: Option) { let main = MainListModel { checked: false, - header_row: full_header_row.is_some(), - full_header_row: full_header_row.unwrap_or(false), + header_row: filled_header_row.is_some(), + filled_header_row: filled_header_row.unwrap_or(false), selected_row: false, val_str: ModelRc::new(data_model_str), val_int: ModelRc::new(data_model_int), diff --git a/krokiet/src/model_operations.rs b/krokiet/src/model_operations.rs index 7ac83b6..a779e1c 100644 --- a/krokiet/src/model_operations.rs +++ b/krokiet/src/model_operations.rs @@ -54,27 +54,38 @@ pub fn filter_out_checked_items(items: &ModelRc, have_header: boo if have_header && !entries_left.is_empty() { // First row must be header assert!(entries_left[0].header_row); + let is_filled_header = entries_left[0].filled_header_row; - if entries_left.len() == 3 { - // First row is header, so if second or third is also header, then there is no enough items to fill model - if entries_left[1].header_row || entries_left[2].header_row { - entries_left = Vec::new(); + if is_filled_header && entries_left.len() <= 2 { + if entries_left.len() == 2 { + if entries_left[1].header_row { + entries_left.clear(); + } + } else { + entries_left.clear(); + } + } else if !is_filled_header && entries_left.len() <= 3 { + if entries_left.len() == 3 { + if entries_left[1].header_row || entries_left[2].header_row { + entries_left.clear(); + } + } else { + entries_left.clear(); } - } else if entries_left.len() < 3 { - // Not have enough items to fill model - entries_left = Vec::new(); } else { + let header_step = if is_filled_header { 1 } else { 2 }; + let mut last_header = 0; let mut new_items: Vec = Vec::new(); for i in 1..entries_left.len() { if entries_left[i].header_row { - if i - last_header > 2 { + if i - last_header > header_step { new_items.extend(entries_left[last_header..i].iter().cloned()); } last_header = i; } } - if entries_left.len() - last_header > 2 { + if entries_left.len() - last_header > header_step { new_items.extend(entries_left[last_header..].iter().cloned()); } @@ -127,16 +138,24 @@ mod tests { assert!(left.is_empty()); } #[test] - fn test_filter_out_checked_items_one_element_valid_normal() { - let items = create_new_model(vec![(false, false, false, vec![])]); + fn test_filter_one_simple_header() { + let items = create_new_model(vec![(false, false, false, false, vec![])]); let (to_delete, left) = filter_out_checked_items(&items, false); assert!(to_delete.is_empty()); assert_eq!(left.len(), items.iter().count()); } #[test] - fn test_filter_out_checked_items_one_element_valid_header() { - let items = create_new_model(vec![(false, true, false, vec![])]); + fn test_filter_one_filled_header() { + let items = create_new_model(vec![(false, true, true, false, vec![])]); + let (to_delete, left) = filter_out_checked_items(&items, true); + assert!(to_delete.is_empty()); + assert!(left.is_empty()); + } + + #[test] + fn test_filter_one_empty_header() { + let items = create_new_model(vec![(false, true, false, false, vec![])]); let (to_delete, left) = filter_out_checked_items(&items, true); assert!(to_delete.is_empty()); assert!(left.is_empty()); @@ -144,90 +163,195 @@ mod tests { #[test] #[should_panic] - fn test_filter_out_checked_items_one_element_invalid_normal() { - let items = create_new_model(vec![(false, true, false, vec![])]); + fn test_filter_invalid_non_header() { + let items = create_new_model(vec![(false, true, true, false, vec![])]); filter_out_checked_items(&items, false); } #[test] #[should_panic] - fn test_filter_out_checked_items_one_element_invalid_header() { - let items = create_new_model(vec![(false, false, false, vec![])]); + fn test_filter_invalid_header() { + let items = create_new_model(vec![(false, false, true, false, vec![])]); filter_out_checked_items(&items, true); } #[test] - fn test_filter_out_checked_items_multiple_element_valid_normal() { - let items = create_new_model(vec![ - (false, false, false, vec!["1"]), - (false, false, false, vec!["2"]), - (true, false, false, vec!["3"]), - (true, false, false, vec!["4"]), - (false, false, false, vec!["5"]), - ]); - let (to_delete, left) = filter_out_checked_items(&items, false); + fn test_filter_filled_header() { + let items = create_new_model(vec![(false, true, true, false, vec!["1"]), (false, false, false, false, vec!["2"])]); + let (to_delete, left) = filter_out_checked_items(&items, true); 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"]); - } + assert_eq!(to_delete_data, Vec::::new()); + assert_eq!(left_data, vec!["1", "2"]); - #[test] - fn test_filter_out_checked_items_multiple_element_valid_header() { let items = create_new_model(vec![ - (false, true, false, vec!["1"]), - (false, false, false, vec!["2"]), - (true, false, false, vec!["3"]), - (false, true, false, vec!["4"]), - (false, false, false, vec!["5"]), - (false, true, false, vec!["6"]), - (false, false, false, vec!["7"]), - (false, false, false, vec!["8"]), + (false, true, true, false, vec!["1"]), + (false, false, false, false, vec!["2"]), + (false, false, false, false, vec!["3"]), ]); let (to_delete, left) = filter_out_checked_items(&items, true); 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"]); - } + assert_eq!(to_delete_data, Vec::::new()); + assert_eq!(left_data, vec!["1", "2", "3"]); - #[test] - fn test_filter_out_checked_items_multiple2_element_valid_header() { let items = create_new_model(vec![ - (false, true, false, vec!["1"]), - (false, false, false, vec!["2"]), - (true, false, false, vec!["3"]), - (false, false, false, vec!["4"]), - (false, false, false, vec!["5"]), - (false, false, false, vec!["6"]), - (false, true, false, vec!["7"]), - (false, false, false, vec!["8"]), + (false, true, true, false, vec!["1"]), + (false, false, false, false, vec!["2"]), + (false, true, true, false, vec!["3"]), ]); let (to_delete, left) = filter_out_checked_items(&items, true); 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"]); + assert_eq!(to_delete_data, Vec::::new()); + assert_eq!(left_data, vec!["1", "2"]); + + let items = create_new_model(vec![ + (false, true, true, false, vec!["1"]), + (true, false, false, false, vec!["2"]), + (false, false, false, false, vec!["3"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, true); + 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!["2"]); + assert_eq!(left_data, vec!["1", "3"]); + + let items = create_new_model(vec![ + (false, true, true, false, vec!["1"]), + (false, false, false, false, vec!["2"]), + (false, false, false, false, vec!["3"]), + (false, false, false, false, vec!["4"]), + (false, true, true, false, vec!["5"]), + (false, false, false, false, vec!["6"]), + (false, false, false, false, vec!["7"]), + (false, true, true, false, vec!["8"]), + (false, false, false, false, vec!["9"]), + (false, true, true, false, vec!["10"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, true); + 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::::new()); + assert_eq!(left_data, vec!["1", "2", "3", "4", "5", "6", "7", "8", "9"]); + + for i in 1..20 { + let mut vec_items = vec![(false, true, true, false, vec!["First"])]; + for j in i..21 { + let is_header = (j - i) % 5 == 0; + let item = if is_header { + (false, true, true, false, vec!["Header"]) + } else { + (false, false, false, false, vec!["Non header"]) + }; + vec_items.push(item); + } + filter_out_checked_items(&create_new_model(vec_items), true); + } } + #[test] + fn test_filter_empty_header() { + let items = create_new_model(vec![(false, true, false, false, vec!["1"]), (false, false, false, false, vec!["2"])]); + let (to_delete, left) = filter_out_checked_items(&items, true); + 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::::new()); + assert_eq!(left_data, Vec::::new()); + + let items = create_new_model(vec![ + (false, true, false, false, vec!["1"]), + (false, false, false, false, vec!["2"]), + (false, false, false, false, vec!["3"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, true); + 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::::new()); + assert_eq!(left_data, vec!["1", "2", "3"]); + + let items = create_new_model(vec![ + (false, true, false, false, vec!["1"]), + (false, false, false, false, vec!["2"]), + (false, true, false, false, vec!["3"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, true); + 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::::new()); + assert_eq!(left_data, Vec::::new()); + + let items = create_new_model(vec![ + (false, true, false, false, vec!["1"]), + (true, false, false, false, vec!["2"]), + (false, false, false, false, vec!["3"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, true); + 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!["2"]); + assert_eq!(left_data, Vec::::new()); + + let items = create_new_model(vec![ + (false, true, false, false, vec!["1"]), + (false, false, false, false, vec!["2"]), + (false, false, false, false, vec!["3"]), + (false, false, false, false, vec!["4"]), + (false, true, false, false, vec!["5"]), + (false, false, false, false, vec!["6"]), + (false, false, false, false, vec!["7"]), + (false, true, false, false, vec!["8"]), + (false, false, false, false, vec!["9"]), + (false, true, false, false, vec!["10"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, true); + 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::::new()); + assert_eq!(left_data, vec!["1", "2", "3", "4", "5", "6", "7"]); + + for i in 1..20 { + let mut vec_items = vec![(false, true, false, false, vec!["First"])]; + for j in i..21 { + let is_header = (j - i) % 5 == 0; + let item = if is_header { + (false, true, false, false, vec!["Header"]) + } else { + (false, false, false, false, vec!["Non header"]) + }; + vec_items.push(item); + } + filter_out_checked_items(&create_new_model(vec_items), true); + } + } 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 } - fn create_new_model(items: Vec<(bool, bool, bool, Vec<&'static str>)>) -> ModelRc { + fn create_new_model(items: Vec<(bool, bool, bool, bool, Vec<&'static str>)>) -> ModelRc { let model = VecModel::default(); for item in items { - let all_items: Vec = item.3.iter().map(|item| (*item).into()).collect::>(); + let all_items: Vec = item.4.iter().map(|item| (*item).into()).collect::>(); let all_items = VecModel::from(all_items); + if item.2 { + assert!(item.1); // Header must be set when full header is set + } model.push(MainListModel { checked: item.0, header_row: item.1, - full_header_row: false, // TODO - this needs to be calculated - selected_row: item.2, + filled_header_row: item.2, + selected_row: item.3, val_str: ModelRc::new(all_items), val_int: ModelRc::new(VecModel::default()), }); diff --git a/krokiet/ui/common.slint b/krokiet/ui/common.slint index 0145755..b93f9ba 100644 --- a/krokiet/ui/common.slint +++ b/krokiet/ui/common.slint @@ -28,7 +28,7 @@ export struct ProgressToSend { export struct MainListModel { checked: bool, header_row: bool, - full_header_row: bool, + filled_header_row: bool, selected_row: bool, val_str: [string], val_int: [int] diff --git a/krokiet/ui/main_lists.slint b/krokiet/ui/main_lists.slint index cc24d08..257c74e 100644 --- a/krokiet/ui/main_lists.slint +++ b/krokiet/ui/main_lists.slint @@ -10,21 +10,21 @@ import {About} from "about.slint"; export component MainList { in-out property <[MainListModel]> duplicate_files_model: []; in-out property <[MainListModel]> empty_folder_model: [ - {checked: false, selected_row: false, header_row: false, full_header_row: false, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , - {checked: false, selected_row: false, header_row: false, full_header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []}, - {checked: true, selected_row: false, header_row: false, full_header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} + {checked: false, selected_row: false, header_row: false, filled_header_row: false, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, filled_header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []}, + {checked: true, selected_row: false, header_row: false, filled_header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[MainListModel]> big_files_model: []; in-out property <[MainListModel]> empty_files_model: [ - {checked: false, selected_row: false, header_row: false, full_header_row: false, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , - {checked: false, selected_row: false, header_row: false, full_header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []}, - {checked: true, selected_row: false, header_row: false, full_header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} + {checked: false, selected_row: false, header_row: false, filled_header_row: false, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, filled_header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []}, + {checked: true, selected_row: false, header_row: false, filled_header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[MainListModel]> temporary_files_model: []; in-out property <[MainListModel]> similar_images_model: [ - {checked: false, selected_row: false, header_row: true, full_header_row: false, val_str: ["Original", "500KB", "100x100", "kropkarz", "/Xd1", "24.10.2023"], val_int: []}, - {checked: false, selected_row: false, header_row: false, full_header_row: false, val_str: ["Similar", "500KB", "100x100", "witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []}, - {checked: true, selected_row: false, header_row: false, full_header_row: false, val_str: ["Similar", "500KB", "100x100", "lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} + {checked: false, selected_row: false, header_row: true, filled_header_row: false, val_str: ["Original", "500KB", "100x100", "kropkarz", "/Xd1", "24.10.2023"], val_int: []}, + {checked: false, selected_row: false, header_row: false, filled_header_row: false, val_str: ["Similar", "500KB", "100x100", "witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []}, + {checked: true, selected_row: false, header_row: false, filled_header_row: false, val_str: ["Similar", "500KB", "100x100", "lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[MainListModel]> similar_videos_model: []; in-out property <[MainListModel]> similar_music_model: []; diff --git a/krokiet/ui/selectable_tree_view.slint b/krokiet/ui/selectable_tree_view.slint index a526ba4..b82afb2 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, full_header_row: false, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , - {checked: false, selected_row: false, header_row: false, full_header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , - {checked: false, selected_row: false, header_row: false, full_header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , - {checked: true, selected_row: false, header_row: false, full_header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} + {checked: false, selected_row: false, header_row: true, filled_header_row: false, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, filled_header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: false, selected_row: false, header_row: false, filled_header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: true, selected_row: false, header_row: false, filled_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; @@ -96,7 +96,7 @@ export component SelectableTableView inherits Rectangle { } } double-clicked => { - if (r.header_row && !r.full_header_row) { + if (r.header_row && !r.filled_header_row) { return; } Callabler.item_opened(r.val_str[root.parentPathIdx - 1] + "/" + r.val_str[root.fileNameIdx - 1]) @@ -104,7 +104,7 @@ export component SelectableTableView inherits Rectangle { 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) { - if (r.header_row && !r.full_header_row) { + if (r.header_row && !r.filled_header_row) { return; } Callabler.item_opened(r.val_str[root.parentPathIdx - 1])